#《飽和攻防 I:幽靈信號》| Generative C2, Steganographic Channels & Behavioral Graph Detection | IDRI

上篇:幽靈信號

凌晨 02:17。

南方聯合戰情室,地下七層。

沒有窗。

沒有時鐘。

只有冷氣、風扇,以及數百個機櫃長年不間斷地把熱量送進另一個房間。

Aster坐在第三排。

她已經四十五歲。

這個年紀,在資安圈算不上老,但也早就過了會因為一個漂亮的 exploit chain 就拍桌叫好的階段。

年輕的時候,他喜歡看封包。

後來喜歡看 session。

再後來,他開始看人。

現在,他看的是系統如何欺騙自己。

螢幕右下角跳出一行訊息。

TELEMETRY PIPELINE: NOMINAL
FLOW INGEST: 98.7%
TLS SESSION OBSERVATION: 96.3%
DNS SENSOR HEALTH: 99.1%

沒有紅色。

沒有警報。

沒有 SOC 最喜歡的那種漂亮得像電影海報一樣的「THREAT DETECTED」。

Aster端起咖啡。

冷掉了。

他喝了一口。

「又是正常。」

坐在旁邊的年輕分析師李穎抬起頭。

「什麼?」

「太正常。」

李穎看了一眼 dashboard。

「今天 false positive 很低。」

「我不是在稱讚它。」

「……」

Aster把杯子放下。

「你知道一個偵測系統最危險的時候是什麼嗎?」

李穎想了幾秒。

「模型失效?」

「不是。」

Aster看著整面牆的流量圖。

「模型非常成功的時候。」


01

三千公里之外。

另一個沒有窗的房間。

螢幕上沒有紅色。

同樣沒有綠色。

只有一條灰色的曲線。

曲線平滑得近乎無聊。

男人站在螢幕前。

沒有人知道他的真名。

在這個專案裡,他只有一個代號:

Morrow。

「第六個 endpoint。」

身後的人問:

「確認?」

「確認。」

「C2?」

Morrow 沒有回答。

他伸手,把畫面放大。

不是 payload。

不是 command。

不是某個漂亮的 exploit。

只是流量。

HTTPS。

正常的 TLS session。

正常的 destination。

正常的 certificate chain。

正常的 packet size。

甚至正常到有點令人感到舒服。

「你確定藍隊看不到?」

Morrow 笑了一下。

「他們當然看得到。」

「那……」

「問題從來不是看不看得到。」

他指著螢幕。

「是他們看到之後,會不會認為那是值得知道的事情。」


02

這一代的防禦系統已經很少依賴傳統 signature。

那個時代早就結束了。

signature 對付已知惡意樣本很好用。

但對一個會改變自己的系統來說,signature 就像在機場門口拿著昨天的照片抓人。

人可以換衣服。

可以換髮型。

甚至可以換身份。

真正成熟的 detection stack,看的不再只是:

「這個封包是不是惡意?」

而是:

「這個行為與它所處的環境相比,有多不合理?」

因此藍隊收集的並不是「封包」。

而是一個巨大、骯髒、昂貴、永遠不完整的觀測空間。

Flow。

DNS。

TLS metadata。

Endpoint telemetry。

Process lineage。

Authentication events。

Destination reputation。

Temporal relationship。

Asset criticality。

User behavior。

Network topology。

每一個觀測值都不可靠。

但它們疊在一起,開始形成某種輪廓。

Aster把滑鼠移到一張圖。

「把 payload view 關掉。」

李穎愣了一下。

「全部?」

「全部。」

「那我們看什麼?」

Aster看著他。

「你還在想封包。」

「……」

「我們現在看關係。」


03

大螢幕上的世界突然變了。

原本是一排排 flow record。

現在變成圖。

Node。

Edge。

Weight。

Timestamp。

一個巨大的 temporal graph。

每台 endpoint 是 node。

每一條有效互動關係,是 edge。

邊不是單純存在或不存在。

它有權重。

有頻率。

有時間。

有方向。

甚至有 context。

某台 workstation 在上午九點與公司 ERP 建立連線。

這很正常。

下午兩點與雲端文件服務建立連線。

也正常。

