iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
AI Security

把 AI 接進 SOC系列 第 24 篇

【Day 24】AI SOC 分析管線:MITRE 對應與規則式查表

  • 分享至 

  • xImage
  •  

昨天做完欄位自我審查。今天走一次分析管線——但只走前半段,規則式、不需要 LLM 的那一半。 後半段(AI 怎麼把這些東西講成一句話)留給明天跟 Day 26 分開處理,今天先把地基打好。


設計上,一筆告警要變成人看得懂的分析,大致走這幾步:解析欄位並對映實體 → 關聯聚合 → 分級 → MITRE 對應 → 產出。

這幾步裡,前四步其實不需要 AI——它們是純規則式的查表跟比對:欄位存不存在就是存不存在,規則有沒有在關聯清單裡就是有沒有,MITRE 編號有沒有對應卡片就是有沒有。 真正需要 AI 的,只有最後一步:把這些查表結果組織成一段人看得懂的敘事。

我想把這兩半分開處理是有原因的:如果我今天直接讓 AI 從頭做到尾,沒辦法區分「這個結論是查表查出來的」還是「AI 自己判斷出來的」。 分開之後,前半段的正確性可以直接用程式驗證(有沒有查到就是有沒有查到),後半段的可信度才是真正需要另外測試的東西——這部分我留到 Day 26。


用 Day 19 那筆真實資料當輸入
我寫了一支小腳本 build_case.py,把「欄位 → 實體」「關聯檢查」「分級因子檢查」「MITRE 查表」這四步串起來。輸入用的是 Day 19 那筆 sudo 失敗告警——只填入 dashboard 當時真的顯示過的欄位,其餘一律留空,不補值:

{
  "rule": {
    "level": 10,
    "description": "Three failed attempts to run sudo",
    "mitre": { "id": ["T1548.003"] }
  },
  "agent": { "name": "manager-host" },
  "data": { "win": {} }
}

跑完,實際輸出長這樣(節錄):

{
  "entities": {
    "host": "manager-host",
    "host_ip": null,
    "account": null,
    "source_ip": null,
    "workstation": null,
    "_note": "any null above means the field genuinely wasn't in the input, not that extraction failed silently"
  },
  "correlation": {
    "in_known_correlation_table": false,
    "note": "此 rule_id 不在本專案已實作的關聯規則清單內(或未提供 rule_id)。這不代表沒有關聯,只代表本 KB 目前沒有為它設計對應的關聯邏輯。"
  },
  "severity": {
    "rule_level": 10,
    "five_tier_mapping": "undefined — 門檻尚未定案,此處不臆造",
    "escalation_factors_checked": {
      "repetition_observed": {
        "description": "同一描述在短時間內出現 2 次",
        "determinable_from_input": true
      }
    }
  },
  "mitre": {
    "mitre_lookup": [{
      "id": "T1548.003",
      "has_technique_card": false,
      "fallback": "T1548",
      "note": "本 KB 只有 T1548 的廣泛技術卡,沒有子技術 T1548.003 的專屬卡片"
    }]
  }
}

一個真實跑出來的缺口:子技術沒有對應卡片
這個輸出裡最有意思的一段是 MITRE 那部分。 這筆告警帶的技術編號是 T1548.003(濫用 sudo 提權),但我的 14 張技術卡裡只有廣泛的 T1548,沒有這個子技術的專屬卡片。 腳本老實回報「沒有對應卡片,退回廣泛卡片,細節建議查 MITRE 官方確認」,而不是假裝有對到。

這正好是 Day 18 那句話的一個真實案例:MITRE 對應的完整度取決於我的規則集跟卡片有多少,不是取決於現實世界的技術有多細。 之前那句話是我推論出來的,今天這筆輸出是第一次真的用程式跑出一個具體例子。

其他欄位(帳號、來源 IP、工作站)全部是 null,原因很單純:這是 Linux 的 sudo 事件,不是 Windows 的 data.win.eventdata. 事件,那些欄位本來就不會存在。 腳本沒有因為欄位是空的就試著猜一個值填進去,只是老實回報「這個欄位真的不存在」——這個區分我覺得很重要,寫程式的時候特別留了一句註解提醒自己不要在這裡偷懶...

一個意外的小插曲:我自己也踩了編碼坑
跑這支腳本的時候,第一次執行,終端機印出來的中文全部變成亂碼。 查了一下,是 Windows 主控台預設編碼跟程式輸出的 UTF-8 對不上——這正是我 wiki 裡 powershell-51-encoding-traps.md 記錄過的同一類問題。 加上 PYTHONUTF8=1 這個環境變數才印對。

這件事本身沒有很嚴重,但我覺得值得記一筆:連我自己寫一支十分鐘的示範腳本,都會踩到自己之前記錄過的坑。 這某種程度上證明了那份坑清單是真的有用的——不是寫完就沒人看,是真的會在意外的地方冒出來提醒你。


明天
接下來想講知識庫怎麼設計—— 一份文件怎麼被切段、怎麼被找到、一個問題怎麼被路由到候選頁。 這篇我打算直接拿真實的 index.md 跟 routing 規則示範一次。

明天見。


上一篇
【Day 23】18 個欄位怎麼餵給 AI?
下一篇
【Day 25】RAG 知識庫設計:Chunking、Metadata 與 Routing
系列文
把 AI 接進 SOC 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言