Target: Windows AppLocker (appid.sys)
Component: AppHashComputeImageHashInternal (Kernel Driver)
Vector: Local Logic Flaw (Input Processing)
The target is AppLocker (appid.sys), a native Windows kernel driver responsible for application control. Unlike the typical “Loud” BYOVD (Bring Your Own Vulnerable Driver) attacks where adversaries drop old drivers to hack the kernel, the Lazarus Group chose the “Silent” path. They exploited a driver that is already running by default in Windows, achieving a true “Living off the Land” exploit with zero load events to alert defenders.
The Context: This Zero-Day was weaponized by the Lazarus Group to deploy a Rootkit. Their goal was Direct Kernel Object Manipulation (DKOM) to unlink security callbacks, effectively blinding EDR/AV solutions like CrowdStrike and Windows Defender from Ring 0.
Root Cause: Blind Trust in User Input leading to Arbitrary Indirect Call.
The vulnerability is a classic logic flaw in how the driver processes input buffers.
appid.sys implicitly trusts that the pointer is safe, failing to check if the request originated from an untrusted User-Mode source.Before triggering the bug, the attacker must talk to the driver. The device object \Device\AppID has an Access Control List (ACL) that denies write access to standard Administrators (STATUS_ACCESS_DENIED). However, the account LOCAL SERVICE has explicit Write permission.
The attacker performs a token manipulation dance:
SeDebugPrivilege to steal the token from winlogon.exe (SYSTEM). This grants SeAssignPrimaryTokenPrivilege.LOCAL SERVICE token from a running svchost.exe.LOCAL SERVICE token. This process can now successfully open a handle to \Device\AppID.The attacker sends IOCTL 0x22A018 to the open handle. The driver passes the input to AppHashComputeImageHashInternal, which executes the unchecked function pointer.
The Obstacle: Modern Windows protections kCFG (Kernel Control Flow Guard) and SMEP (Supervisor Mode Execution Prevention) prevent simply jumping to shellcode.
The Solution: The attacker uses a valid “Gadget” that is allowed by kCFG: ExpProfileDelete().
ObfDereferenceObject to decrement a reference count.*Address = *Address - 1.The target of this decrement is the Previous Mode field in the current thread’s _KTHREAD structure.
The Math: Current Value (0x01) minus Gadget Action (1) equals 0x00 (Kernel Mode).
Once this byte flips to 0, the kernel treats the attacker’s thread as trusted (Ring 0). System calls now bypass security constraints, granting unrestricted Read/Write access to the entire system memory.
The remediation was simple: Microsoft added a check to ensure the IOCTL is not called from User Mode.
Vulnerable Logic: The driver accepted the IOCTL and processed the pointer regardless of the caller’s privileges.
Patched Logic:
// Pseudo-code based on binary diffing
NTSTATUS AipSmartHashImageFile(...) {
// THE FIX: Verify caller mode
if (ExGetPreviousMode() != KernelMode) {
return STATUS_ACCESS_DENIED;
}
// "Vulnerable" logic continues only if the caller is already Kernel...
AppHashComputeImageHashInternal(...);
}
Implicit Trust is Deadly. The driver assumed that because it was an internal component, the code execution flow was safe. It wasn’t.
ExGetPreviousMode).ExpProfileDelete) can be weaponized if a logic flaw allows you to control its inputs.