晚上十一點,與一個平常從未出現過的 endpoint 建立長時間、低頻率、週期性極高的 encrypted session。

單獨看。

還是正常。

Aster說:

「現在把 host 行為做成 sequence。」

李穎操作。

時間軸出現。

X_t = [x_(t-k), …, x_(t-2), x_(t-1), x_t]

每個 x_t 都不是一個封包。

而是一個時間窗口中的 behavioral representation。

包括:

  • flow duration
  • inter-arrival statistics
  • packet-size distribution
  • connection frequency
  • destination novelty
  • DNS query characteristics
  • TLS metadata
  • endpoint context

以及最重要的:

與其他節點的關係。

Aster說:

「這就是我們以前漏掉的東西。」

李穎問:

「哪個?」

「我們一直在問一個錯誤問題。」

他停了一下。

「我們問的是:『這個封包像不像惡意流量?』」

「但真正該問的是——」

他指向圖中央。

「這個東西為什麼會出現在這裡?」


04

Morrow 的系統沒有「隱藏」。

至少從工程定義上,它沒有。

隱藏是一種很古老的思維。

藏在正常流量裡。

藏在圖片裡。

藏在 DNS 裡。

藏在某種 protocol 的欄位裡。

這些方法都建立在一個前提:

觀察者沒有足夠的資訊。

但國家級防禦系統最大的優勢,恰恰就是資訊量。

所以 Morrow 的設計目標不是讓訊號消失。

而是讓訊號進入一個合理的統計分布。

概念上,它可以被描述成:

z ∼ q_φ(z|x)

再由生成模型得到:

x̂ = G_θ(z)

但 Morrow 不關心某一個生成樣本看起來是否「像正常流量」。

那太簡單。

他關心的是:

P(X_(t:t+n))

以及更高階的:

P(X_(t:t+n),G_t)

換句話說:

不只是像不像一個正常封包。

而是:

像不像一個正常世界裡,合理存在的行為。

這是兩回事。


05

李穎看著模型輸出。

「這裡有一個 anomaly score。」

Aster走過來。

「多少?」

「0.71。」

「閾值?」

「0.83。」

「所以?」

「沒有告警。」

Aster點頭。

「很好。」

李穎鬆了一口氣。

下一秒,Aster問:

「那為什麼它是 0.71?」

李穎愣住。

「因為……模型判斷它還在正常分布內。」

「這是答案嗎?」

「不是。」

「很好。」

Aster把椅子拉過來。

「把 score 拆開。」

畫面變成 feature attribution。

Traffic statistics。

Temporal deviation。

Destination novelty。

Graph neighborhood。

每一項都有 contribution。

其中一項微微偏高。

Graph neighborhood deviation。

李穎皺眉。

「這個 node 的單點特徵不高。」

「對。」

「但是鄰居有問題。」

「不是。」

Aster搖頭。

「鄰居沒有問題。」

「那是?」

「鄰居的組合有問題。」


06

這是 GNN 最容易被小說寫錯的地方。

很多人喜歡把它描述成:

AI 看懂了網路拓撲。

不。

GNN 沒有「看懂」。

它只是在某種 message-passing framework 下,把鄰居資訊聚合進 node representation。

最簡化可以寫成:

h_v^(l+1) = σ ( W_1 h_v^(l) + ∑_(u∈ N(v)) W_2 h_u^(l) )

但真正的系統不會只有一張靜態圖。

網路是時間性的。

所以:

G_t=(V,E_t)

而且:

E_t ≠ E_(t+1)

一條 edge 的存在,本身就是訊號。

它的出現時間也是。

消失時間也是。

頻率也是。

方向也是。

權重也是。

甚至與其他 edge 同時出現的 pattern,也是。

因此藍隊真正分析的,不是:

「這台機器連到哪裡?」

而是:

「這台機器的關係結構,正在如何改變?」

Aster盯著那條 edge。

「把時間拉長。」

李穎拉到七天。

再拉到三十天。

那條 edge 原本幾乎不存在。

然後在某一天開始出現。

一天。

兩天。

三天。

頻率緩慢增加。

沒有爆發。

沒有尖峰。

沒有任何戲劇性。

像一個人每天晚上走過同一條巷子。

第一次沒有人注意。

第二次也沒有。

