iT邦幫忙

0

Day28 BehaviorGuard 可靠性強化:告警去重、監控生命週期與短暫程序漏報實測

  • 分享至 

  • xImage
  •  

前面幾天,我們已經讓 BehaviorGuard 具備 Process、Network、File、Persistence 等行為偵測能力,並逐步完成 Correlation Engine(事件關聯引擎)、Response Policy(回應政策)與受控環境的自動化回應。

但一套資安監控系統,不是能夠偵測到攻擊就代表已經完善。

例如:

  • 同一個檔案修改事件,會不會產生大量重複告警?
  • 網路連線關閉後重新開啟,系統還會再次偵測嗎?
  • 按下 Ctrl+C 時,所有監控程序是否正常停止?
  • 如果要監控其他目錄,是不是只能修改程式碼?
  • 如果惡意程序只執行 0.1 秒,系統真的看得到嗎?

這些問題都與 Reliability(可靠性) 有關。

因此,Day28 的目標不是增加更多偵測規則,而是改善 BehaviorGuard 原本已經存在的功能,並透過實際實驗找出限制。


一、改善 File Alert Deduplication(檔案告警去重)

在 File Monitor 中,Watchdog 會持續監控檔案系統事件。

例如建立一個檔案:

echo "Hello" > test.txt

Linux 可能產生多個事件:

CREATED
MODIFIED

這並不一定代表發生了兩次獨立的安全事件。

如果每次異動都直接產生 Alert,SOC(Security Operations Center,資安監控中心)分析人員可能收到大量重複告警。

因此,我們改進了 detection/alert_manager.py,利用以下資訊判斷是否需要抑制重複告警:

  1. Rule ID:觸發哪一條偵測規則。
  2. File Path:哪一個檔案。
  3. SHA256:檔案內容是否改變。
  4. Suppression Window:短時間內是否已發出相同告警。

目前使用的告警抑制窗口為 5 秒。

例如同一個檔案,短時間內觸發相同規則,而且 SHA256 沒有改變,就可以避免重複發出內容相同的告警。

不過,若檔案內容改變,SHA256 也會改變,系統就應該允許新的告警。

另外,針對 BG-FILE-005(Executable Permission Added),還必須考慮檔案權限。

原因是:

chmod +x test.sh

只改變檔案的執行權限,不一定會改變 SHA256。

如果只比較內容雜湊,就可能忽略有安全意義的權限變動。

因此我們讓這條規則的去重判斷也包含 Mode 與 Executable 狀態。

這次完成了 9 項去重測試,並使用實際 Watchdog 事件驗證相同內容抑制、不同內容重新告警的情況。

學到的資安觀念

Alert Deduplication(告警去重)不是單純刪除重複紀錄,而是判斷兩個事件是否具有相同的安全意義。

設計不良的去重機制可能減少告警數量,卻同時造成真正重要的事件被忽略。


二、檢查 Correlation Engine 與 Response Safety

BehaviorGuard 已經可以把不同來源的安全事件整合分析。

例如:

可疑 Python 執行
        ↓
可疑腳本檔案建立
        ↓
Persistence 行為
        ↓
Correlation Engine
        ↓
判斷是否形成可疑行為鏈

在 Day28,我們針對 BG-CORR-006 進行誤報控制測試,確認事件關聯不應只因為幾種 Alert 同時存在就直接成立。

目前的實作會利用 UID、關聯腳本路徑與時間窗口等條件進行比對。

例如,兩個事件雖然發生在同一段時間,但如果 UID 或腳本路徑不同,就不應輕易認定是同一條攻擊鏈。

這次完成了 7 項相關測試,包括不同 UID、不同路徑、事件順序與重複觸發等情況。

Response Safety

此外,我們也重新測試自動化回應政策。

目前 BehaviorGuard 對一般即時偵測事件採取保守方式:

Detection
    ↓
Alert
    ↓
Response Policy
    ↓
DRY_RUN / REVIEW_REQUIRED
    ↓
