Day 7 我完成了 BehaviorGuard 第一版的 Network Monitor,可以從 Linux 系統取得目前的 TCP 連線,也能偵測像 nc 這類工具建立的可疑 Listener。
但做到這裡後,我發現一個問題:
如果只是看到 nc、看到某個 Port 就發出 Alert,那其實離真正的 EDR 還有一段距離。
因為真實環境中本來就存在大量正常網路連線,例如瀏覽器、Web Server、系統更新、API、SSH 等。
如果每一條 Connection 都產生 Alert,SOC 人員反而會被大量警告淹沒。
所以 Day 8 的目標,就是讓 Network Detection 從單純的「條件判斷」,開始往:
Context + Behavior
的方向發展。
原本 Network Monitor 可能只知道:
Process : nc
PID : 1653690
Remote : 127.0.0.1:9999
但只知道 Process Name 還不夠。
因此我加入 Process Context,透過 Linux /proc 取得:
PID
PPID
Process
Parent Process
UID
Command Line
實際取得的資訊例如:
PID : 1653690
PPID : 1653552
Process : nc
Parent : zsh
Command : nc 127.0.0.1 9999
UID : 1000
這樣 BehaviorGuard 看到的就不只是:
nc 建立了一條 Connection
而是可以理解成:
zsh
└── nc
└── TCP Connection
這些資訊之後都可以成為判斷行為的重要 Context。
接著我加入 Network Context,讓 BehaviorGuard 判斷 Remote IP 屬於哪一種範圍。
主要分成:
LOOPBACK
PRIVATE
EXTERNAL
MULTICAST
UNSPECIFIED
UNKNOWN
例如:
127.0.0.1
→ LOOPBACK
192.168.1.50
→ PRIVATE
10.0.0.5
→ PRIVATE
8.8.8.8
→ EXTERNAL
這裡有一個很重要的觀念:
EXTERNAL 不代表惡意。
例如 Firefox 連到 Internet:
firefox → External :443
本來就是正常行為。
所以不能寫成:
External Connection
↓
HIGH Alert
而是要搭配 Process、Parent、Command、Port 等資訊一起判斷。
接著我升級 network_monitor.py。
現在一筆 Network Event 會包含:
Timestamp
PID
PPID
Process
Parent
UID
Command
TCP State
Local IP
Local Port
Remote IP
Remote Port
Network Scope
實際執行可以看到:
========== Network Event ==========
Process : nc
PID : 1653690
Parent : zsh
UID : 1000
Command : nc 127.0.0.1 9999
State : ESTAB
Local : 127.0.0.1:46686
Remote : 127.0.0.1:9999
Network Scope : LOOPBACK
這裡我開始理解一個 EDR 很重要的概念:
Telemetry 不等於 Alert。
系統可以收集很多 Event,但不是每個 Event 都應該變成資安警告。
目前 BehaviorGuard 已經建立六條 Network Detection Rule。
偵測像 nc、ncat、socat 這類工具建立 Listening Socket。
例如:
nc -l -p 9999
BehaviorGuard 發現這類工具正在監聽 Port 時,就會產生 Detection Signal。
第二條則是觀察這類工具是否建立 TCP Connection。
例如:
nc → 127.0.0.1:9999
如果 Connection 進入:
ESTAB
就會產生:
BG-NET-002
Suspicious Network Connection
Severity: MEDIUM
第三條開始加入 Context。
例如:
python3
+
ESTAB
+
EXTERNAL
會被視為值得進一步觀察的行為。
但如果:
python3 → 127.0.0.1
Network Scope 是:
LOOPBACK
就不會因為這條 Rule 產生 Alert。
第四條不是直接把某個 Port 定義成惡意,而是觀察:
Process 與 Destination Port 的組合是否不尋常。
例如:
curl → External :443
這是非常常見的 HTTPS 行為,所以:
No Alert
但如果:
curl → External :4444
則產生:
BG-NET-004
Unusual Process Port Behavior
Severity: LOW
這裡故意只設定成 LOW。
因為使用 4444 並不能直接證明是攻擊,它只是一個值得注意的弱訊號。
第五條針對 Shell 與 Interpreter 的網路活動。
目前觀察:
bash
sh
zsh
python
python3
perl
ruby
例如:
python3 → External
可能同時觸發:
BG-NET-003
+
BG-NET-005
目前 Detection Layer 會保留兩個不同的 Signal。
未來再透過 Correlation 與 Risk Scoring 判斷它們是否屬於同一個 Incident。
今天最重要的是:
BG-NET-006
Connection Burst / Scan-like Pattern
前面的 Rule 大部分都是:
一筆 Event
↓
Rule
↓
Alert
但是有些攻擊行為不能只看一筆 Event。
例如:
nc → 9001
nc → 9002
nc → 9003
nc → 9004
nc → 9005
單獨看:
nc → 9001
可能沒有辦法判斷發生什麼事情。
但是把多筆 Event 合起來:
同一組執行來源
+
同一 Destination
+
10 秒內
+
連線到 5 個不同 Destination Ports
就開始具有 Scan-like Behavior 的特徵。
因此我建立:
detection/network_behavior.py
並使用:
from collections import defaultdict, deque
保存最近的 Network Activity。
目前設定:
WINDOW_SECONDS = 10
PORT_THRESHOLD = 5
也就是:
在 10 秒時間窗口內觀察是否出現多個不同 Destination Port。
這也是 BehaviorGuard 第一次開始具有「短期記憶」。
從:
Event → Alert
變成:
Event 1
↓
記住
Event 2
↓
記住
Event 3
↓
記住
Event 4
↓
記住
Event 5
↓
分析整體行為
↓
BG-NET-006
第一次設計 BG-NET-006 時,我使用:
PID + Process + Remote IP
作為事件的分組依據。
但真實測試後發現:
nc → 9001
nc → 9002
nc → 9003
nc → 9004
nc → 9005
其實會產生五個不同 PID。
例如:
PID 1675805 → :9001
PID 1675806 → :9002
PID 1675807 → :9003
PID 1675808 → :9004
PID 1675809 → :9005
如果只看 PID,BehaviorGuard 會認為:
五個完全不同的 Process
但進一步觀察後發現它們其實具有:
相同 Parent
相同 PPID
也就是:
zsh
│
┌───────┼───────┐
↓ ↓ ↓
nc nc nc
↓ ↓ ↓
:9001 :9002 :9003
所以後來把 BG-NET-006 的 Execution Context 改成利用:
PPID
+
Process
+
Remote IP
進行事件聚合。
這樣即使 Child Process 的 PID 不同,只要它們來自同一個 Parent Context,就可以一起分析。
接著我建立五個 Listener:
for p in 9001 9002 9003 9004 9005; do
nc -l -p $p >/dev/null 2>&1 &
done
再建立五條 Connection:
for p in 9001 9002 9003 9004 9005; do
nc 127.0.0.1 $p >/dev/null 2>&1 &
done
BehaviorGuard 成功產生:
BG-NET-006
Connection Burst / Scan-like Pattern
Severity: HIGH
原本以為成功了。
但仔細看 Alert 後,發現一個問題:
Listener 那一側竟然也觸發 BG-NET-006。
例如:
nc -l -p 9001
接受 Client Connection 後可能看到:
9001 ← 53944
9002 ← 59880
9003 ← 39442
9004 ← 45504
9005 ← 34342
這些:
53944
59880
39442
45504
34342
是 Client 使用的臨時 Port。
但 BG-NET-006 第一版看到:
很多不同 Remote Ports
就把它判斷成 Scan-like Pattern。
這就是今天遇到的:
False Positive(誤報)
因此我再次修改 BG-NET-006。
對目前 Prototype 的 nc / ncat 測試,先利用 Command Context 判斷它是不是 Listener。
核心邏輯:
if process in ["nc", "ncat"]:
normalized_command = f" {command} "
if (
" -l " in normalized_command
or " --listen " in normalized_command
):
return None
也就是:
nc -l -p 9001
↓
Listener
↓
不加入 BG-NET-006 的掃描行為統計
而:
nc 127.0.0.1 9001
↓
Client Connection
↓
加入 BG-NET-006 的 Behavior History
這是目前 Prototype 的簡化處理方式,之後如果要做得更完整,還需要更可靠地判斷 Connection Direction。
修改完 Rule 後,我重新執行相同測試。
這次 Listener:
nc -l -p 9001
nc -l -p 9002
nc -l -p 9003
nc -l -p 9004
nc -l -p 9005
沒有再錯誤觸發 BG-NET-006。
而 Client:
nc → 127.0.0.1:9001
nc → 127.0.0.1:9002
nc → 127.0.0.1:9003
nc → 127.0.0.1:9004
nc → 127.0.0.1:9005
成功觸發:
========== BehaviorGuard Network Alert ==========
Rule : BG-NET-006
Name : Connection Burst / Scan-like Pattern
Severity : HIGH
Description:
nc processes from the same execution context
connected to 5 different destination ports
on 127.0.0.1 within 10 seconds.
這代表這次 Rule Tuning 成功降低了實際測試中發現的 False Positive。
目前 BehaviorGuard 的 Network Detection 大致變成:
Linux Network
↓
Network Monitor
↓
Raw Network Telemetry
↓
Process Context
+
Network Context
↓
Enriched Network Event
↓
Detection Engine
│
├── BG-NET-001
├── BG-NET-002
├── BG-NET-003
├── BG-NET-004
├── BG-NET-005
└── BG-NET-006
↓
Stateful Detection
↓
Alert
目前完成六條 Network Rules:
BG-NET-001
Suspicious Listener
BG-NET-002
Suspicious Network Connection
BG-NET-003
Unexpected External Connection
BG-NET-004
Unusual Process / Port Behavior
BG-NET-005
Shell / Interpreter Network Activity
BG-NET-006
Connection Burst / Scan-like Pattern
今天最大的收穫其實不是多寫了幾條 Rule,而是開始理解 Detection Engineering 真正困難的地方。
例如:
python3 → Internet
可能是正常 API Request,也可能是其他程式正常連線。
所以不能看到一個「看起來可疑」的東西就直接判定遭到攻擊。
需要更多 Context。
EDR 可以收集大量:
Process Event
Network Event
File Event
Command Event
但如果:
每個 Event
↓
全部 Alert
那最後 SOC 人員會被大量 Alert 淹沒。
所以 Detection 的目的不是「產生越多 Alert 越好」。
而是找出真正值得分析的 Behavior。
今天第一次 BG-NET-006 其實成功偵測到了:
很多不同 Remote Ports
但是後來才發現:
那些 Port 是 Listener 看到的 Client Ephemeral Ports。
也就是:
程式邏輯沒有壞
但:
資安語意判斷錯了
這讓我第一次比較明顯感受到:
Rule 能跑,不代表 Rule 就寫對了。
今天完整經歷:
設計 Rule
↓
人工測試
↓
真實 Socket 測試
↓
產生 Alert
↓
分析 Alert
↓
發現 False Positive
↓
找出 Ephemeral Port 問題
↓
加入 Listener Filtering
↓
重新測試
↓
False Positive 降低
這比單純寫一條:
if suspicious:
alert()
複雜很多。
以前的 Rule 比較像:
看到 A
↓
Alert
今天第一次做到:
看到 A
看到 B
看到 C
看到 D
看到 E
↓
放進時間窗口
↓
分析整體 Pattern
↓
Alert
這就是 Stateful Detection 的概念。
也讓 BehaviorGuard 開始真正往「Behavior-based Detection」發展。
目前 BehaviorGuard 進度:
Process Detection ✅
Command Detection ✅
Network Detection ✅
File Detection ⏳
Persistence Detection ⏳
Correlation ⏳
Risk Scoring ⏳
Alert Deduplication ⏳
Response ⏳
今天最大的進步,是 BehaviorGuard 不再只是:
看到 nc
↓
Alert
而開始變成:
Telemetry
↓
Context Enrichment
↓
Detection Rule
↓
Behavior History
↓
Time Window
↓
Pattern Detection
↓
False Positive Analysis
↓
Rule Tuning
而且今天也真的遇到一次 False Positive,找出原因後重新調整 Detection Logic。
我覺得這也是目前做到 Day 8 為止,第一次比較明顯感受到:
資安偵測真正困難的不是「寫出 Alert」,而是讓 Alert 有意義。
下一步準備開始補 BehaviorGuard 的另一個 Telemetry Source。