到了第十五次,保全開始記得那張臉。


07

Morrow 看著同一條 edge。

「他們看到了。」

助手問:

「確定?」

「不是看到。」

Morrow 說。

「他們正在形成一個假設。」

「我們還沒被判定。」

「所以?」

「繼續。」

Morrow 沒有興奮。

他甚至沒有笑。

因為這不是遊戲。

在這個尺度上,真正的攻防沒有電影裡那種倒數計時。

沒有:

TEN.

NINE.

EIGHT.

真正的戰爭比較像兩個人在黑暗中摸索。

你伸手。

對方退半步。

你退。

對方再伸手。

每一個動作都改變下一個動作的條件。

這叫 feedback。

也是所有 adaptive system 最終會遇到的問題。


08

藍隊開始改變策略。

不是提高 anomaly threshold。

而是把單一模型的判斷拆開。

第一層:

Flow-level representation。

第二層:

Temporal behavior。

第三層:

Graph-level context。

第四層:

Cross-host correlation。

第五層:

Asset semantics。

一台財務部門工作站和一台測試伺服器做同樣的事。

分數不應該一樣。

這就是 context。

同樣的 encrypted traffic。

同樣的 destination。

同樣的 packet-size distribution。

如果發生在:

CI/CD runner

可能完全正常。

如果發生在:

domain controller

那就不是一回事。

資安最麻煩的地方就在這裡。

沒有絕對異常。

只有:

P(x|context)

以及:

P(x|context,history)


09

凌晨 03:41。

李穎突然說:

「有了。」

Aster沒有抬頭。

「什麼?」

「六台 endpoint。」

「一起?」

「不是。」

「時間?」

「三十六小時。」

Aster看過去。

畫面上出現六個 node。

它們彼此並沒有直接連線。

但各自的 behavior embedding 開始靠近。

不是 feature 一模一樣。

而是:

變化方向相似。

這比 signature 更麻煩。

也比 signature 更漂亮。

Aster問:

「誰先?」

李穎調出時間軸。

「第三台。」

「為什麼?」

「不知道。」

「很好。」

李穎看他。

「又很好?」

「不知道是好事。」

Aster站起來。

「因為知道答案的人,很容易開始相信答案。」


10

模型開始建立一個假設:

H_1: Common behavioral source

不是感染。

不是 malware。

甚至沒有證據證明存在 command-and-control。

只有六台 endpoint,在不同時間出現了相似的行為轉移。

如果把它畫成圖:

Host_1 → X

Host_2 → X

Host_3 → X

但 X 不是一台 server。

X 是一個latent behavioral pattern。

Aster看著它。

突然笑了。

李穎問:

「怎麼?」

「他很聰明。」

「紅隊?」

「嗯。」

「哪裡?」

「他沒有讓六台機器做同一件事。」

Aster指著圖。

「他讓它們看起來像各自正常。」


11

Morrow 的生成模型並沒有讓每一條 session 都長得一樣。

相反地。

它刻意保留 variation。

因為真正的人類流量從來不是 deterministic。

人不會每隔精確的十秒做一次事情。

使用者會停頓。

會重試。

會突然開另一個網頁。

會因為 Wi-Fi 抖一下重新連線。

會在午餐時間消失。

會在週五晚上突然產生一堆完全沒有商業價值的流量。

Noise 本身就是生命。

所以 Morrow 的模型學的不是「正常封包」。

它學的是:

正常世界的不完美。

這也是生成式模型真正有意思的地方。

不是生成得漂亮。

而是生成得足夠不漂亮。

因為過度完美本身就是異常。


12

藍隊開始檢查 entropy。

DNS。

TLS。

Flow timing。

每一個 feature 都在合理範圍。

甚至一些傳統 detector 的 confidence 反而下降。

李穎問:

「是不是我們想太多?」

Aster看著他。

「這句話是分析師最危險的一句話。」

「為什麼?」

「因為它不是結論。」

他指著畫面。

「它只是疲勞。」


13

04:12。

藍隊把 temporal graph 的 window 從 24 小時改成 14 天。

模型重算。

結果出現。

Anomaly score:

0.91。

李穎站起來。

「超過 threshold。」

Aster沒有動。

