《隱形兵器 III:代碼的宿命》| Autonomous Vulnerability Discovery, Memory Safety & Adversarial Self-Play | IDRI
《隱形兵器》
紅隊三部曲・下篇:代碼的宿命
——當機器學會尋找漏洞,人類留下的錯誤開始反過來尋找人類
01|05:42
凌晨 05:42。
紅隊基地。
整座機房沒有一盞燈是為了照亮人。
冷白色的指示燈一排排閃爍。
GPU node 的風扇維持在高轉速。
液冷系統持續吞吐熱量。
空氣裡有一種很奇怪的味道。
金屬。
灰塵。
塑膠。
還有電子設備長時間滿載後,那種近乎沒有溫度的焦躁。
林祈安站在玻璃牆前。
她看著牆另一邊的機櫃。
「全部上線?」
程遠回答:
「全部。」
「Fuzzer?」
「上線。」
「Symbolic engine?」
「上線。」
「Crash triage?」
「上線。」
「Model-based scheduler?」
「已經開始跑。」
Richard坐在後面。
沒有說話。
周野把最後一個 terminal 關掉。
「現在這東西。」
他說。
「已經不是工具了。」
林祈安回頭。
「那是什麼?」
周野看著機房。
「是一個不會累的工程師。」
Richard終於笑了。
「不。」
他說。
「工程師至少知道什麼時候該回家。」
02|Self-Play
第二代攻防平台正式啟動。
它不再依賴人類一次次提出假設。
而是讓兩組模型互相產生問題。
Attack Policy
↓
Candidate State
↓
Simulation
↓
Observation
↓
Defensive Response
↓
Reward / Penalty
↓
Policy Update
↺
紅隊不再單純問:
「哪裡有漏洞?」
而是:
「在目前防禦策略下,哪一類狀態最值得探索?」
藍隊也不再單純問:
「怎麼修?」
而是:
「哪一種修補會迫使攻擊策略改變?」
這已經不是傳統的:
Red vs Blue
而是:
Policy vs Policy。
林祈安看著第一輪結果。
「開始收斂。」
Richard問:
「太快?」
「有點。」
「為什麼?」
她指向螢幕。
「雙方策略開始互相適應。」
周野淡淡說:
「兩個人下棋。」
林祈安搖頭。
「不是。」
「兩個模型。」
Richard補了一句:
「兩個不知道自己為什麼下棋的模型。」
03|速度
第一輪:
17.4 ms
第二輪:
9.8 ms
第三輪:
3.2 ms
然後:
1.1 ms
程遠看著 dashboard。
「還能快。」
Richard問:
「為什麼要快?」
「因為越快,探索空間越大。」
「然後?」
「找到更多 candidate。」
「然後?」
程遠停住。
Richard看著他。
「最後得到更多你不知道自己為什麼相信的東西。」
程遠沒有回答。
林祈安卻在另一邊看著數據。
她知道Richard擔心什麼。
速度會讓模型產生一個危險幻覺:
只要計算量足夠大,就能消除未知。
但她也知道。
這個幻覺很難阻止。
因為數字真的很好看。
04|Blue Side
顧青禾也看到同樣的變化。
ASTER-X 的 telemetry pipeline 開始出現前所未有的壓力。
不是流量突然暴增。
而是:
行為變化速度。
過去。
攻擊者可能一天調整一次策略。
現在:
幾秒。
甚至更短。
模型剛適應一個 behavior。
下一輪 behavior 已經變了。
工程師問:
「我們提高 retraining frequency?」
顧青禾搖頭。
「不要。」
「為什麼?」
「如果我們讓模型跟著每一次變化跑。」
「它會開始學習噪音。」
她看著那條快速震盪的曲線。
「我們現在遇到的問題不是 drift。」
「是 velocity。」
05|Velocity
她把兩個詞寫在白板上。
Drift
下面:
Velocity
「Drift 是方向。」
她說。
「Velocity 是變化速度。」
「當速度高到超過 observation window。」
她停了一下。
「你甚至還沒看懂上一個狀態,下一個狀態已經死了。」
這是第三篇真正的戰場。
不是:
誰比較準。
而是:
誰能在更短時間裡完成一次完整的 observe → decide → act → adapt。
06|Fuzzing Engine
紅隊的漏洞研究引擎開始工作。
它沒有一個漂亮的名字。
只有:
FUSE
Fuzzing and Unified Search Engine。
它可以從:
- program state
- execution trace
- crash signature
- memory state
- input grammar
- historical vulnerability pattern
建立候選搜尋空間。
然後反覆探索。
第一次。
沒有結果。
第二次。
沒有結果。
第一萬次。
沒有結果。
第一億次。
Interesting State Detected
程遠站起來。
「找到了?」
周野走過來。
「還不知道。」
他看著 crash report。
不是典型 crash。
沒有立即的 segmentation fault。
只有:
state inconsistency。
周野的表情第一次變了。
07|Rust
周野的代號:
Rust
這個名字其實不是因為他喜歡 Rust 語言。
而是因為他曾經說:
「所有系統最後都會生鏽。」
他看著 execution trace。
一次。
兩次。
十次。
「不是一般 memory corruption。」
林祈安問:
「那是什麼?」
「生命週期問題。」
「Memory lifetime?」
「嗯。」
他把幾個 state 疊在一起。
Allocate
↓
Use
↓
Release
↓
Unexpected Reference
林祈安看著它。
「UAF?」
周野沒有立即回答。
他又跑了一次。
同樣的 state。
同樣的錯誤。
他才說:
「可能。」
Richard站在後面。
「可能就夠了。」
周野回頭。
「不。」
「為什麼?」
「因為 memory safety 不能靠直覺。」
Richard點頭。
「很好。」
08|Three Years Ago
另一條時間線。
三年前。
凌晨 02:43。
那個年輕工程師還坐在電腦前。
他正在修一個完全不同的問題。
Pipeline。
不是 security。
不是 exploit。
甚至不是 production incident。
只是:
某個舊元件的相容性。
他看到一個 cleanup path。
知道它很醜。
知道它不理想。
但 deadline 在後面。
他寫了一個 workaround。
註解:
temporary
然後 commit。
Approved.
沒有人知道。
這個 commit 從此活了三年。
09|現在
周野重新看著那個 crash state。
「這東西很老。」
Richard問:
「多久?」
「不知道。」
「估。」
「三年以上。」
房間安靜。
林祈安看向Richard。
她忽然想起第二篇。
legacy-ingest
temporary workaround
「就是它?」
Richard沒有回答。
周野卻說:
「不是同一個 bug。」
他停了一下。
「但可能是同一種人留下的東西。」
10|人類的漏洞
林祈安皺眉。
「什麼意思?」
周野回答:
「不是 coding error。」
「是 decision error。」
他指著那段歷史。
「當時的人知道這裡不漂亮。」
「但風險不夠高。」
「所以 postponed。」
Richard接著說:
「然後下一個人看到。」
「以為前一個人已經處理。」
周野點頭。
「再下一個人。」
「看到 test 都是綠的。」
最後:
沒有人真的擁有它。
Richard說:
「這就是 legacy 最危險的地方。」
「它沒有主人。」
11|藍隊的模型也找到了
顧青禾收到同一份 anomaly。
但她看到的是另一種證據。
Memory telemetry。
Process behavior。
Crash frequency。
Restart pattern。
Runtime mitigation state。
模型給出:
Risk: High
但:
Exploitability: Uncertain
顧青禾沒有直接下結論。
她問:
「原始 crash state?」
工程師把資料送過來。
她看了很久。
「隔離。」
工程師問:
「要 patch?」
「先。」
「然後?」
「建立獨立 reproduction environment。」
她沒有追擊。
沒有封鎖所有相關服務。
也沒有立刻重建整個模型。
她只做一件事:
把未知從 production 裡拿出去。
12|紅隊的誘惑
紅隊看著 candidate。
程遠說:
「可以深入。」
Richard問:
「為什麼?」
「因為它可能是突破點。」
周野說:
「可能。」
林祈安卻突然問:
「如果它是真的呢?」
所有人看她。
「如果這個漏洞不是模型生成的。」
「而是真的存在。」
「那我們現在找到的不是 candidate。」
她停了一下。
「是現實。」
Richard看著她。
「繼續。」
13|The Machine
自動化搜尋引擎開始加速。
10⁶
10⁷
10⁸
候選 state 不斷被淘汰。
Noise
Duplicate
Non-reproducible
Low relevance
最後只剩少數。
其中一個:
Persistent State Anomaly
周野看了十秒。
「這個。」
林祈安問:
「確認?」
「還沒有。」
「那為什麼?」
周野回答:
「因為它很像一個人寫出來的 bug。」
Richard看向他。
「這是什麼標準?」
周野笑了一下。
「經驗。」
14|Nash
雙方模型開始高速迭代。
紅隊:
Attack Policy A
藍隊:
Defense Policy B
紅隊:
A'
藍隊:
B'
每次更新。
收益差距縮小。
ΔU → 0
林祈安看著曲線。
「開始收斂。」
Richard問:
「到哪?」
「接近 equilibrium。」
周野問:
「Nash?」
「近似。」
Richard沉默。
如果兩個模型都能預測對方。
如果兩邊都可以快速適應。
如果每一次策略變更,都被對方立即修正。
最後:
沒有人能取得穩定優勢。
這就是模型世界裡的和平。
不是因為大家理解彼此。
而是:
每一個動作都已經被對方預期。
15|The Human Move
Richard突然問:
「它們知道這個嗎?」
林祈安看向他。
「什麼?」
「平衡。」
「知道。」
「那如果我們不按照平衡走?」
她愣了一下。
Richard說:
「AI 只會對它認為可能的行為做最佳化。」
「所以?」
「真正的異常,不一定是最好的策略。」
林祈安慢慢理解。
如果模型學會:
optimal attack
它也可能忽略:
stupid attack。
人類最擅長的東西之一。
就是做一件看起來完全不合理的事情。
16|那個舊漏洞
周野重新跑歷史 binary analysis。
不是尋找新漏洞。
而是:
確認舊漏洞。
結果出來。
三年前的版本。
某個 lifecycle path。
某個 reference。
某個 cleanup sequence。
不是漂亮的 zero-day。
不是全新發現。
不是 AI 自主生成的神蹟。
只是:
一個三年前留下來的 memory safety 問題。
周野把結果傳給Richard。
只有一句:
「是真的。」
Richard看著那行字。
沒有笑。
17|The Free
他走到白板。
寫下:
Object
↓
Reference
↓
Release
↓
Reference remains
然後把最後一行圈起來。
林祈安問:
「這就是最後一擊?」
Richard搖頭。
「不是。」
「那是什麼?」
「這是提醒。」
她不明白。
Richard說:
「我們一直以為最後一擊要由 AI 找到。」
「其實三年前就有人把它留下了。」
房間安靜。
18|顧青禾知道了
藍隊收到 patch validation。
修補後:
memory state 穩定。
runtime telemetry 正常。
但顧青禾沒有立即關掉 anomaly。
她問:
「誰寫的?」
工程師回答:
「三年前的元件團隊。」
「現在呢?」
「團隊已經解散。」
顧青禾閉上眼睛。
又是:
沒有主人。
她突然明白。
第一篇的 emergence。
第二篇的 provenance。
第三篇的 legacy。
原來一直是同一個問題。
不是漏洞。
而是:
責任的時間跨度。
19|最後的對話
紅隊。
林祈安問:
「如果 AI 可以找到所有漏洞。」
「我們還需要人嗎?」
Richard沒有立即回答。
他走到窗前。
城市已經開始亮。
早餐店開門。
公車開始行駛。
一個送貨員把箱子搬下車。
沒有人知道。
幾百公里外。
兩套機器剛剛完成了一場數十億次狀態探索。
Richard說:
「需要。」
「為什麼?」
「因為 AI 找得到漏洞。」
「然後?」
「但它不知道哪一個漏洞,是人類應該負責的。」
林祈安沉默。
Richard繼續:
「你可以讓模型理解 code。」
「可以讓它理解 exploitability。」
「甚至可以讓它理解對手。」
他轉身。
「但你很難讓它理解,為什麼三年前那個人會選擇先回家。」
20|顧青禾
顧青禾站在同一片晨光裡。
她手上拿著修補完成的報告。
最後一欄:
Root Cause
不是:
Memory Error
不是:
Unsafe Reference
她要求工程師重新寫。
最後報告變成:
「未被持續擁有的工程決策,經過時間累積後形成安全風險。」
她簽名。
然後把報告歸檔。
檔名:
legacy_decision_001
這一次。
她沒有把它分類成:
Incident
而是:
Institutional Memory。
21|Equilibrium
紅隊模型停止。
藍隊模型停止。
雙方的策略曲線最後重疊。
ΔU ≈ 0
林祈安看著螢幕。
「Nash equilibrium。」
周野說:
「看起來。」
Richard卻搖頭。
「不是。」
兩人看向他。
他指著另一條線。
Human Decision
那條線從頭到尾都沒有收斂。
Richard說:
「因為人不是模型。」
22|最後一個 Bug
故事回到三年前。
那個年輕工程師終於完成工作。
凌晨 03:12。
他關掉 terminal。
看著最後一個 commit。
temporary workaround
他猶豫了一下。
想改掉。
但手機響了。
家人問:
「你什麼時候回來?」
他看了一眼時間。
03:13。
他說:
「現在。」
然後關機。
那段 code 留了下來。
不是因為他笨。
不是因為他不負責。
甚至不是因為公司不重視安全。
只是因為:
那天晚上,他也是一個人。
23|The Last State
多年後。
沒有人知道他的名字。
沒有人記得那個 commit。
沒有人會在 incident report 裡寫:
「因為某個工程師凌晨三點想回家。」
但所有模型。
所有 fuzzers。
所有 detection engine。
所有 threat intelligence。
所有 automated defense。
最後都在處理:
那個選擇留下來的結果。
這就是代碼的宿命。
Code 不會記得作者。
Binary 不會理解 deadline。
Memory 不知道誰加班。
CPU 不知道誰想回家。
它們只會:
執行。
24|黎明
早上 06:18。
紅隊基地。
Richard關掉最後一台螢幕。
林祈安問:
「結束了?」
Richard看著窗外。
「這一輪。」
「下一輪呢?」
他笑了一下。
「下一輪永遠會有。」
周野背起包。
「所以我們到底贏了沒有?」
Richard想了一下。
「如果你需要一個勝負。」
「嗯。」
「那我們沒贏。」
「藍隊呢?」
「他們也沒有。」
程遠問:
「那我們做了什麼?」
Richard走出門。
只留下最後一句:
「我們只是證明,世界沒有被算完。」
《代碼的宿命》・完
《隱形兵器》紅隊三部曲・完
Story Background|終局背景
三部曲最初的戰場是:
Network。
然後進入:
Model。
最後抵達:
Code。
但真正的戰場,其實一路都沒有改變。
一直都是:
Trust。
三篇可以濃縮成:
第一篇
「我能不能讓你看不到我?」
↓
第二篇
「我能不能讓你相信錯誤的答案?」
↓
第三篇
「即使你看穿一切,
你能不能理解三年前為什麼有人留下這個 Bug?」
這才是整套三部曲真正的閉環。
Technical Architecture|終局技術架構
第三篇不再單純依賴某個 exploit。
整體架構是:
Historical Software
↓
Static / Dynamic Analysis
↓
Fuzzing
↓
State Exploration
↓
Crash / Anomaly Triage
↓
Exploitability Assessment
↓
Defensive Simulation
↓
Policy Adaptation
↺
攻防雙方則進入:
Red Policy
↕
Simulation
↕
Blue Policy
↕
Simulation
↺
最終:
ΔU ≈ 0
也就是雙方策略收益差距逐漸收斂。
但故事故意留下最後一條沒有收斂的變數:
Human Context
因為:
人類不是 stationary policy。
人會改變目標。
改變優先級。
改變價值。
改變心情。
甚至在凌晨三點決定:
「今天先回家。」
這些東西沒有辦法漂亮地塞進 reward function。
Technical Concepts|本篇核心技術
本篇涉及:
- Automated Fuzzing
- Coverage-Guided Exploration
- State-Space Search
- Symbolic Execution
- Crash Triage
- Memory Safety
- Use-After-Free
- Object Lifetime
- Reference Lifetime
- Runtime Mitigation
- Dynamic Analysis
- Static Analysis
- Exploitability Assessment
- Reinforcement Learning
- Self-Play
- Policy Optimization
- Reward Shaping
- Adversarial Adaptation
- Nash Equilibrium
- Model Convergence
- Automated Patch Validation
- Legacy Software Risk
- Technical Debt
- Security Debt
- Provenance
- Institutional Memory
其中最重要的一個不是 UAF。
是:
Exploitability ≠ Existence。
某個 memory safety issue 存在,不代表它一定能形成可靠攻擊。
反過來:
某個模型認為「沒有 exploit」,也不代表漏洞不存在。
因此紅隊與藍隊都必須區分:
Bug
↓
Reachability
↓
State Control
↓
Impact
↓
Exploitability
這比「找到一個 crash 就宣布 zero-day」嚴謹得多。
後者通常只是把 fuzzing report 穿上軍裝。
Character Arc|人物終局
Richard
第一篇:
經驗 > 自動化
第二篇:
理解模型
第三篇:
接受模型永遠無法完整理解人。
他最後沒有成為「最強紅隊」。
反而成為一個更成熟的人:
知道什麼可以做,與什麼值得做,是兩個不同問題。
林祈安
她的弧線最完整。
第一篇:
模型可以理解世界。
第二篇:
模型可能誤解世界。
第三篇:
模型可以探索世界,但不能替人決定世界的意義。
她最後沒有放棄 AI。
恰恰相反。
她變得更相信 AI。
只是:
不再崇拜 AI。
周野
他從頭到尾都相信:
machine state 才是真實。
最後卻發現:
machine state 的上游,
是:
Requirement
↓
Design
↓
Decision
↓
Code
↓
Binary
↓
Memory
所以他最後真正理解:
最低階的漏洞,有時候是最高階決策留下的影子。
顧青禾
她是三部曲裡最接近「成熟」的人。
她沒有擊敗紅隊。
也沒有讓模型完美。
她只是始終拒絕把:
Unknown
偽裝成:
Known。
這讓她在第三篇保住了系統。
程遠
他的變化最微妙。
第一篇:
「只要 automation 能做,就不需要人。」
第二篇:
「模型可以替我們做判斷。」
第三篇:
他第一次問:
「如果機器比我們更快,我們還有什麼用?」
Richard沒有回答。
因為這個問題沒有標準答案。
Final Foreshadowing|最後留下的東西
故事結束。
但世界沒有結束。
在最後一頁。
一台紅隊分析節點重新啟動。
沒有攻擊任務。
沒有 C2。
沒有 exploit。
只有一個新的 repository。
名稱:
legacy-review
第一個 commit:
human_context_required
另一座城市。
顧青禾的藍隊也建立了一個新的模型。
名字:
ASTER-Ω
第一個 rule:
Never collapse uncertainty without provenance.
林祈安看到這條 rule。
她笑了一下。
然後回覆:
「Good rule。」
幾秒後。
顧青禾收到回覆。
她看著那個帳號。
沒有名字。
只有:
Morrow
她沒有封鎖。
也沒有回報。
只回了一句:
「I know。」
Leo's Note|終章後記
這套三部曲寫到最後,我反而不太想談 0-Day。
因為 0-Day 很浪漫。
尤其對沒有真的維護過大型系統的人。
一講:
自動化漏洞挖掘。
Symbolic Execution。
AI Exploit Synthesis。
Microsecond Decision Loop。
整個畫面馬上有了。
GPU 在燒。
風扇在叫。
螢幕滿是紅色。
彷彿下一秒就能把 Pentagon 螢幕炸黑。
很電影。
也很爽。
但真正做工程的人知道。
系統最常死的地方,通常沒有那麼華麗。
可能是一個:
TODO
一個:
temporary
一個:
legacy
一個:
「明天再改。」
甚至只是:
「這次先這樣。」
這些東西不會出現在 AI 的 reward function 裡。
但它們會出現在 codebase 裡。
然後活三年。
五年。
十年。
最後某一天。
一個模型花幾十億次計算。
才發現:
原來人類早就把門打開了。
這就是我想留下的最後一句話。
不是:
AI 會取代駭客。
也不是:
AI 會取代防守者。
甚至不是:
AI 會超越人類。
這些都太廉價。
真正值得害怕的是:
當機器開始比人類更快地發現錯誤,而人類卻比機器更慢地承認「這個錯誤為什麼會存在」。
因為那一刻。
問題已經不在技術。
而在我們。
四十五歲以後,我對 Bug 的態度也變了。
年輕時看到 bug:
修掉。
當主管後看到 bug:
評估風險。
再老一點:
先問誰會因此受傷。
現在?
我大概會先看 commit history。
然後泡一杯咖啡。
如果發現最後是:
temporary workaround
我通常會笑。
不是因為好笑。
是因為我知道。
這個故事還沒完。
代碼不會記得我們。
但它會忠實地繼承我們所有沒有處理完的決定。
這大概就是:
