iT邦幫忙

0

Day22 BehaviorGuard:使用 SIGTERM 實作受控程序終止測試

  • 分享至 

  • xImage
  •  

在 Day21,我們已經將 Detection Engine 與 Response Engine 串接起來,讓 BehaviorGuard 偵測到可疑程序後,可以根據 Alert 的 Severity 產生對應的回應建議。

不過,當時的 Response Engine 仍然使用 Dry Run 模式。

例如,當系統收到 HIGH Alert 時,只會顯示:

Action     : Recommend process termination
Mode       : DRY RUN
Executed   : False

也就是說,系統雖然會建議終止程序,但沒有真正對 Linux 執行任何終止動作。

因此,Day22 我們要開始實作真正的 Process Termination(程序終止)。

今天的目標並不是自動終止所有可疑程序,而是先在 Kali Linux 中建立一個受控測試程序,讓 Response Engine 使用 SIGTERM 發送終止訊號,並確認程序是否成功結束。


一、什麼是 Process Termination?

在 Linux 系統中,每個正在執行的程序(Process)都有一個 PID(Process ID),也就是程序識別碼。

例如,我們執行:

sleep 60

Linux 就會啟動一個 sleep 程序,讓它等待 60 秒後結束。

如果我們想提前結束這個程序,可以向它發送 Signal(訊號)。

Signal 是 Linux 用來通知程序發生特定事件的一種機制。

其中常見的兩種終止訊號是:

Signal 編號 說明
SIGTERM 15 要求程序結束,程序可以處理或忽略
SIGKILL 9 強制終止程序,無法被程序捕捉或忽略

這兩種訊號最大的差別在於,SIGTERM 允許程序在收到訊號後進行清理,例如關閉檔案或釋放資源。

SIGKILL 則是由核心強制終止程序,不會讓程序執行自己的訊號處理程式。

因此,今天我們先選擇 SIGTERM,而不是直接使用 SIGKILL。

不過,SIGTERM 並不保證程序一定會結束,所以發送訊號後還需要確認結果。


二、今天的實作流程

今天我們的測試流程如下:

建立 sleep 60 測試程序
          ↓
取得測試程序 PID
          ↓
呼叫 terminate_test_process()
          ↓
檢查授權參數
          ↓
確認程序物件與允許的測試指令
          ↓
傳送 SIGTERM
          ↓
等待程序結束
          ↓
確認處置結果

與 Day21 不同的是,這次真的會對 Linux 的測試程序發送終止訊號。

但目前尚未接入真正的 Detection-to-Termination 自動處置流程。


三、修改 Response Engine

首先進入專案:

cd ~/behaviorguard

備份原本的 Response Engine:

cp response/engine.py response/engine_day21_backup.py

接著編輯:

nano response/engine.py

我們保留原本的 handle_alert(),並在檔案下方新增一個函式:

import subprocess


def terminate_test_process(proc, authorized=False):

    print("\n========== BehaviorGuard Termination ==========")

    if not authorized:
        print("[DENIED] Authorization required.")
        return False

    if not isinstance(proc, subprocess.Popen):
        print("[DENIED] Invalid process object.")
        return False

    if proc.args != ["sleep", "60"]:
        print("[DENIED] Not an allowed test process.")
        return False

    if proc.poll() is not None:
        print("[INFO] Process already exited.")
        return False

    print(f"PID        : {proc.pid}")
    print("Action     : Process Termination")
    print("Mode       : CONTROLLED TEST")

    try:
        proc.terminate()
        proc.wait(timeout=3)

        print("Executed   : True")
        print("Result     : Process exited")
        print("==============================================")
        return True

    except subprocess.TimeoutExpired:
        print("[FAIL] Process did not exit in time.")
        return False

    except OSError as e:
        print(f"[ERROR] {e}")
        return False

1. 為什麼使用 subprocess?

subprocess 是 Python 標準函式庫,讓 Python 程式能夠建立及管理其他作業系統程序。

例如:

proc = subprocess.Popen(["sleep", "60"])

這行程式會在 Linux 啟動一個新的 sleep 60 子程序。

Popen() 會回傳一個程序物件,讓我們可以取得 PID、檢查程序狀態,或對該程序發送終止訊號。

2. 為什麼需要授權檢查?

if not authorized:
    print("[DENIED] Authorization required.")
    return False

這裡要求呼叫函式時,必須明確傳入 authorized=True,否則拒絕執行。

不過,這只是我們在程式中設計的基本控制條件,不是真正的使用者身分驗證或企業級授權機制。

3. 為什麼檢查 Popen 物件?

if not isinstance(proc, subprocess.Popen):
    return False

這行會確認傳入的是 Python 的 Popen 程序物件,而不是任意數字或其他資料型態。

接著再檢查:

if proc.args != ["sleep", "60"]:
    return False

這次只允許指定的 sleep 60 測試指令。

需要注意,這些檢查是受控測試的初步限制,還不是完整的程序身分驗證。

4. poll() 是什麼?

if proc.poll() is not None:
    return False

poll() 用來檢查子程序是否已經結束。

如果回傳 None,代表程序當下還沒有結束。

如果回傳整數,則代表程序已經結束,該數字是程序的結束狀態。

因此,在發送 SIGTERM 前,我們先確認程序仍在執行。

5. terminate() 與 wait() 的差別