「不是這個。」

「什麼?」

「看 uncertainty。」

畫面右側出現:

H(Y|X)

Predictive uncertainty。

模型很確定:

這不太正常。

但模型不知道:

為什麼。

Aster點了一下。

「這才是我們真正需要的東西。」

李穎問:

「為什麼?」

「因為高 confidence 的錯誤很危險。」

「但 uncertainty?」

「它會告訴你哪裡沒有知識。」


14

凌晨 04:19。

Morrow 的螢幕亮了。

第一次。

不是警報。

是一個很小的數字。

Δ D_(KL) > ε

他看了一眼。

「他們換觀測窗口了。」

助手問:

「代表什麼?」

「代表他們不再相信短期統計。」

「那我們?」

Morrow 沒有回答。

他看著那條曲線。

然後把手放在桌上。

「現在才開始。」


15

接下來六個小時,沒有任何攻擊。

沒有 exploit。

沒有 privilege escalation。

沒有 data exfiltration。

沒有 spectacular event。

什麼都沒有。

這反而讓藍隊更加不安。

因為如果一個惡意行為存在,卻沒有產生任何傳統意義上的「結果」,那麼它到底是在做什麼?

Aster開始懷疑另一件事情。

「把 asset owner 拉出來。」

李穎問:

「現在?」

「現在。」

「四點半?」

「所以?」

李穎打電話。

十分鐘後,資料回來。

其中五台機器屬於一般使用者。

第六台:

資產管理伺服器。

Aster沉默。

李穎看著他。

「這是不是代表……」

「不要說代表。」

Aster打斷。

「我們只有 evidence。」

「那合理假設是?」

「有人正在建立關係。」

「為什麼?」

Aster看著那張圖。

「這才是問題。」


16

資安專家最討厭的一種錯誤,是把 correlation 當 causation。

六台機器行為相似。

不代表它們被同一個東西控制。

一條 edge 反覆出現。

不代表它是 C2。

一個 anomaly score 超過 threshold。

不代表 malware 存在。

模型甚至可能只是學到了:

某個部門剛好換了一批軟體。

或者:

某個 CDN 改了 routing。

或者:

某個 Windows update 改變了 telemetry。

所以Aster做了一件很老派的事情。

他叫人:

去問人。


17

上午 07:06。

第一個使用者說:

「昨天 IT 幫我更新了一個工具。」

第二個:

「我昨天重開機三次。」

第三個:

「不知道,我在跑測試。」

第四個:

「昨天晚上加班。」

第五個:

「我在下載公司文件。」

第六個……

「我昨天沒有碰那台機器。」

房間安靜下來。

李穎轉過頭。

Aster沒有說話。

「自動化任務?」

「沒有。」

「排程?」

「查了。」

「服務帳號?」

「沒有異常。」

Aster看著第六台。

「再查 process lineage。」


18

Process lineage 沒有異常。

Network lineage 也沒有。

Authentication 沒有。

DNS 沒有。

TLS 沒有。

一切都乾淨。

乾淨得令人討厭。

Aster突然說:

「把那台 machine 從 graph 裡移除。」

李穎愣住。

「什麼?」

「移除。」

「如果它是 pivot point 呢?」

「就是要看。」

李穎照做。

圖重新計算。

六個 node。

變成五個。

Anomaly score:

0.88。

仍然存在。

Aster點頭。

「很好。」

「所以第六台不是核心。」

「對。」

「那它是什麼?」

「可能只是被影響的觀測點。」


19

這就是圖模型最殘酷的地方。

Node 被移除。

Graph 還活著。

一個模型如果只靠單點 anomaly,攻擊者只需要控制單點。

但當模型開始理解 network behavior,攻擊者必須同時處理:

Node + Edge + Time + Context

四個維度。

這不是更難隱藏一個封包。

而是更難讓整個世界看起來合理。


20

Morrow 也知道。

他的模型開始收到一個反饋。

不是藍隊的告警。

而是某些 observation pattern 的變化。

防守者改了 window。

改了 graph aggregation。

改了 feature weighting。

這意味著:

防守者正在學。

Morrow 看著模型輸出。

一個新的 objective 出現:

min_θ D(P_θ(X),P_(normal)(X))

