Skip to content

CVE-2024-21338

AppLocker -- IOCTL 0x22A018 missing access control allows kernel code execution

Exploited in the Wild

This vulnerability was exploited in the wild before or shortly after patching.

Summary

Field Value
Driver appid.sys
Vulnerability Class IOCTL Hardening
Vulnerable Build 10.0.22621.2506 (KB5031455)
Fixed Build 10.0.22621.3155 (KB5034765)
Exploited ITW Yes

Affected Functions

  • AipSmartHashImageFile
  • CTuneupMgr::NotifyRefresh

Root Cause

appid.sys is the kernel-mode component of Windows AppLocker. It creates a device object (\Device\AppId) that user-mode services interact with via DeviceIoControl. Among its IOCTL handlers, code 0x22A018 dispatches to a routine that computes a "smart hash" of a file by calling AipSmartHashImageFile, which maps the target file into kernel memory.

What makes this IOCTL dangerous is a combination of three design failures. First, it uses METHOD_NEITHER, meaning the I/O manager passes raw user-mode pointers directly to the driver without buffering or probing. The driver receives whatever addresses the caller provides. Second, the handler dereferences a user-supplied pointer from the input buffer without calling ProbeForRead or ProbeForWrite, so a kernel-mode virtual address can be passed in place of a user-mode buffer. Third, and most critically, no access control check guards this IOCTL. Any process at any privilege level can open a handle to \Device\AppId and send control code 0x22A018.

The result is that any process with admin privileges can send a DeviceIoControl request that causes the kernel to read from or write to an arbitrary address during the hash computation.

AutoPiff categorizes this as ioctl_hardening with detection rules:

  • ioctl_input_size_validation_added
  • ioctl_code_default_case_added

Exploitation

This vulnerability was exploited in the wild by Lazarus Group as an admin-to-kernel zero-day to deploy the FudModule rootkit. The discovery by Gen Digital (Avast) marked it as a "beyond BYOVD" attack: instead of bringing a vulnerable third-party driver, the attackers targeted appid.sys, which ships with every modern Windows installation and loads by default. No driver drop, no blocklist to worry about.

The exploitation is direct and deterministic. A DeviceIoControl call with IOCTL 0x22A018 supplies a crafted input buffer whose pointer fields contain chosen kernel addresses. The handler dereferences these during the smart-hash computation, reading from or writing to arbitrary kernel memory. There is no race condition, no heap grooming, and no information leak needed as a prerequisite.

Lazarus used this kernel read/write primitive to overwrite kernel callback structures and token fields. From there, they deployed the FudModule rootkit, which performs data-only DKOM (Direct Kernel Object Manipulation) to disable security monitoring callbacks (PsSetCreateProcessNotifyRoutine, ObRegisterCallbacks), zero ETW provider registrations, and hide processes. The rootkit operates entirely through kernel data manipulation, avoiding code injection that would trigger code integrity alerts.

Patch Analysis

The patch in KB5034765 (build 10.0.22621.3155) made three changes to the IOCTL dispatch logic in appid.sys:

  1. Access control validation on IOCTL 0x22A018 -- the handler now verifies caller privileges before processing.
  2. Input buffer size validation -- requests with an input buffer smaller than the expected structure size are rejected.
  3. A default case in the IOCTL dispatch switch that rejects unrecognized control codes.

AutoPiff detects these via the ioctl_input_size_validation_added and ioctl_code_default_case_added rules, which fire when binary diffing shows new size checks on IOCTL input buffers and a default rejection path in the dispatch.

Detection

YARA Rule

rule CVE_2024_21338_appid_sys {
    meta:
        description = "Detects vulnerable version of appid.sys (pre-patch)"
        cve = "CVE-2024-21338"
        author = "KernelSight"
        severity = "high"
    strings:
        $mz = { 4D 5A }
        $driver_name = "appid.sys" wide ascii nocase
        $version = "10.0.22621.2506" wide ascii
        $smart_hash = "AipSmartHashImageFile" ascii
        $ioctl_code = { 18 A0 22 00 }  // IOCTL 0x22A018 little-endian
    condition:
        $mz at 0 and $driver_name and $version and ($smart_hash or $ioctl_code)
}

ETW Indicators

Provider Event / Signal Relevance
Microsoft-Windows-AppLocker AppLocker policy evaluation events Anomalous IOCTL traffic to \Device\AppId from non-AppLocker service processes
Microsoft-Windows-Security-Auditing Event 4688 -- Process creation Post-exploitation child processes spawned with SYSTEM or kernel-level privileges
Microsoft-Windows-Security-Auditing Event 4672 -- Special privileges assigned Privilege acquisition after kernel callback or token field corruption
Microsoft-Windows-Kernel-Audit Kernel callback registration changes Tampering with security callback arrays (PsSetCreateProcessNotifyRoutine, ObRegisterCallbacks) for EDR evasion
Microsoft-Windows-Kernel-Process Process and thread creation/termination Hidden processes or tampered tokens from FudModule rootkit deployment

Behavioral Indicators

  • A user-mode process opens a handle to \Device\AppId and sends IOCTL 0x22A018 with input buffers containing kernel-mode pointer values
  • The IOCTL input buffer contains pointer fields pointing to kernel virtual addresses rather than valid file paths, repurposing the hash computation as a kernel R/W primitive
  • Kernel security callback arrays (process creation notify routines, object callbacks, minifilter callbacks) are zeroed or corrupted, indicating FudModule rootkit deployment
  • A process token is modified by writing directly to kernel memory, transitioning from admin to kernel-level access without documented APIs
  • A process communicates with \Device\AppId while the AppLocker service (AppIDSvc) is not running or the process has no relationship to AppLocker policy evaluation (since 0x22A018 is an internal-only IOCTL)

Broader Significance

CVE-2024-21338 represents a shift in the admin-to-kernel exploitation landscape. Lazarus Group's decision to use a built-in Windows driver instead of a BYOVD approach eliminates the most visible part of the BYOVD kill chain: dropping a third-party driver that defenders can blocklist, hash-check, or flag via Sysmon Event ID 6. appid.sys is always present, always loaded, and always trusted. This makes the attack invisible to driver-loading telemetry. The FudModule rootkit's data-only approach (modifying kernel data structures without injecting code) further evades code integrity and ETW-based detection. Together, these techniques demonstrate a mature threat actor adapting to hardened environments where traditional BYOVD and code-injection techniques are increasingly monitored.

References