Audit Log

其中:

  • DRY_RUN:模擬決策,不直接執行對系統有影響的動作。
  • REVIEW_REQUIRED:需要進一步確認或人工審核。
  • Audit Log:記錄回應決策,方便後續調查。

這次 10 項 Response Policy 測試通過,包括無效 PID、無效目標與受控實驗條件的檢查。

需要特別區分的是,BehaviorGuard 仍保留先前設計的受控實驗回應功能;經過登錄與授權的實驗目標,可以依據對應政策執行特定回應。因此不能把一般 Live Alert 的 DRY_RUN 直接解讀成所有情境下都不可能有實際動作。

學到的資安觀念

EDR 的 Response 不只是「偵測到就封鎖」。

真正需要考慮的是:

這筆告警是否有足夠的證據?執行動作是否安全?如果誤判,會不會影響正常業務?


三、Graceful Shutdown:讓三個 Monitor 正常停止

原本 BehaviorGuard 同時執行三個監控器:

BehaviorGuard Agent
    │
    ├── Process Monitor
    ├── Network Monitor
    └── File Monitor

但是三個監控器的停止方式並不一致。

Process 與 Network Monitor 使用:

while True:
    ...
    time.sleep(1)

而主程式主要依靠 daemon thread 隨 Python 行程結束。

這種方式雖然可以停止程式,但不是完整的執行緒生命週期管理。

因此,我們加入 threading.Event() 作為統一停止訊號。

概念如下:

stop_event = threading.Event()

監控器改成:

while not stop_event.is_set():
    # 執行監控
    stop_event.wait(1)

當使用者按下 Ctrl+C:

stop_event.set()

三個監控器收到停止訊號後,便能離開各自的監控迴圈。

主程式再使用 join() 等待執行緒結束。

實際測試結果

[INFO] Shutdown requested
[STOP] Process Monitor
[STOP] Network Monitor
[STOP] File Monitor
[PASS] All three monitor threads stopped
[INFO] BehaviorGuard shutdown complete

這次測試證明,三個監控執行緒在正常操作下都能收到停止訊號並完成關閉。

實作過程遇到的問題

這部分其實花了不少時間除錯。

最初使用字串比對方式自動修改 Python 函式,結果因為函式格式與預期不同,出現:

RuntimeError: Monitor function boundaries not found

後來又遇到:

RuntimeError: Unexpected function signature

最後改為利用 Python AST(Abstract Syntax Tree,抽象語法樹)定位函式,並讓修改腳本能辨識原本是否已經加入 stop_event,才成功完成升級。

另外,由於 Kali 預設使用 zsh,而 status 是 zsh 的特殊唯讀參數,所以使用:

status=$?

會出現:

zsh: read-only variable: status

之後改用其他變數名稱才解決。

這讓我學到,維護程式時不能只考慮功能本身,也要注意不同 Shell、原始程式版本與自動修改工具的可靠性。


四、Network State Tracker:處理連線消失與再次出現

原本 Network Monitor 使用兩個集合:

seen_connections = set()
seen_alerts = set()

用途是避免同一個網路連線持續被重複輸出。

但問題是,這些集合會持續累積已經看過的連線資訊。

如果同一個連線特徵消失後再次出現,舊機制可能還記得之前的狀態,造成新事件被忽略。

因此我們新增:

agent/network_state.py

並實作 NetworkStateTracker。

核心概念是每次取得新的網路快照,和前一次比較。

current = {
    self.connection_key(event)
    for event in events
}

new_keys = current - self.active
disappeared = self.active - current

其中:

  • current:這一次掃描看見的連線。
  • self.active:上一輪記錄的連線。
  • new_keys:這一次新出現的連線。
  • disappeared:上一輪存在、這次消失的連線。

這樣就能保留目前存在的連線狀態,而不是永久保存所有歷史連線。

實際測試

我們使用 socat 建立本機 TCP Listener:

socat TCP-LISTEN:19999,bind=127.0.0.1,reuseaddr -

