agrsm64.sys — Missing authorization on a world-reachable modem control device with unchecked IOCTL dispatch (CWE-862) fixed
KB5075912
1. Overview
- Unpatched Binary:
agrsm64_unpatched.sys - Patched Binary:
agrsm64_patched.sys - Overall Similarity Score: 0.0026 (Effectively 0%)
- Diff Statistics: Matched: 7, Changed: 5, Identical: 2, Unmatched (Either direction): 0.
- Verdict: The vendor neutralized the driver. The ~2,880-function unpatched build is replaced by a 7-function inert stub whose
DriverEntryimmediately returns a failure status (0xC0000424) and whose import table is empty, removing the entire device/IOCTL attack surface.
2. Vulnerability Summary
- Severity: Medium
- Vulnerability Class: Missing Authorization (CWE-862)
- Affected Functions:
DriverEntry(0x12E008),SerialIoControl(0x1297F8, IRP_MJ_DEVICE_CONTROL dispatch)
Root Cause & Exploitability:
The unpatched driver creates a control device \Device\LSISM_xface (IoCreateDevice, DeviceType 0x1B = FILE_DEVICE_SERIAL_PORT, Exclusive = FALSE, DeviceExtensionSize = 0, DeviceCharacteristics = 0 so no FILE_DEVICE_SECURE_OPEN) and exposes it via IoCreateUnprotectedSymbolicLink at \DosDevices\LSISM_xface. The creation call supplies no explicit security descriptor/SDDL, and the symbolic link is created with a world-accessible security descriptor. The IRP_MJ_DEVICE_CONTROL handler SerialIoControl (registered at MajorFunction[0xe]) reads the IOCTL code straight from the IRP stack location and dispatches roughly two dozen IOCTL_SERIAL_* codes (0x1B0004 through 0x1B006C, METHOD_BUFFERED) without any caller-authorization check — no SeAccessCheck, no privilege check, no PreviousMode validation. The most security-relevant reachable side effect is a nonpaged pool allocation of caller-controlled size (IOCTL 0x1B0008 / IOCTL_SERIAL_SET_QUEUE_SIZE) and unauthenticated writes into the driver's serial-configuration state in the device extension.
Note on hardware-access routines: the driver does call MmMapIoSpace (CSV92EXInit, 0x8406D) and does perform raw I/O-port in/out (CAmrALiStop_0, 0x183E1 onward), but both operate only on the modem's own assigned hardware resources during device initialization — the physical address comes from the assigned hardware resource descriptor, and the port base is the device's assigned I/O base at [ext+0x198]. Neither is reachable from, nor influenced by, the user-mode IOCTL surface, so neither provides an attacker-controlled memory or port primitive.
Attacker-Reachable Entry Point & Data Flow:
1. A process opens \\.\LSISM_xface, resolved through the world-accessible symbolic link.
2. DeviceIoControl generates an IRP that reaches SerialIoControl (MajorFunction[0xe]).
3. SerialIoControl reads the IOCTL code from the IRP stack location (mov eax, [rbx+18h] at 0x12985A) and dispatches without checking caller privileges.
4. IOCTL 0x1B0008 reads a size from the first DWORD of the input buffer and, if it exceeds the current queue size, calls ExAllocatePoolWithTag(NonPagedPool, size, 'AGSM') at 0x129B35.
3. Pseudocode Diff
// === UNPATCHED DRIVER: DriverEntry (0x12E008) ===
RtlInitUnicodeString(&var_208, u"\\Device\\LSISM_xface");
if (IoCreateDevice(DriverObject, 0, &var_208, 0x1b, 0, FALSE, &DeviceObject) >= 0) {
DeviceObject->Flags |= DO_BUFFERED_IO;
RtlInitUnicodeString(&var_1f8, u"\\DosDevices\\LSISM_xface");
// World-accessible link; device created with no explicit SDDL and no FILE_DEVICE_SECURE_OPEN
IoCreateUnprotectedSymbolicLink(&var_1f8, &var_208);
}
// Registers the IRP_MJ_DEVICE_CONTROL dispatch that performs no authorization check
DriverObject->MajorFunction[0xe] = SerialIoControl;
// === PATCHED DRIVER: DriverEntry (0x1C0006008) ===
return 0xC0000424; // immediate failure; no device, no symbolic link, no dispatch registration
4. Assembly Analysis
Unprotected Device Exposure (DriverEntry, 0x12E008):
000000000012E2F1 call cs:__imp_RtlInitUnicodeString ; builds SymbolicLinkName = "\DosDevices\LSISM_xface"
000000000012E2F7 lea rdx, aDeviceLsismXfa ; DeviceName = "\Device\LSISM_xface"
000000000012E2FC lea rcx, [rsp+248h+SymbolicLinkName] ; SymbolicLinkName
000000000012E301 call cs:__imp_IoCreateUnprotectedSymbolicLink ; world-accessible symbolic link
The preceding IoCreateDevice (0x12E2C3) passes DeviceType 0x1B, Exclusive = 0 and DeviceCharacteristics 0 (no FILE_DEVICE_SECURE_OPEN) with no explicit security descriptor.
IOCTL Dispatch Without Authorization (SerialIoControl, 0x1297F8):
000000000012980C mov rsi, [rcx+40h] ; device extension
...
000000000012985A mov eax, [rbx+18h] ; IoControlCode from IRP stack location ([Irp+0xB8]+0x18)
000000000012985D mov ecx, 1B0048h
0000000000129862 cmp eax, ecx ; range dispatch — no SeAccessCheck / PreviousMode check
Caller-Controlled Pool Allocation (IOCTL 0x1B0008, 0x129B11):
0000000000129B11 cmp dword ptr [rbx+10h], 8 ; InputBufferLength >= 8
0000000000129B15 mov rax, [r12+18h] ; SystemBuffer
0000000000129B1C mov eax, [rax] ; caller-supplied size
0000000000129B1E cmp eax, [rsi+140h] ; only bound: must exceed current queue size
0000000000129B24 jbe loc_12A256
0000000000129B2A mov rdx, rax ; NumberOfBytes
0000000000129B2D xor ecx, ecx ; NonPagedPool
0000000000129B35 call cs:__imp_ExAllocatePoolWithTag ; Tag 'AGSM'
5. Trigger Conditions
- Load the signed
agrsm64.sysdriver on a target machine (requires a vulnerable-driver-blocklist bypass if enforced). - From an unprivileged context, open a handle:
CreateFileW(L"\\\\.\\LSISM_xface", ...). This resolves through the world-accessible symbolic link; the device is created withoutFILE_DEVICE_SECURE_OPENor an explicit SDDL. - Issue
DeviceIoControltargetingIOCTL_SERIAL_*codes in the range0x1B0004through0x1B006C(METHOD_BUFFERED). - To trigger pool allocation: send IOCTL
0x1B0008with an input buffer whose first DWORD (greater than the current queue size at[ext+0x140]) becomes theNumberOfBytespassed toExAllocatePoolWithTag(NonPagedPool, ...). - To trigger device-extension manipulation: send IOCTL
0x1B001Cpassing 0x14 bytes, copied into the device extension (offsets 0x184–0x194). - Observable Effect: unauthenticated modem-configuration changes and a caller-sized nonpaged pool allocation from a low-integrity process. No BSOD occurs during normal operation.
6. Exploit Primitive & Development Notes
- Verified reachable primitives (from the user-mode IOCTL surface):
- Nonpaged pool allocation of caller-controlled size (IOCTL
0x1B0008/ IOCTL_SERIAL_SET_QUEUE_SIZE): the size is taken from the input buffer and is only bounded to be larger than the current queue size, so a large value can be requested (pool-exhaustion / denial of service, pool grooming). - Unauthenticated writes into the serial device-extension configuration fields (e.g. IOCTL
0x1B001Ctimeouts,0x1B0018wait mask, line-control settings). - Not present / not attacker-reachable: the driver's
MmMapIoSpace(CSV92EXInit, 0x8406D) and port-I/O (CAmrALiStop_0, 0x183E1) operations act only on the device's own assigned hardware resources during initialization; they are not reachable from the IOCTL dispatch and their addresses are not attacker-influenced. No arbitrary kernel read/write primitive is exposed by the reachable IOCTL surface. - Overall assessment: because no attacker-controlled memory-corruption or arbitrary read/write primitive was found on the reachable path, a reliable local privilege escalation is not demonstrated by the code. The verified impact is limited to an authorization bypass with denial-of-service-class and configuration-tampering effects.
7. Debugger PoC Playbook
Ensure a kernel debugger (WinDbg/KD) is attached to the unpatched target.
Breakpoints:
- bp agrsm64_unpatched!SerialIoControl (0x1297F8)
Why: Intercepts all IOCTL traffic. RCX = Device Object, RDX = IRP.
- bp agrsm64_unpatched+0x129b35
Why: Hits the ExAllocatePoolWithTag allocation inside IOCTL 0x1B0008.
What to Inspect:
- At SerialIoControl (0x1297F8): Check the IRP stack location. Execute dx -r1 (*((kg64!_IRP*)(@rdx))) and inspect Tail.Overlay.CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode.
- At +0x129b35: Inspect RAX/RDX to verify the caller-controlled pool allocation size (first DWORD of the input buffer, required to exceed [ext+0x140]).
Trigger Setup:
1. From user mode (even low-integrity): HANDLE h = CreateFileW(L"\\\\.\\LSISM_xface", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
2. Issue IOCTL: DeviceIoControl(h, 0x1b0008, inputBuf, sizeof(inputBuf), outputBuf, sizeof(outputBuf), &bytesReturned, NULL);
3. Send a 4-byte payload in inputBuf (e.g. 0x1000) to exercise the allocator.
Struct/Offset Notes:
- IRP Control Code: [rdx+0xB8]+0x18
- Device Extension: [rcx+0x40]
8. Changed Functions — Full Triage
The diff reported 5 matched/changed functions. Because the patched binary was reduced to 7 generic runtime functions, structural false positives dominate the diff:
_start(Sim: 0.245): Cosmetic. Entry point relocation due to the modified image base.sub_EC8CC(Sim: 0.405): Cosmetic. Address-sequence false match.sub_EF3B0(Sim: 0.405): Cosmetic. False match between CRT/security-cookie routines.CSV92EXIntEnter (sub_843EC)(Sim: 0.876): Cosmetic. Unpatched is a modem state setter writing to hardware registers; the patched match is a__security_init_cookieroutine.__security_check_cookie (sub_E6FB0)(Sim: 0.965): Cosmetic. Both are__security_check_cookieroutines with minor address relocations.
Note on Behavioral Changes: The actual behavioral security change is not reflected in the changed_functions array because the patch removed all of the original functions without providing directly matched equivalents; they were structurally unmatched.
9. Unmatched Functions
- Removed: 0 tracked by the diff agent (see caveat).
- Added: 0 tracked by the diff agent.
Implication: The patched build is an entirely new stub driver, replacing the ~2,880-function unpatched binary. The diff agent mapped only a handful of CRT functions, leaving the discarded unpatched functions untracked.
10. Confidence & Caveats
- Confidence Level: High for the missing-authorization finding.
- Rationale: The disassembly confirms
IoCreateUnprotectedSymbolicLinkat 0x12E301, device creation with noFILE_DEVICE_SECURE_OPENand no explicit SDDL,SerialIoControlregistered atMajorFunction[0xe], and anIOCTL_SERIAL_*dispatch (0x1B0004–0x1B006C) with no authorization check, including a caller-sized nonpaged pool allocation at 0x129B35. - Scope: The
MmMapIoSpace(0x8406D) and port-I/O (0x183E1) paths were confirmed to run only during device initialization on the modem's assigned hardware resources; they are not reachable from the user-mode IOCTL surface and do not constitute an attacker-controlled primitive. - Manual Verification Required: Confirming, on a live system, that the device's default security descriptor actually permits an unprivileged process to open
\\.\LSISM_xface(the driver applies no explicit ACL; effective access depends on the I/O manager's default device security).