今天最重要的兩行是:

proc.terminate()
proc.wait(timeout=3)

在 Linux 上,proc.terminate() 會向指定的子程序發送 SIGTERM。

但發送訊號並不代表程序已經結束。

因此,我們接著使用 proc.wait(timeout=3),最多等待三秒,確認子程序退出並取得其結束狀態。

如果三秒內沒有結束,Python 就會產生 subprocess.TimeoutExpired 例外。

這樣就能區分「已經發送終止訊號」與「已確認程序結束」。

不過,wait() 確認的是程序已退出,並不能單靠它證明程序一定是因 SIGTERM 而結束。


四、建立測試程式

完成 Response Engine 後,我們另外建立:

nano test_day22.py

程式如下:

import subprocess
from response.engine import terminate_test_process

print("========== Day22 Response Test ==========")

# 產生自己的測試程序
proc = subprocess.Popen(["sleep", "60"])

print(f"[TEST] Created process PID: {proc.pid}")

try:
    result = terminate_test_process(
        proc,
        authorized=True
    )

    if result and proc.poll() is not None:
        print("[PASS] Test process terminated successfully.")
    else:
        print("[FAIL] Termination was not confirmed.")

finally:
    if proc.poll() is None:
        proc.terminate()

        try:
            proc.wait(timeout=3)
        except subprocess.TimeoutExpired:
            proc.kill()
            proc.wait()

print("[INFO] Day22 test completed.")

這次測試主要分成三個部分。

第一部分:建立真實子程序

proc = subprocess.Popen(["sleep", "60"])

讓 Kali Linux 建立一個等待 60 秒的測試程序。

第二部分:呼叫 Response Engine

result = terminate_test_process(
    proc,
    authorized=True
)

這次傳入真正建立的程序物件,並設定測試授權參數。

第三部分:確認與清理

if result and proc.poll() is not None:
    print("[PASS] Test process terminated successfully.")

這裡同時確認終止函式回傳成功,以及子程序確實已退出。

另外,我們使用 finally 確保即使測試失敗,也會嘗試清理測試程序,避免留下不必要的背景程序。

finally 中的清理動作屬於測試程式的工作,不是 Response Engine 的自動處置機制。


五、實際測試結果

完成程式後,執行:

PYTHONPATH=. python3 test_day22.py

這次在 Kali Linux 實際取得的結果:

========== Day22 Response Test ==========
[TEST] Created process PID: 2538718

========== BehaviorGuard Termination ==========
PID        : 2538718
Action     : Process Termination
Mode       : CONTROLLED TEST
Executed   : True
Result     : Process exited
==============================================

[PASS] Test process terminated successfully.
[INFO] Day22 test completed.

從結果可以看到:

  1. Python 成功建立 PID 為 2538718 的測試程序。
  2. Response Engine 通過授權參數及測試指令檢查。
  3. 程式呼叫 terminate() 發送 SIGTERM。
  4. wait() 成功等待到測試程序結束。
  5. 最後顯示 [PASS],確認本次受控程序終止測試成功。

這次與 Day21 的最大差別是:

Day21:

Mode       : DRY RUN
Executed   : False

Day22:

Mode       : CONTROLLED TEST
Executed   : True
Result     : Process exited

因此,這次不再只是印出終止程序的建議,而是實際透過 Python 對 Linux 子程序執行終止操作。


六、目前功能的限制

雖然今天成功終止測試程序,但目前的功能還存在幾個限制。

第一:尚未自動終止偵測到的可疑程序。

今天使用的是 test_day22.py 建立的 sleep 60,不是由 Detection Engine 發現惡意程序後自動觸發終止。

第二:授權機制仍然很簡單。

目前的 authorized=True 只是函式參數,還沒有加入真正的身分驗證、白名單與核准流程。

第三:尚未完成完整的程序身分確認。

雖然我們檢查了 Popen 物件和測試指令,但還沒有完整驗證執行檔路徑、程序建立時間等資訊。

第四:尚未建立 Response Audit Log。

目前終止結果只顯示在 Terminal,還沒有另外保存每次處置的時間、對象、動作及結果。

因此,今天完成的是受控測試程序終止功能,還不是能安全地自動處置任意可疑程序的 EDR Response 系統。


七、今日心得

Day22 是我們第一次真正讓 BehaviorGuard 對 Linux 程序執行終止操作。

在前面的實作中,Response Engine 主要依照 Severity 產生建議,實際上並沒有改變系統狀態。

今天透過 Python 的 subprocess 模組,建立了一個可以管理的測試子程序,並利用 SIGTERM 要求它結束,再使用 wait() 確認執行結果。

這次也讓我更清楚了解,程序終止不是單純傳送一個訊號就結束,還需要考慮程序是否仍然存在、能否正常退出,以及測試失敗時如何清理。

另外,我認為真正的 Response 功能必須比 Detection 更謹慎。

因為偵測規則可能產生誤判,如果沒有任何限制就自動終止程序,可能影響正常的系統服務。

目前我們只允許受控的 sleep 60 測試程序進入終止流程,因此還需要進一步改善安全性。

下一篇 Day23,我們預計開始建立 Response Policy(回應安全策略),加入更完整的程序檢查與限制條件,為之後將 Detection 與真正的 Process Termination 串接做準備。


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

尚未有邦友留言

立即登入留言