第一次啟動時,BehaviorGuard 偵測到:

Rule       : BG-NET-001
Name       : Suspicious Network Listener
Severity   : HIGH
Process    : socat
PID        : 2942453
Local      : 127.0.0.1:19999

接著關閉 socat,再次啟動相同 Port。

第二次結果:

Rule       : BG-NET-001
Process    : socat
PID        : 2942806
Local      : 127.0.0.1:19999

兩次都成功觸發 BG-NET-001,並產生不同的 Event ID。

而且這兩次事件的 Response Policy 都維持 DRY_RUN,沒有對測試程序執行未經批准的即時處置。

需要注意,兩次 PID 不同,因此這項實測證明新程序可以再次被偵測;相同 PID、相同連線特徵消失後重新出現的邏輯,則是透過 State Tracker 單元測試驗證。


五、File Monitor:從固定路徑變成可設定路徑

原本 File Monitor 只能監控:

WATCH_PATH = "/tmp/behaviorguard_test"

這對初期開發很方便,但不利於後續擴充。

因此,我們加入環境變數 BG_WATCH_PATH,讓使用者能指定不同監控目錄。

例如:

BG_WATCH_PATH="$HOME/behaviorguard_day28_watch_test" \
PYTHONPATH=. python3 -u agent/behaviorguard.py

這樣就不需要每次都打開 Python 程式修改路徑。

同時,我們也加入路徑驗證,避免任意指定根目錄 / 或 /etc。

實際測試

首先建立測試目錄:

mkdir -p ~/behaviorguard_day28_watch_test

啟動後,系統顯示:

[INFO] Watching: /home/kun/behaviorguard_day28_watch_test

接著建立測試檔案:

echo "Day28 file watch test" > ~/behaviorguard_day28_watch_test/test.txt

BehaviorGuard 成功收到:

Action      : CREATED
File Name   : test.txt
Extension   : .txt
Hidden      : False
Executable  : False
Size        : 22
UID         : 1000

以及後續的:

Action      : MODIFIED
Path        : /home/kun/behaviorguard_day28_watch_test/test.txt

代表 File Monitor 已經能接收指定目錄的檔案事件。

但需要注意,目前新增的功能主要是監控範圍設定;原本部分 Detection Rules 仍針對固定實驗目錄進行路徑正規化,因此不能直接宣稱所有檔案規則都已完整支援任意監控目錄。


六、最重要的實驗:Process Monitor 會漏掉短暫程序嗎?

這是今天我覺得最有收穫的部分。

BehaviorGuard 的 Process Monitor 目前是透過 Linux /proc 取得程序資訊。

但它採用的是 Polling(輪詢),意思是每隔一段時間才檢查系統有哪些程序。

例如:

0.0 秒:程序開始執行
0.1 秒:程序已經結束
1.0 秒:Process Monitor 再次掃描

這樣就可能完全看不到剛才執行的程序。

第一次測試發生的問題

我們最初測試不同存活時間的程序,竟然全部得到 100% 觀察率。

後來發現測試程式存在錯誤:使用了已經設定為 True 的 threading.Event.wait(),導致輪詢沒有按照預期等待。

換句話說,測試工具本身就有問題。

修正後,我們也排除 Zombie Process(殭屍程序)等可能造成誤判的因素。

重新測試,每組 10 次:

程序存活時間 觀察成功次數 觀察率
0.1 秒 3/10 30%
0.3 秒 3/10 30%
0.5 秒 6/10 60%
1.0 秒 10/10 100%
2.0 秒 10/10 100%

這次結果顯示,程序存活時間越短,越有可能被輪詢方式漏掉。

為什麼這是資安問題?

假設攻擊者透過 Python 快速執行一段可疑程式。

即使我們寫了:

BG-CMD-002
Python Inline Code Execution

如果監控器根本沒看見這個 Python 程序,後面的 Detection Engine 就沒有事件可以分析。

這就是 Telemetry Gap(遙測資料缺口):不是規則無法辨識,而是規則沒有收到事件。


