Logic Bugs
Flaws in program logic that violate security assumptions without a traditional memory corruption root cause, enabling privilege escalation or security bypass.
Description
Logic bugs are vulnerabilities rooted in incorrect security design or implementation rather than memory safety violations. They encompass missing authorization checks, incorrect privilege enforcement, flawed state machine transitions, security policy bypasses, and improper error handling that fails open. Unlike buffer overflows or use-after-free bugs, logic bugs do not corrupt memory -- instead, they allow an attacker to perform operations that should be denied by the driver's security model.
These vulnerabilities are particularly dangerous because they are often missed by memory-safety tooling. Static analyzers focused on buffer sizes, fuzzers looking for crashes, and runtime sanitizers designed to catch memory corruption will all fail to detect a missing SeAccessCheck call or an incorrect PreviousMode usage. Logic bugs require human understanding of the driver's intended security model and manual verification that every security-relevant operation is properly gated. This makes them among the hardest vulnerability classes to detect systematically.
A common and severe subclass involves PreviousMode / RequestorMode misuse. When a kernel driver processes a request from user mode, it should check Irp->RequestorMode or ExGetPreviousMode() to determine whether the caller has kernel-mode privileges. If a driver incorrectly hardcodes KernelMode or uses the wrong accessor, it bypasses all user/kernel boundary checks, treating an unprivileged user-mode request as if it came from kernel mode. Similarly, drivers that call ObReferenceObjectByHandle without OBJ_FORCE_ACCESS_CHECK allow user-mode callers to open handles to objects they should not have access to.
Another important subclass is device object security. Every device object created by IoCreateDevice or IoCreateDeviceSecure has a security descriptor that controls who can open a handle to it. If the security descriptor is overly permissive (e.g., allows Everyone full access), any user can open the device and send IOCTLs. When combined with an IOCTL that performs a privileged operation, this becomes a direct privilege escalation path. Many third-party drivers create their device objects with no explicit security descriptor, inheriting default permissions that may be too permissive.
Common Patterns in Drivers
- Missing
SeSinglePrivilegeCheckorSeAccessCheckbefore performing a privileged operation (e.g., mapping physical memory, loading a driver, modifying system configuration) - Using
KernelModeinstead of the actualIrp->RequestorModewhen validating caller privileges, causing all requests to be treated as kernel-originated ObReferenceObjectByHandlecalled withoutOBJ_FORCE_ACCESS_CHECK, allowing user-mode callers to access objects regardless of their DACL- Incorrect
STATUS_SUCCESSreturn on an error path, causing the caller to proceed as if the operation succeeded when it actually failed or was denied - State machine that allows invalid transitions: an object can be re-initialized while active, or a cleanup operation can be skipped by transitioning directly to a terminal state
- Signature or integrity validation that accepts malformed, truncated, or self-signed data as valid
- Security descriptor not applied to a device object or named pipe, allowing any user to open and send IOCTLs to a privileged driver
- Missing impersonation level check before using a client's token for access decisions
- Version or integrity downgrade: allowing an older, vulnerable component to replace a newer, patched one without proper version enforcement
- Error path that does not clean up partially-completed state, leaving the system in an inconsistent security posture
Exploitation Implications
Logic bug exploitation is typically straightforward once identified, because there is no need for heap manipulation, race winning, or complex memory layout control. If the bug is a missing access check, the attacker simply calls the unprotected IOCTL or syscall and obtains the privileged operation directly. If the bug is a PreviousMode bypass, the attacker's user-mode process gains the ability to call kernel APIs as if it were kernel code.
The impact of logic bugs ranges from information disclosure (reading objects that should be access-controlled) to direct privilege escalation (performing operations restricted to SYSTEM or administrators). Some logic bugs create preconditions for memory corruption: for example, a missing access check might allow an unprivileged user to trigger a code path that was designed to be called only by trusted kernel components, and that code path contains an unsafe operation that is "safe" only because of the expected caller privilege level.
Logic bugs are also notable for their persistence. Because they are not detected by memory safety tools, they can survive for years across many Windows versions. The CVE-2024-21338 appid.sys vulnerability, for example, existed for years in a driver that was routinely audited, because the flaw was in the security model (allowing arbitrary callback invocation) rather than in memory handling. This makes logic bug hunting a high-value activity for both defenders and attackers.
Typical Primitives Gained
- Direct privilege escalation via missing access check -- the attacker performs a privileged operation without authorization
- Direct IOCTL R/W -- when a missing access check exposes a memory read/write IOCTL to unprivileged callers
- Token Manipulation -- when
PreviousModebypass allows unprivileged token modification - Security policy bypass -- circumventing signature validation, integrity checks, or version enforcement
- Write-What-Where -- when a logic bug grants access to a write operation that is normally restricted
Mitigations
- Principle of least privilege -- Device objects should have restrictive security descriptors that limit access to the minimum set of users required
- OBJ_FORCE_ACCESS_CHECK -- Always use this flag when calling
ObReferenceObjectByHandlefrom IOCTL handlers to enforce DACL checks on user-supplied handles - RequestorMode enforcement -- Always use
Irp->RequestorModeorExGetPreviousMode()instead of hardcodingKernelModefor access decisions - Secure device creation -- Use
IoCreateDeviceSecurewith an explicit SDDL string to define precise access control on the device object at creation time - Code review checklists -- Maintain driver-specific security checklists that enumerate every entry point and the required access checks, validated during code review
- Threat modeling -- Formal threat modeling of driver interfaces identifies missing access checks before code is written
Detection Strategies
- Patch diffing: Look for added
SeAccessCheck,SeSinglePrivilegeCheck,IoIs32bitProcess,ExGetPreviousMode, orOBJ_FORCE_ACCESS_CHECKadditions. AutoPiff detects these asprivilege_check_addedandaccess_mode_enforcement_added. - Manual code review: This is the most effective technique for logic bugs. Review every IOCTL handler's entry point for proper caller validation. Verify that
RequestorModeis checked, that required privileges are enforced, and that error paths return appropriate error codes. - Security model analysis: Document the driver's intended security model (who should be able to call what), then systematically verify that each IOCTL and exposed interface enforces that model.
- Threat modeling: Enumerate all entry points (IOCTLs, file system callbacks, PnP handlers) and determine what security checks each one should perform. Compare against the actual implementation.
- Differential testing: Compare the behavior of the driver when called from an administrative context versus an unprivileged context. Any operation that succeeds from both contexts but should be admin-only indicates a missing access check.
Related CVEs
| CVE | Driver | Description |
|---|---|---|
| CVE-2024-26229 | csc.sys |
Missing access check allows unprivileged user to perform privileged IOCTL operations |
| CVE-2024-21302 | ntoskrnl.exe |
Secure kernel version downgrade bypass allowing rollback to vulnerable components |
| CVE-2024-21338 | appid.sys |
Logic flaw allows arbitrary kernel callback invocation from user mode |
| CVE-2023-28252 | clfs.sys |
Logic error in base log record validation enabling controlled memory corruption |
| CVE-2023-28218 | win32k.sys |
Missing privilege check in window management operation |
| Capcom.sys | Capcom.sys |
Intentional ring-0 code execution from user-supplied function pointer — the quintessential logic bug |
AutoPiff Detection
privilege_check_added-- Detects patches addingSeSinglePrivilegeCheck,SeAccessCheck, or equivalent privilege verification calls before privileged operationsaccess_mode_enforcement_added-- Detects replacement of hardcodedKernelModewith properIrp->RequestorModeorExGetPreviousMode()for caller validationhandle_force_access_check_added-- Detects addition ofOBJ_FORCE_ACCESS_CHECKflag toObReferenceObjectByHandlecalls, enforcing DACL checks on user-supplied handlesinterlocked_refcount_added-- Detects reference count hardening that prevents state manipulation through concurrent operationserror_path_corrected-- Detects fixes to error paths that previously returnedSTATUS_SUCCESSor failed to clean up state properlystate_validation_added-- Detects addition of state machine transition validation to prevent invalid or out-of-order operationsauthorization_validation_added-- Detects general authorization check additions to driver entry points