Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2015-2291 — 适用于 Windows 的 Intel 以太网诊断驱动程序中的 (1) IQVW32.sys(1.3.1.0 之前版本)和 (2) IQVW64.sys(1.3.1.0 之前版本)允许本地用户通过特制的 (a) 0x80862013、(b) 0x8086200B、(c) 0x8086200F 或 (d) 0x80862007 IOCTL 调用导致拒绝服务,或可能以内核权限执行任意代码。 | Kitploit
工具/GitHubGitHub/gmh5225/cve-2015-2291
权限提升漏洞分析漏洞利用论文与研究学习与教育二进制利用
GitHubgmh5225/cve-2015-2291

CVE-2015-2291

适用于 Windows 的 Intel 以太网诊断驱动程序中的 (1) IQVW32.sys(1.3.1.0 之前版本)和 (2) IQVW64.sys(1.3.1.0 之前版本)允许本地用户通过特制的 (a) 0x80862013、(b) 0x8086200B、(c) 0x8086200F 或 (d) 0x80862007 IOCTL 调用导致拒绝服务,或可能以内核权限执行任意代码。

查看仓库
5574年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2015-2291

(1) 版本低于 1.3.1.0 的 IQVW32.sys 和 (2) 版本低于 1.3.1.0 的 IQVW64.sys(位于 Windows 版 Intel 以太网诊断驱动程序中)允许本地用户通过特制的 (a) 0x80862013、(b) 0x8086200B、(c) 0x8086200F 或 (d) 0x80862007 IOCTL 调用导致拒绝服务,或可能以内核权限执行任意代码。

概述

本仓库包含针对该漏洞的分析文章,以及可在 64 位 Windows 7 SP1 和 Windows 10 20H2 上运行的概念验证(PoC)漏洞利用代码。驱动程序文件位于 Driver Files 目录中。如果你在文章中发现了任何拼写错误,或者希望某些细节有更详细的描述,请在该仓库上创建一个 issue!我会尽快修复。

动机

专门为这个设备驱动程序编写漏洞利用的动机,仅仅是因为它目前正在野外被滥用来加载攻击者的未签名 rootkit。恶意软件通过使用 BYOVD(自带易受攻击驱动程序)方法,可以检查自己是否以提升的权限运行,放置一份易受攻击设备驱动程序的副本,加载该驱动程序,然后利用它获得内核代码执行能力以加载 rootkit。我未能成功对恶意软件样本进行逆向工程,因此我决定自己动手创建这个漏洞利用程序。

在野外发现的样本: https://bazaar.abuse.ch/sample/84ed7fec67de5621806dbb43af5167a5fc60ab7f2403448519dc0eca2b8f9022/ https://bazaar.abuse.ch/sample/0925b8985b19d7925d68186d666b0050a4cb3f2a577d64765d770a57a2eab9ae/ https://bazaar.abuse.ch/sample/e8b7f42d544fe8b954c4021315cff2fdd44d67d11704009cdf3037d34e0c0a93/

CVE-2015-2291 - 漏洞利用的技术分析

该设备驱动程序,即 iqvw64e.sys,是一款用于执行网络适配器诊断的驱动程序。它通过暴露几个 IO 控制码(也称为 IOCTL),允许用户态组件与设备驱动程序交互以执行大量内核例程,并且在交互过程中,用户输入缓冲区中会提供一个“子”IO 控制码。用于触发易受攻击代码路径的 IO 控制码是 0x80862007。除了主控制码之外,本分析还将涉及上述“子”IO 控制码:用于触发 memmove 函数调用的 0x33 代码,以及用于触发 memset 函数调用代码路径的 0x30 代码。本文不会涉及 DriverEntry 例程的任何细节,因为 Microsoft 文档页面上已有足够的资料,可以为你提供透彻的解释。

首先,我们要弄清楚如何与该设备驱动程序进行交互。与设备驱动程序通信的最常见方式是通过名为 DeviceIoControl 的函数。该函数的基本思想是:我们可以传入由 CreateFileA 创建的有效驱动程序句柄,传入一个与我们想要的内核例程对应的 IO 控制码,传入一个它所期望的结构(或缓冲区),然后它会在我们的输出缓冲区中返回数据。虽然这类例程有时可能是必要的(例如为了超频而访问模型特定寄存器),但它们也会对安全构成严重风险。但是……怎么构成的呢?

就 CVE-2015-2291 而言,该漏洞可由非特权用户触发。由于不存在任何清理检查,并且利用该漏洞 不 需要管理员权限,因此存在安全风险。这两个缺陷的根源在于,攻击者能够完全控制 IO 控制码接口暴露的 memset 和 memmove 函数调用。还记得之前提到的 DeviceIoControl 函数吗?我们能够传入一个将在内核例程中使用的结构。所有环节就是这样串联起来的。