七、改善 Process Polling:比較 1 秒與 0.2 秒

發現問題後,我們沒有直接認為 Polling 不可使用,而是先嘗試降低輪詢間隔。

原本的程式:

stop_event.wait(1)

改成:

stop_event.wait(get_process_interval())

再透過環境變數控制:

BG_PROCESS_INTERVAL=0.2 \
PYTHONPATH=. python3 -u agent/behaviorguard.py

這樣就可以讓 Process Monitor 每 0.2 秒檢查一次系統程序。

接著我們另外建立比較實驗,每個條件測試 15 次。

實際測試結果

程序存活時間 1 秒輪詢 0.2 秒輪詢
0.1 秒 0% 53.3%
0.3 秒 20% 100%
0.5 秒 46.7% 100%
1.0 秒 100% 100%

我們可以觀察到,縮短輪詢間隔後,短暫程序的觀察率有明顯提升。

其中 0.3 秒程序的結果尤其明顯:

1.0 秒輪詢:3/15
0.2 秒輪詢:15/15

但 0.1 秒程序仍然只有 53.3% 的觀察率,代表即使增加輪詢頻率,短暫程序仍可能被漏掉。

另外,這次 Benchmark 使用獨立的 psutil 掃描執行緒,測試的是程序觀察能力,並不等於完整 BehaviorGuard 的端到端 Detection 成功率。

Polling 是否越快越好?

不一定。

當輪詢從每秒 1 次增加到每秒約 5 次,系統也需要更頻繁地收集程序資訊。

因此可能造成:

  • CPU 使用量增加。
  • 更多系統呼叫與 /proc 讀取。
  • 在程序數量很多的主機上產生額外負載。

本次尚未量測 CPU 和記憶體消耗,所以還不能判定 0.2 秒就是最理想的監控間隔。

未來可以再研究 Linux Audit 或 eBPF 等事件式監控機制,減少對輪詢的依賴。


八、Day28 完成成果

今天並沒有新增大量 Detection Rules,但改善了 BehaviorGuard 的可靠性。

改善項目 今日成果
File Alert 去重 降低重複告警,加入權限變動考量
Correlation 驗證 測試 UID、路徑、時間等關聯條件
Response Safety 驗證 DRY_RUN 與受控回應條件
Graceful Shutdown 三個 Monitor 可正常停止
Network State Tracker 追蹤連線出現、消失與再次出現
File Monitor 支援自訂監控目錄
Process Monitor 支援設定輪詢間隔
Reliability Benchmark 實測短暫程序觀察率

這些改善讓 BehaviorGuard 不再只是單純觸發規則的 Python 程式,而是逐步具備監控狀態管理、安全回應控制與可靠性驗證的 EDR 原型。

不過,還有一些限制尚未解決,例如短暫程序的漏報、完整系統目錄的監控覆蓋、輪詢效能負載,以及事件式遙測機制。

九、今日心得

Day28 讓我對資安監控系統有了不同的理解。

以前我認為,只要寫出足夠多的 Detection Rules,就能讓系統越來越安全。

但今天才真正發現,偵測規則只是整個 EDR 系統的其中一部分。

如果資料收集不完整,規則就沒有辦法判斷;如果去重設計不良,重要事件可能被忽略;如果 Response 沒有安全控制,也可能因為誤報而影響正常系統。

最讓我印象深刻的是第一次 Benchmark 得到全部 100% 的結果。

一開始我以為這代表 BehaviorGuard 非常可靠,但後來才發現是測試方式有問題。修正後,才真正看見短暫程序的漏報現象。

這也讓我學到,資安工程師不能只看見 PASS 就認為系統安全,而是要確認測試方法是否正確、結果是否合理,以及還有哪些沒有被測試到的情況。

Day28 最後,我們雖然沒有徹底解決 Polling 的限制,卻已經能用實際數據說明問題、提出改善方式並驗證改善效果。

我認為這比單純增加幾條偵測規則更有意義。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言