但旁邊還有另一個:

min_θ D(P_θ(G),P_(normal)(G))

他沉默了。

「他們開始看 graph。」

助手問:

「需要改嗎?」

Morrow 看著他。

「不。」

「為什麼?」

「因為現在改,反而會告訴他們我們知道。」

「那我們什麼都不做?」

「對。」

助手皺眉。

Morrow 說:

「最好的 camouflage,不是永遠不變。是讓對方不知道你什麼時候會變。」


21

中午 12:43。

藍隊的模型第一次產生了一個新標籤。

不是:

MALWARE

不是:

C2

不是:

EXFILTRATION

而是:

UNRESOLVED BEHAVIORAL CLUSTER

Aster看著那幾個字。

「這個名字誰取的?」

李穎說:

「模型。」

「很好。」

「我們不應該直接命名成 C2?」

「不。」

Aster搖頭。

「一旦你把它叫 C2,所有人都會開始找證明它是 C2 的資料。」

「這叫 confirmation bias。」

「沒錯。」

他把椅子往後推。

「模型可以不知道。」

「人不能?」

「人更應該知道自己不知道。」


22

晚上 21:17。

Morrow 的系統第一次收到完整的 telemetry summary。

不是原始封包。

不是 packet capture。

而是一組摘要。

Graph embedding。

Temporal statistics。

Endpoint context。

Risk score。

他看了很久。

然後笑了。

「他們把我們變成一個 node。」

助手問:

「什麼?」

Morrow 指著畫面。

「以前我們在流量裡。」

「現在我們在他們的模型裡。」

「那不是更危險?」

「是。」

「那為什麼你笑?」

Morrow 沉默了一會兒。

「因為這代表我們終於有東西可以攻擊了。」


23

但那不是一句勝利宣言。

因為另一個房間裡,Aster也在看著同一件事情。

他看著模型生成的 latent representation。

某一片區域出現了一個極細微的聚類。

它不符合任何已知 benign class。

也沒有足夠 evidence 被標成 malicious。

Aster問:

「這是什麼?」

沒有人回答。

他把畫面放大。

那個 cluster 很小。

非常小。

像黑夜裡一顆幾乎看不見的星。

但它正在移動。

不是位置。

是邊界。

Aster突然明白。

這不是一次普通的 detection problem。

這是一場 distribution war。


24

正常與異常從來不是兩個固定集合。

它們之間有一條會移動的邊界。

今天:

x ∈ Normal

明天:

x ∉ Normal

而後天:

Normal_(t+1) ≠ Normal_t

當攻擊者開始生成新的行為。

防守者開始重新學習。

防守者改變觀測。

攻擊者再次適應。

這個系統便不再是靜態分類。

它開始成為:

𝒜 ↔ 𝒟

一個 feedback loop。

而真正危險的地方是:

雙方都在改變對方。


25

凌晨 03:02。

距離第一個異常行為已經四十七小時。

藍隊終於決定隔離一台 endpoint。

沒有大規模封鎖。

沒有 kill switch。

沒有把整個 domain 拉黑。

Aster只隔離了一台。

李穎問:

「為什麼只一台?」

「因為我們不知道。」

「如果是攻擊?」

「那我們觀察它怎麼反應。」

「如果不是?」

「那我們沒有摧毀整個網路。」

這就是成熟的防守。

不是勇敢。

是知道錯誤決策的成本。


26

隔離發生後。

什麼都沒有。

另外五台繼續運作。

Graph 沒有崩。

Temporal pattern 沒有改變。

沒有 beacon storm。

沒有 fallback。

沒有 emergency behavior。

李穎說:

「可能抓錯了。」

Aster點頭。

「有可能。」

「那我們是不是白忙?」

Aster笑了一下。

「不是。」

「至少知道了一件事。」

「什麼?」

「如果它真的在控制什麼——」

他看著圖。

「它沒有急。」


27

同一時間。

Morrow 看著被隔離的 endpoint。

他的模型沒有重試。

沒有切換。

沒有改變。

他只是關掉了一條 edge。

然後等待。

助手問:

「這樣不會讓藍隊發現?」

Morrow 說:

「他們早就發現了。」

「那?」

「我想知道。」

「什麼?」