让我们退一步来看。首先,我们需要获取与易受攻击设备驱动程序相关的驱动程序句柄。但在此之前,我们需要找到对应的命名设备对象。这些对象通过符号链接(通常是硬编码的)暴露给用户空间,可以使用 SysInternals 套件中的 WinObj 找到它们。虽然我们可以使用字符串转储工具来转储符号链接,或者对设备驱动程序进行逆向工程,但我只是加载了设备驱动程序,然后用 WinObj 找到了它。找到的与该设备驱动程序相关的符号链接是 \\.\GLOBALROOT\Device\Nal。要获取驱动程序句柄,我们需要调用 CreateFileA 函数,让它返回一个有效的驱动程序句柄,以便我们稍后在该过程中使用。此过程的代码如下:```C if (h_nal == (HANDLE)-1) { printf("\n[-] Unable to obtain a driver handle to the Nal device driver. Error: %d (0x%x)", GetLastError(), GetLastError()); unused = getchar(); return 1; } printf("\n[+] Obtained a driver handle to the Nal device driver. Handle Value: 0x%p", h_nal);

在后续的利用过程中,我们将使用驱动程序句柄。现在,我们将开始准备我们的漏洞利用程序。下一步是使用 [LoadLibraryA](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya) 函数加载 `ntdll.dll` 库,以返回一个 [模块句柄](https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types),这样我们就可以动态定位我们需要的函数。尽管 `ntdll.dll` 库可能已经加载到我们的进程中,但我们仍然需要获得一个可用的库句柄。我们用于漏洞利用的函数包括 [NtQuerySystemInformation](https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation)(用于在后续利用过程中泄漏 NT 内核的基址(具有中等进程完整性))和 [NtQueryIntervalProfile](http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FProfile%2FNtQueryIntervalProfile.html)(用于触发漏洞)。至于加载 `ntdll.dll` 库的代码,如下所示:```C
h_ntdll = LoadLibraryA("C:\\Windows\\System32\\ntdll.dll");
if (!h_ntdll)
{
	printf("\n[-] Failed to load the \"ntdll.dll\" API library. Error: %d (0x%x)", GetLastError(), GetLastError());
	unused = getchar();
	return 0;
}
printf("\n[+] Loaded the \"ntdll.dll\" API library. Handle Value: 0x%p", h_ntdll);

现在我们已经获得了库的句柄,接下来将从定位 NtQueryIntervalProfile 函数开始。首先,由于该函数未公开文档化,我们需要为此函数定义一个类型。虽然你可以在网上找到该类型定义,但为了便于访问,我在这里已经提供了它:```C typedef unsigned int(__stdcall* NtQueryIntervalProfile)( unsigned int ProfileSource, PULONG Interval );

要使用这个函数,我们还需要使用 `NtQueryIntervalProfile` 类型声明一个变量(局部或全局,由你决定)。现在,我们如何把这个变量变成实际可用的函数?为此,我们将使用名为 [GetProcAddress](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress) 的函数。通过传入我们要搜索的模块的句柄(第一个参数)并传入函数名称(第二个参数),我们就能在模块中找到任何我们想要的函数,并获得指向该函数的指针!提供了代码来帮助你处理这些信息。```C
_NtQueryIntervalProfile = (NtQueryIntervalProfile)GetProcAddress(h_ntdll, "NtQueryIntervalProfile");
if (!_NtQueryIntervalProfile)
{
	printf("\n[-] Failed to locate the \"NtQueryIntervalProfile\" function. Error: %d (0x%x)", GetLastError(), GetLastError());
	unused = getchar();
	return 1;
}
printf("\n[+] Located the \"NtQueryIntervalProfile\" function. Function Address: 0x%p", _NtQueryIntervalProfile);

动态加载函数以及使用它们的能力之所以有效,是因为函数本身是指向可执行代码的指针。函数的实际主体是将要执行的代码。

现在我们已经解析了 NtQueryIntervalProfile 函数指针,但仍然需要获取 NtQuerySystemInformation 函数的地址。与此前一样,我们需要这个函数的类型定义,并且还需要声明一个变量来调用该函数。同样如前所述,为了方便访问,我提供了类型定义。```C typedef NTSTATUS(WINAPI* NtQuerySystemInformation)( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength );

同样地,和之前一样,我们需要定位该函数。之前对 `GetProcAddress` 的调用与这次调用之间的唯一区别在于我们正在查找的函数。我们可以复制该函数并更改第二个参数,以查找我们的第二个函数。代码编写完成后,我们应得到类似如下的内容:```C
_NtQuerySystemInformation = (NtQuerySystemInformation)GetProcAddress(h_ntdll, "NtQuerySystemInformation");
if (!_NtQuerySystemInformation)
{
	printf("\n[-] Failed to locate the \"NtQuerySystemInformation\" function. Error: %d (0x%x)", GetLastError(), GetLastError());
	unused = getchar();
	return 0;
}
printf("\n[+] Located the \"NtQuerySystemInformation\" function. Function Address: 0x%p", _NtQuerySystemInformation);

完美!我们已经找到了所有需要的缺失函数。现在,我们需要泄露 NT 内核基址。借助 NtQuerySystemInformation,我们可以创建一个查询,返回当前加载的所有设备驱动程序的基址和其他信息。NtQuerySystemInformation 函数的第一个参数是一个枚举,具体来说是一个未公开文档化的枚举。该枚举是 SystemModuleInformation,其对应的值为 0xB。然后,我们需要传入一个指向其中一个返回结构的指针。所需的结构和枚举如下所示,由 FuzzySecurity (@b33f) 提供:```C typedef enum _SYSTEM_INFORMATION_CLASS { SystemModuleInformation = 0xB, } SYSTEM_INFORMATION_CLASS;

typedef struct SYSTEM_MODULE { ULONG Reserved1; ULONG Reserved2; ULONG Reserved3; PVOID ImageBaseAddress; ULONG ImageSize; ULONG Flags; WORD Id; WORD Rank; WORD LoadCount; WORD NameOffset; CHAR Name[256]; } SYSTEM_MODULE, * PSYSTEM_MODULE;

下载工具