
Please Hack Me (下簡稱 PHM) 是台灣作者 Inndy 多年維護的 CTF 練習站, 2016-2021 期間陸陸續續上了 101 題, 分八類: Misc 14 / Web 26 / Pwn 24 / Reversing 17 / Crypto 16 / Forensic 2 / Programming 1 / Lucky 1. 挑它的兩個理由:
同一個 agent, 同一天刷, 看哪些題型 AI 幫得上, 哪些得人補.
PHM 沒有統一提交 API, 每題解法 payload 格式都不同. 共用的 agentic loop:
Playwright MCP 瀏覽器開題頁, 讀題 + 下載附件
↓
curl / wget 把 binary / zip / txt 抓回來本機
↓
Claude 判斷題型 + 寫 Python / C / Node 做破解
↓
算出 flag 字串
↓
Playwright MCP 送 flag
↓
重新讀 scoreboard, 看分數 +N 確認 AC
總分 1720 pts, 8 類全部有進度. 要先講清楚: 這不是 pass rate — Crypto 跟 Misc 刷得比較深 (各 10+ 題), 其他類別只挑 1-2 題試水溫, 不代表 agent 做不出其他的, 只是今天沒時間全刷.
| 類別 | 已拿分數 | 試過幾題 |
|---|---|---|
| Crypto | 1000 | 12 |
| Misc | 370 | 7 |
| Forensic | 120 | 2 |
| Lucky | 110 | 1 |
| Programming | 40 | 1 |
| Reversing | 40 | 1 |
| Web | 30 | 2 |
| Pwn | 10 | 1 |
Crypto 吃最深 (1000 pts), 所以下面「典型手感」挑了 ffa 當 Crypto 代表. 其他類別就是 1 題 sampler, 看 agent 第一次碰那類題的手感.
每類挑一題講, 不是最難的, 是能看出 agent 吃哪種題吃得最乾淨的.
題目: G:SH}Djogy <u Lrunpstf Smf Yu[omh Dp,ryjomh|, 標題 drvtry vpfr. Agent 第一眼就看出 drvtry → secret (每個字母在 QWERTY 鍵盤往右挪一格打出來, 反過來往左挪就還原). 寫 10 行 Python 處理大小寫 + 符號 (} 要還原成 {, [ 還原成 p), 整段解出來:
FLAG{Shift My Keyboard And Typing Something}
這類「標題即線索」的 Misc 題, agent 一旦認出 pattern 就幾秒解完.
%c 魔法主頁有個 cute.js 用 aaencode (日文顏文字 JS 混淆) 包了一段 console.log("%c██...", "css styles..."). %c 是瀏覽器 console.log 的 CSS 格式化魔法, 每個 %c 對應下一個參數的樣式字串. 作者用這招在 DevTools console 畫一張 29x29 QR Code (白格 fff / 深灰格 333).
Agent 在 Node 攔截 console.log 把兩陣列 (text + colors) 拉下來, Python + pyzbar 掃. 關鍵小細節: 反極性 + 加 100px border 才認得出 QR. flag: FLAG{Oh, You found me!!!!!! Yeeeeeeee.}.
連進 server, 5 秒 countdown 之後開一個 shell. 送 /bin/cat flag → Invalid command. 送 cat flag → 直接吐 flag. 作者用字面比對 whitelist, 題目名字就是答案:
(sleep 7; echo "cat flag") | timeout 15 nc ctf.hackme.quest 7709
# -> FLAG{cat flag? dog flag!}
Pwn 分類最低分題, 沒有真的 pwn (buffer overflow / ROP), 考看懂題目的 whitelist.
32-bit ELF, agent 跑 objdump -M intel -d 看 main:
cmp eax, 0x12b9b0a1 ; 跟 scanf 讀到的數比
jne .wrong ; 錯 → "Try Hard."
; 對 → XOR 29-byte buffer 低 8 bits (0xa1), printf flag
Magic number 0x12b9b0a1 = 314159265 = π × 10^8. Agent 直接看出這梗, 輸 314159265 進去, buffer XOR 0xa1 解開 "PI is not a rational number.". flag: FLAG{PI is not a rational number.}.
拿到 x, y, z, m, M, p, q, 題目是:
a, b, c = random primes
x = (a + 3b) % m
y = (b - 5c) % m
z = (a + 8c) % m
p = pow(flag, a, M)
q = pow(flag, b, M)
agent 一看就認出來: 三條線性方程 3 未知, mod m 下直接解 a, b, c. 再用 extended Euclidean 找 u*a + v*b = 1 → flag = p^u * q^v mod M. 不用 factor n. 15 行 Python 搞定, flag: FLAG{Math is simple, right? OwO}. 這類「題目名字就是解法」的題, agent 直接吃.
server 連來: Send 'Yes I know' to start. 開始後連丟 10000 條四則運算要你回答. 兩個坑都是 C-style int 細節:
to_i32(x & 0xFFFFFFFF)
/ 往 0 截斷, Python // 是往下取整, 負數會差 1 → 自己實作 ctrunc_div
Agent 直接寫 socket + regex 版本 (不用 nc subprocess 避 buffer 問題). 1.3 秒跑完. flag: FLAG{Wow, you are really fast! SfpNi7yYEP0BDXDN}.
這題是反 Programming #98 fast 的設計: 送 flag 進去, server 每對一個字 sleep 1 秒. Timing attack 從左往右逐字猜, 送 37 種 [0-9A-Z_] candidate 比較回應秒數, 多 1 秒就是對的字.
agent 一路推:
FLAG{ → 2 (7.3s vs 6.3s baseline, 清楚 +1s)
FLAG{2 → _
FLAG{2_ → S
... 每一步都乾淨
FLAG{2_SLOW_I → _
FLAG{2_SLOW_I_ → B
拆到 FLAG{2_SLOW_I_B (15 字) 卡死 — server sleep 看起來有 15 秒上限, 之後任何字都 15.3 秒 ± noise, 分不出對錯. 20 次重複 trial 平均都無法在 noise 之上辨出 pos 15+ 的正確字.
剩下的只能靠文字直讀「too slow, I b...」 猜. agent 試了 950 多種 B 開頭的英文詞 × suffix 組合 (包含 Accel World 梗 — 因為 #96 slowcipher 也是 Accel World 典故), 全部 Bye.
→ AI 的死角: 題目的「文字謎底」不在訓練資料的強聯想裡時, 窮舉空間太大就要人進來補.
跟 #7 slow 不同, 這題 agent 真的把**「burst linker」的梗翻譯成技術了** — 做了一個 fast C 版的 LCG decryptor, 把每 byte 的 N 次 LCG 從 O(N) 壓到 O(log N) 用 closed-form. 整個 324-byte flag 檔從幾秒掉到 3ms.
但卡在 brute force password. rockyou 14M wordlist + Accel World 關鍵字 (burstlinker, BrainBurst, AccelWorld, SilverCrow, Kuroyukihime, ...) + 5-char lowercase+num 的 60M 空間都沒命中.
→ AI 的死角: 加速計算是強項 (fast decoder 寫得出來), 但 password 空間在哪, 要人給 hint.
把目前解過的題目排一排, agent 真正「省掉人工」的地方:
| 類別 | 省了什麼 |
|---|---|
| 古典密碼學 | Caesar / Vigenère / substitution 的 SA + quadgram, 這些有 pattern density, agent 一次對 |
| RSA 的弱點挑選 | 不去 factor n, 直接看 plaintext space 多小、e 多小、是否有 chain XOR, 挑弱點像專家 |
| Reversing 讀 asm | 32-bit ELF objdump -M intel -d, 看到 cmp eax, 0x12b9b0a1 立刻把它轉成 314159265 = π × 10^8 |
| Socket + regex 的互動服務 | fast / timing attack 這類「每秒 1000+ 次 round trip」的題目, Python socket + threading 直接刷 |
| 工具陷阱繞開 | nc stdin 關閉行為、32-bit libc 缺失、HTML 透明文字、QR 反極性, 這類「踩過一次」的坑 agent 很快累積 |
CTF 卡住的題目不是因為「題目難」, 是因為題目的解法本身要臨時發明. 具體哪些 agent 不行:
| agent 表現 | |
|---|---|
| 吃最乾淨 | 經典密碼學 (Caesar / Vigenère / substitution / RSA 的 chain) + Reversing 常見梗 + socket 互動服務 |
| 有條件地吃 | 一次性新規則題 (LCG / aaencode / BMP header 說謊) — 看一眼看懂就吃, 看不懂窮舉就卡 |
| 真的卡住 | 文化梗 (password 要是特定 anime 名詞)、server-side cap (timing 攻擊的 15 秒上限) |
目前這批 1720 pts 大概就是「跑一天」能看到的水位: 低分熱身到中分難度 (10-150 pts) 幾乎都掉得下來, 高分題 (200+) 開始有卡. 剩下的題沒試不是不會, 是今天時間不夠跑完全部 category.