Morrow 看著那條消失的 edge。

「他們到底是在抓我們,還是在測試自己的模型。」


28

三天後。

藍隊仍然沒有給出最終結論。

因為所有證據都指向同一個令人不舒服的答案:

沒有單一 indicator 能證明它。

沒有 payload。

沒有固定 destination。

沒有固定 timing。

沒有 signature。

沒有明確 malware family。

只有一個不斷變化的 behavioral cluster。

Aster最後在報告上寫:

「目前無法證明惡意活動存在。」

停了一下。

又加了一句:

「但目前亦無法證明該行為屬於自然產生。」

李穎看著兩句話。

「這算結論嗎?」

Aster說:

「這是我們目前最誠實的結論。」


29

凌晨。

Aster一個人留在 SOC。

咖啡已經換成水。

牆上的圖還在跑。

Node。

Edge。

Timestamp。

Embedding。

Probability。

世界被壓縮成一個個數字。

他忽然想起二十年前。

那時候他第一次看見 TCP three-way handshake。

SYN。

SYN/ACK。

ACK。

那時候網路還很單純。

人發出訊號。

另一端回答。

然後連線建立。

後來一切變得複雜。

TLS。

CDN。

QUIC。

Cloud。

Container。

Service Mesh。

Machine Learning。

每一層都在試圖解決上一層留下來的問題。

最後,大家發現:

訊號沒有消失。

只是變得更難理解。

Aster看著那張 graph。

輕聲說:

「幽靈不是沒有訊號。」

「幽靈只是讓你不知道,哪一個訊號值得相信。」


30

而在另一端。

Morrow 關掉螢幕。

房間陷入黑暗。

他的系統仍然運作。

沒有 command。

沒有 heartbeat。

沒有明顯控制流。

只有一個很小的 latent state 在持續更新。

z_(t+1)=f(z_t,o_t)

觀測進來。

狀態改變。

策略不動。

至少現在不動。

Morrow 站起來。

走到門口。

停了一下。

他回頭看螢幕。

那張圖上,有一條極細的線。

它不指向任何 server。

不指向任何 domain。

甚至不指向任何已知資產。

它只連著一群原本互不相關的節點。

像某種尚未完成的星座。

Morrow 說:

「別急。」

「讓他們先學會看。」


31

同一秒。

藍隊的模型完成一次 retraining。

新的 baseline 被建立。

新的 normal distribution 出現。

新的 graph representation 被寫入模型。

世界被重新定義了一次。

沒有警報。

沒有槍聲。

沒有駭客電影裡那種紅色警示燈。

只有一個很小的數字:

Δ P = 0.013

百分之一點三。

人類看不出來。

模型看得見。

而真正危險的事情,往往就是這樣開始。

不是爆炸。

不是入侵。

不是一個駭客坐在黑色螢幕前敲下 Enter。

而是某個原本沒有意義的東西,經過足夠長的時間之後,開始形成意義。

第一個幽靈信號,從來不是一個封包。

它是一個問題。

而當防守者開始問:

「為什麼這些東西會同時出現在這裡?」

攻擊者真正害怕的,也就不再是 signature。

而是——

有人開始看懂關係。


技術附錄:本篇世界觀中的抽象模型

以下僅作為小說設定,不對應可直接部署的攻擊程式。

Network Telemetry

↓

Flow Features + Temporal State + Graph Context

↓

Behavioral Encoder

↓

Latent Space

↓

Anomaly Estimation + Graph Deviation

↓

Contextual Risk

↓

Human / Automated Triage

紅隊端的抽象目標並不是「讓封包消失」,而是試圖讓 observable behavior 落在正常分布附近:

D(P_(observed),P_(normal)) → 0

藍隊則逐漸放棄單一 observable。

它開始同時估計:

P(X_t|X_(t-k:t-1))

以及:

P(X_t,G_t|Context,History)

因此攻防真正的差異,從:

「誰比較會藏?」

逐漸變成:

「誰能更快發現對方正在改變觀測模型?」

這也是第一篇真正的終點。

不是紅隊成功。

不是藍隊成功。

而是雙方都第一次意識到:

下一場戰爭,不會發生在封包裡。

它會發生在「什麼叫正常」這件事情本身。