Pixiv - KiraraShss
2531 words
13 minutes
Windows Kernel Exploitation筆記
使用工具
- VM: Windows 11 25H2 Pro (Target Machine)
- Visual Studio 2022 (用來寫WDK)
- Process Monitor
- Process Explorer
- Windbg + KDNet
基礎知識
Process
_EPROCESS: Kernel 中代表 Process 的主要結構- PCB (
_KPROCESS): 核心 Process 控制區塊, 負責排程、中斷…DirectoryTableBase(CR3):指向 PML4 (Kernel mode)UserDirectoryTableBase:指向 User mode 的 PML4 (用於 KVAS/KPTI)
UniqueProcessId(PID)ActiveProcessLinks:雙向鏈結串列, 串接系統中所有 Process, Exploit 常用目標Token:指向 Security ContextPEB:指向 User space 的 Process Environment BlockThreadListHead:串接該 Process 下所有 Thread
- PCB (
Thread
_ETHREAD:Kernel 中代表 Thread 的主要結構- TCB (
_KTHREAD):Thread 控制區塊KernelStack:Context switch 時紀錄當前 StackTrapFrame:紀錄進入 Kernel mode 時的暫存器狀態Teb:指向 User space 的 Thread Environment BlockPreviousMode:0 為 KernelMode, 1 為 UserMode. KernelMode 下會略過大部分權限檢查
- TCB (
Security Machanism
-
Integrity Level
- 分為 Untrusted, Low, Medium, High, System, Protected
- Mandatory Policy
- No-Write-Up: 低權限無法寫入高權限 Object
- No-Read-Up: 低權限無法讀取高權限 Process 的 Memory
- No-Execute-Up: 低權限無法叫高權限 Process 使用 COM
-
存取控制清單 (ACL)
- DACL:決定誰可以存取物件, 若為 Null則大家都能存取, 若為 Empty則反之
- SACL:決定哪些操作需要被紀錄 (Audit)
-
安全識別碼 (SID)
- 唯一識別 User 或 Group, 包含 Revision, IdentifierAuthority, Subauthority, RID
- RID 500 為 Admin, 501 為 Guest
-
特權 (Privileges)
- 只有 Local machine 有效
- Super Privileges
SeDebugPrivilege:可 Open 任意 ProcessSeRestorePrivilege:可覆蓋任意檔案SeTcbPrivilege:可偽裝任意使用者SeLoadDriverPrivilege:可載入 Driver (獲得 Kernel code 執行權限)SeCreateTokenPrivilege:可建立任意 TokenSeTakeOwnershipPrivilege:可修改 Object owner (進而修改 DACL)
-
存取權杖 (Access Token)
- 包含 SID, Group, Privileges, Integrity Level
- Primary Token vs Impersonation Token (模擬其他 Security Context)
- 結構 (_TOKEN):包含
UserAndGroups(SID 陣列),Privileges(Present/Enabled),IntegrityLevelIndex
-
Access Check流程
- Mandatory Integrity Check (MIC):優先檢查 Integrity Level
- DACL Check:若無 DACL 則全權限; 若有, 則依序檢查 ACE
- 遇到 Deny 則直接拒絕
- 若檢查完所有 ACE 仍無 Match, 則拒絕
Memory Management
- Control Register
- CR2:Page Fault 時的 Virtual Address
- CR3:存放 PML4 的實體位址 (Page Table Base)
- CR4:控制 Protected mode 操作 (如 SMEP/SMAP 相關)
- CR8:當前 IRQL
- Paging
- 位址轉換:CPU 透過 MMU 將 Virtual Address (VA) 轉為 Physical Address (PA)
- Page 狀態:Committed (已分配), Shareable (共享), Free, Reserved
- Copy-On-Write(CoW):寫入時才複製 Page, 節省記憶體
- Page Table
- 4-Level Paging (x64):PML4 -> PDPT -> PD -> PT -> Physical Page
- PTE (Page Table Entry):包含 PFN (Page Frame Number) 和屬性位元 (NX, R/W, U/S, Valid)
- Valid (V):0 代表 Page out 或 Invalid
- U/S:User/Supervisor 權限位
- NX:No-Execute
- Self-ref entry:PML4 中指向自己的 Entry,用於定位 Page Table 的 VA (Win10 1607 後位置隨機化)
- Exploit 技巧:修改 PTE (如清除 NX bit) 使記憶體可執行。
_KUSER_SHARED_DATA(0xfffff78000000000) 是一個好目標
- Page Fault
- Soft Page Fault:Page 還在物理記憶體中
- Hard Page Fault:需從 Disk (Page file) 讀回
- Virtual Address Descriptors (VADs)
- 管理 User Space 的虛擬記憶體狀態 (Reserved/Committed),紀錄於 AVL Tree (
_MMVAD)
- 管理 User Space 的虛擬記憶體狀態 (Reserved/Committed),紀錄於 AVL Tree (
- PFN Database
- 紀錄每個 Physical Page 的狀態 (
_MMPFN),如 Active, Free, Modified, Bad 等
- 紀錄每個 Physical Page 的狀態 (
中斷與排程 (IRQL & DPC)
- Interrupt Request Levels (IRQL)
- Passive Level (0):User mode 運行於此
- APC Level (1):APC 調用
- Dispatch/DPC Level (2):Kernel scheduler, Page fault handler. 此層級以上不可發生 Page fault (僅能存取 Non-paged memory)
- 規則:低 IRQL 不能中斷高 IRQL. Exploit 時若 IRQL 過高需用
KeLowerIrql降低
- Deferred Procedure Calls (DPC)
- 目的:讓高優先級的中斷服務程式 (ISR) 能將較不緊急的任務延後執行,避免長時間佔用 CPU 高 IRQL
- 執行時機:當 IRQL 降回 Dispatch Level 時處理 DPC Queue
- 種類
- Normal DPC:跑在 Dispatch Level
- Threaded DPC:跑在 Passive Level,由專屬 Thread 執行
Windows Driver
常見類型
- WDM (Windows Driver Model):最傳統且常見的模型, 包含 Bus、Function、Filter driver
- KMDF (Kernel-Mode Driver Framework):微軟提供的Driver框架, 封裝底層操作以減少錯誤
- Mini-Filter Drivers:用於攔截 File System 操作 (如防毒軟體)
- Win32k / GDI:GUI 子系統, 雖然漏洞多但極其複雜
Driver 與 User Mode的互動方式
- Driver 載入時建立 Symbolic Link (如
\\.\BitLocker) 供 User Application 溝通 - 開發者透過
CreateFile取得 Handle,並使用DeviceIoControl發送 I/O Request - I/O Request Packet (IRP)
- I/O Manager 將 User 的請求封裝成 IRP 送給 Driver
- Driver 處理完後呼叫
IoCompleteRequest完成操作 - 關鍵結構
- Memory Descriptor List (MDL):描述記憶體區塊
_IO_STACK_LOCATION:記錄每一層 Driver 的操作 (Major Function, Parameters 等)
資料存取方式
- Buffered I/O (
METHOD_BUFFERED)- 系統分配一塊 Non-paged pool (SystemBuffer)
- 進入時將 User Input 複製到 SystemBuffer
- 完成時將 SystemBuffer 內容複製回 User Output Buffer
- 優點:安全,隔離了 User 記憶體
- Direct I/O (
METHOD_IN/OUT_DIRECT)- I/O Manager 建立 MDL 鎖定 User Buffer 的實體頁面
- Driver 透過 MDL 取得 Kernel Virtual Address 進行存取
- 特性:Buffer 被鎖在記憶體中 (Locked), 不會被 Page Out
- Neither Buffered Nor Direct I/O (
METHOD_NEITHER)- 直接使用 User Space 的指標 (
Type3InputBuffer,UserBuffer) - 風險:必須極度小心, 存取前需驗證指標是否位於 User Space (
ProbeForRead/Write), 且只能在 User Thread 的 Context 下使用
- 直接使用 User Space 的指標 (
Bug Classes
記憶體相關漏洞
- Buffer Overflow:Stack 或 Heap 溢位
- Integer Overflow/Underflow:常見於記憶體分配大小的計算, 導致後續的 Heap Overflow 或 OOB
- Use After Free (UAF)
- 釋放指標後未設為 NULL,形成 dangling pointer
ObDereferenceObjectIssue:物件計數歸零後未清空指標
- Double Free:重複釋放記憶體,或將
Irp->MdlAddress設為 NULL 導致系統重複釋放 - Out-of-Bounds (OOB)
Irp->IoStatus.Information:在 Buffered I/O 中, 若此值大於 SystemBuffer 大小, 複製回 User Buffer 時會導致越界讀寫
- Uninitialized Variable:分配記憶體 (如
ExAllocatePool2帶POOL_FLAG_UNINITIALIZED) 後未清空即使用, 可能導致資訊洩漏
指標驗證問題
- User 傳入的指標若未經驗證 (Probe), 攻擊者可傳入 Kernel Address 造成任意讀寫
- MmProbeAndLockPages:需注意
AccessMode是否正確設為UserMode,否則可能鎖定 Kernel Memory
競爭條件 (Race Condition / Double Fetch)
- Double Fetch
- Kernel 從 User 記憶體讀取第一次數值做檢查 (Check)
- 再次讀取數值進行操作 (Use)
- 攻擊:User 在兩次讀取之間修改數值 (TOCTOU),繞過檢查
- Reference Count Issue:多執行緒下對全域物件的操作未加鎖或未使用 Interlocked API
邏輯漏洞
- Access Mode Mismatch
PreviousMode與RequestorMode的檢查繞過ZwOpenFile等 Kernel API 預設為KernelMode,若未正確轉發 User 權限可能導致繞過 ACL
- CreateFile Issue
- 使用
IO_NO_PARAMETER_CHECKING或OBJ_KERNEL_HANDLE可能繞過安全檢查 - 未正確設置 Security Descriptor (DACL)
- 使用
類型混淆
ObReferenceObjectByHandle未指定物件型態, 導致將錯誤的物件當作預期物件處理 (例如將 Event Handle 當作 File Handle)
安全機制與測試工具
- ACL
IoCreateDeviceSecure可指定 SDDL 設定權限- 常見錯誤:忘記設
FILE_DEVICE_SECURE_OPEN, 導致 Namespace 下的子路徑權限未繼承
- Special Pool
- 分配記憶體時夾在無效頁面 (Unmapped pages) 之間
- 一旦發生 Overflow 或 UAF 存取,立即觸發 Crash (BSOD),方便偵錯
Kernel Exploitation
Protection
- Kernel Address Space Layout Randomization (KASLR)
- 每次開機時核心記憶體位置隨機化, 不重開機位置不變
- 需要資訊洩漏(Info Leak)來繞過
- Supervisor Mode Execution Protection (SMEP)
- 禁止 Kernel Mode 直接執行 User Space 的程式碼
- 由
CR4暫存器的第 20 bit 控制
- Supervisor Mode Access Prevention (SMAP)
- 禁止 Kernel Mode 直接存取(讀寫)User Space 的資料
- 由
CR4的第 21 bit 和EFLAG.AC決定。呼叫 DPC 時通常會啟用
- Kernel Virtual Address Shadow (KVA Shadow)
- 針對 Meltdown 漏洞的緩解措施,實現了 Kernel/User Page Table 的隔離
- 在 Kernel Mode 下, User Space 的 PML4 NX (No-Execute) bit 會被設為 1, 這導致即使關閉 SMEP, 也無法直接在 Kernel Mode 執行 User Space 的 Shellcode
- Patch Guard (Kernel Patch Protection)
- 定期檢查關鍵結構(如
SSDT,GDT,IDT,CR4)是否被竄改 - 若偵測到修改,會觸發
CRITICAL_STRUCTURE_CORRUPTION (0x109)藍屏
- 定期檢查關鍵結構(如
- Kernel Control Flow Guard (kCFG)
- 防止間接跳轉(Indirect Jump)被竄改,驗證目標位址是否有效
Information Leak
- NtQuerySystemInformation
- 在 Medium Integrity Level 下, 可用
SystemModuleInformation或SystemExtendedHandleInformation獲取核心模組或物件位址 - 限制: 從 Windows 11 24H2 開始, 除非擁有
SeDebugPrivilege, 否則此方法失效
- 在 Medium Integrity Level 下, 可用
- 漏洞利用: 在 Low Integrity 或新版 Windows 下, 通常需要依賴驅動程式本身的 Info Leak 漏洞
利用現代 Stack Overflow (Ret2usr)
繞過 SMEP/SMAP
- 利用 ROP (Return Oriented Programming) 修改
CR4暫存器, 清除第 20 (SMEP) 和 21 (SMAP) bit - 必須保持
CR4其他 bits 不變
繞過 KVA Shadow (核心難點)
由於 KVA Shadow 會將 User Space 標記為不可執行(NX), 直接跳轉會導致 ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY (0xfc)
- 方法一:利用任意讀寫 (Arbitrary R/W)
- 計算目標 User Space 位址對應的 Page Table Entry (PTE) 位址
- 利用
nt!MmPteBase計算 PTE 虛擬位址 - 將該 PTE 的 NX bit 清0, 使其變為可執行
size_t PTEBASE = 0xffffcd8000000000;size_t getpte(size_t addr){ size_t result = 0 ; result = addr >> 9 ; result = result & 0x7FFFFFFFF8; result = PTEBASE + result; return result;}- 方法二:利用 ROP
- 若只有 Stack Overflow, 可用 ROP 呼叫
nt!MiGetPteAddress取得 PTE 位址, 再修改其內容以清除 NX bit
- 若只有 Stack Overflow, 可用 ROP 呼叫
- 恢復環境: Shellcode 執行完畢返回 User Mode 前, 必須將 PTE 和
CR4改回原狀, 否則會被 Patch Guard 抓到或導致崩潰
Code Execution to EoP
Token Stealing
- 原理: 將當前 Process 的 Token 替換為 System Process (PID 4) 的 Token
- 關鍵結構鏈 (GS Segment)
GS:[0x188]->_KTHREAD(Current Thread)_KTHREAD->Process(offset 0x220 等, 隨版本變動) ->_EPROCESS(Current Process)- 從
_EPROCESS透過ActiveProcessLinks遍歷所有 Process - 檢查
UniqueProcessId是否為 4 (System PID) - 找到後, 將 System 的
Token複製到當前 Process 的Token欄位
安全返回 User Mode (處理 TrapFrame 與 APC)
執行完 Shellcode 後需安全返回, 避免 BSOD
- TrapFrame 設置
- 使用
KiSystemServiceExit返回 User Mode - 需將 RBP 指向合法的 TrapFrame
- 在 20H2 後,RBP 實際上是指向
TrapFrame + 0x80的位置,設置時需修正 offset
- 使用
- 解決 APC_INDEX_MISMATCH (0x01)
- 因為
DeviceIoControl會呼叫KeEnterCriticalRegion(禁用 APC), 若返回時Thread->KernelApcDisable不為 0 會崩潰 - 解法: 在 Shellcode 中手動將
KernelApcDisable歸零, 或呼叫KeLeaveCriticalRegion
- 因為
提高 Exploit 穩定性
- 修復 I/O 卡死: 呼叫
IoCompleteRequest(Irp, 0)讓 I/O 請求完成, 否則 Process 無法結束. IRP 可從Thread->IrpList找到 - 釋放鎖 (Lock): 呼叫
IopReleaseFileObjectLock(FileObject), 否則 Process 結束時會因持有 Lock 而崩潰
Arbitrary R/W to EoP
若只有任意讀寫能力,無需執行 Shellcode 也能提權
Enabling Privileges
- 定位到當前 Process 的
_EPROCESS -> Token - 修改
Token中的_SEP_TOKEN_PRIVILEGES結構 - 將 Present 和 Enabled 欄位寫入
0xffffffffffffffff(全開) - 這會給予
SeDebugPrivilege, 允許注入程式碼到 System Process (如winlogon.exe)
修改 PreviousMode (The “Easy Way”)
- 原理:
_ETHREAD有一個PreviousMode欄位, 0 代表 Kernel Mode, 1 代表 User Mode - 攻擊: 利用任意寫入將
PreviousMode改為 0 - 效果: 系統會認為請求來自 Kernel, 從而繞過大多數 API 的權限檢查 (Access Check). 攻擊者可以直接對 System Process 呼叫
OpenProcess或DuplicateToken而不受 ACL 限制
Windows Kernel Exploitation筆記
https://blog.cyberangel.work/posts/winkrnl/