ACL / Security Descriptor Manipulation
Token swapping replaces a process's identity. PreviousMode manipulation removes the user/kernel boundary. ACL and security descriptor manipulation takes a different approach entirely: instead of changing who the attacker is, it changes what the attacker is allowed to access. Every kernel object in Windows, whether process, file, registry key, or system API, has a security descriptor that determines which principals can interact with it and how. Corrupting a single security descriptor can open access to an entire class of privileged operations without modifying any token or thread state.
The technique is particularly powerful when applied to global security descriptors. Windows stores several singleton security descriptors in kernel memory that gate access to broad categories of operations. Corrupting one of these global descriptors affects every subsequent access check that references it, rather than requiring per-object corruption. A single write to SepMediumDaclSd, for example, can re-enable information classes in NtQuerySystemInformation that Microsoft restricted to Medium integrity and above, providing a KASLR bypass from a sandboxed or Low integrity context.
SepMediumDaclSd corruption
SepMediumDaclSd is a global security descriptor in ntoskrnl.exe that gates access to sensitive NtQuerySystemInformation information classes. Starting with Windows 10 20H1, Microsoft restricted classes like SystemModuleInformation and SystemExtendedHandleInformation to Medium integrity and above by adding a DACL check against this descriptor. This closed a longstanding information leak path that sandbox escapes and Low integrity exploits relied on for KASLR bypass.
With a bit-manipulation primitive (such as RtlClearAllBits redirected through a UAF callback), zeroing the DACL within SepMediumDaclSd produces a NULL DACL. In Windows security semantics, a NULL DACL is distinct from an empty DACL. An empty DACL (one with zero ACEs) denies all access. A NULL DACL grants all access to everyone. By zeroing the entire ACL structure, the corruption produces a NULL DACL, and Low integrity or sandboxed processes can once again query kernel module base addresses and handle object addresses.
SepMediumDaclSd (kernel global)
SECURITY_DESCRIPTOR
Dacl -> ACL structure
AceCount, AclSize, ACE entries
After RtlClearAllBits(&fakeBitmap):
DACL bytes zeroed -> interpreted as NULL DACL -> everyone has full access
CVE-2026-21241 used this technique as the first step of a two-part KASLR bypass. The exploit triggered a UAF in afd.sys, reclaimed the freed slot with a pool spray, and redirected the UAF callback to RtlClearAllBits with a fake RTL_BITMAP pointing at SepMediumDaclSd. After the DACL was zeroed, the exploit called NtQuerySystemInformation(SystemExtendedHandleInformation) from Low integrity to enumerate kernel object addresses, then proceeded with WIL feature flag manipulation for a secondary KASLR bypass.
SepMediumDaclSd Control field bit-flip
A lighter variant targets the Control field in the SECURITY_DESCRIPTOR rather than the DACL itself. The Control field is a 16-bit value containing flags like SE_DACL_PRESENT (0x04) and SE_SACL_PRESENT (0x10) that determine how Windows evaluates the descriptor. Clearing SE_SACL_PRESENT tricks SeAccessCheck into skipping Mandatory Integrity Control (MIC) validation, which is the check that prevents Low integrity processes from accessing Medium-or-higher integrity objects.
This approach has two advantages over full DACL zeroing. First, it leaves the DACL structurally intact, which means forensic analysis of the security descriptor does not reveal an obviously corrupted or NULL DACL. Second, it only requires a single bit-flip rather than zeroing an entire ACL structure, making it achievable with a smaller or more constrained write primitive.
The StarLabs Chrome sandbox escape demonstrated this technique. A partial write from CVE-2024-30088's TOCTOU race zeroed the lower byte of SepMediumDaclSd.Control, clearing SE_SACL_PRESENT and letting an Untrusted IL Chrome renderer call NtQuerySystemInformation(SystemExtendedHandleInformation) for kernel object address enumeration. The integrity check was bypassed without modifying the DACL at all.
Direct DACL overwrite
With a full arbitrary write primitive (from I/O Ring, pipe attributes, or PreviousMode manipulation), the attacker has more options for DACL manipulation.
NULL-out the DACL pointer. Setting the Dacl field in a SECURITY_DESCRIPTOR to NULL (a single 8-byte write on x64) grants everyone full access to whatever object or API the descriptor protects. This is the simplest DACL corruption and requires only one write.
Add a GENERIC_ALL ACE. Appending an ACE that grants GENERIC_ALL to the attacker's SID gives the attacker full access while preserving existing ACEs for other principals. This is more surgical than a NULL DACL but requires constructing a valid ACE structure in kernel memory.
Modify the mandatory integrity label. The SACL in a security descriptor may contain a mandatory label ACE that specifies the object's integrity level. Lowering or removing this label bypasses MIC without touching the DACL. This is useful when the attacker needs to access a specific object (like a SYSTEM process handle) rather than bypassing a global check.
Process security descriptor corruption
Each process has a security descriptor that controls who can open handles to it. By default, SYSTEM processes have restrictive DACLs that prevent standard users from obtaining PROCESS_ALL_ACCESS handles. Setting a NULL DACL on a SYSTEM process's security descriptor lets any process call OpenProcess with full access, then duplicate the process's token for privilege escalation.
This approach achieves privilege escalation without modifying any token or PreviousMode field. The attacker does not become SYSTEM. Instead, the attacker gains the ability to interact with SYSTEM processes as if they had SYSTEM privileges. The practical result is the same: the attacker can duplicate a SYSTEM token, inject code into a SYSTEM process, or read SYSTEM process memory.
Key kernel structures
typedef struct _SECURITY_DESCRIPTOR {
BYTE Revision;
BYTE Sbz1;
SECURITY_DESCRIPTOR_CONTROL Control; // SE_DACL_PRESENT, SE_DACL_DEFAULTED, etc.
PSID Owner;
PSID Group;
PACL Sacl;
PACL Dacl; // primary target for corruption
} SECURITY_DESCRIPTOR;
typedef struct _ACL {
BYTE AclRevision;
BYTE Sbz1;
WORD AclSize;
WORD AceCount; // zeroing AceCount = empty DACL (deny all)
WORD Sbz2; // zeroing entire ACL = NULL DACL (allow all)
} ACL;
The distinction between an empty DACL and a NULL DACL is critical for exploitation. An empty DACL (AceCount = 0 but DACL pointer is valid) denies all access because there are no ACEs granting permission. A NULL DACL (DACL pointer = NULL or SE_DACL_PRESENT not set) grants all access because the absence of a DACL is interpreted as "no restrictions." Zeroing the entire ACL structure (as RtlClearAllBits does) sets the DACL pointer itself to zero, producing a NULL DACL.
Related CVEs
| CVE | Driver | Description |
|---|---|---|
| CVE-2026-21241 | afd.sys |
RtlClearAllBits used to zero SepMediumDaclSd DACL, bypassing NtQuerySystemInformation restrictions |
| CVE-2024-30088 | ntoskrnl.exe |
TOCTOU partial write used to clear SE_SACL_PRESENT in SepMediumDaclSd Control field (StarLabs Chrome sandbox escape) |
| CVE-2024-26229 | csc.sys |
Missing access check allows unauthorized access to privileged objects |
AutoPiff Detection
privilege_check_added-- Detects patches that add privilege or access checkshandle_force_access_check_added-- Detects addition ofOBJ_FORCE_ACCESS_CHECKin handle operationsadded_access_check-- Detects addition ofSeAccessCheckorSeFastTraverseCheckcalls
Mitigations
Kernel Data Protection (KDP) provides the most promising defense against security descriptor corruption. VBS-backed read-only protection for global security descriptors like SepMediumDaclSd would prevent direct memory writes from modifying them. Microsoft has not yet applied KDP to all sensitive global descriptors, but the infrastructure exists.
Secure Pool allocates security descriptors in VBS-protected pool regions, preventing direct memory corruption. When applied, even a full arbitrary write primitive cannot modify the descriptor.
Integrity level enforcement provides defense-in-depth. Even with a NULL DACL, mandatory integrity checks (MIC) provide a secondary barrier: a Low integrity process cannot write to a Medium integrity object even if the DACL grants access, unless the integrity label is also corrupted. However, as the SepMediumDaclSd Control field bit-flip demonstrates, MIC can be bypassed through the same security descriptor corruption mechanisms.
See Also
- Bit-Manipulation Primitives --
RtlClearAllBitsas the mechanism for zeroing DACLs - Token Manipulation -- alternative privilege escalation via token corruption
- KASLR --
SepMediumDaclSdcorruption is a KASLR bypass technique - PreviousMode Manipulation -- another boundary-bypass technique