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 DriverEntry immediately 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

  1. Load the signed agrsm64.sys driver on a target machine (requires a vulnerable-driver-blocklist bypass if enforced).
  2. From an unprivileged context, open a handle: CreateFileW(L"\\\\.\\LSISM_xface", ...). This resolves through the world-accessible symbolic link; the device is created without FILE_DEVICE_SECURE_OPEN or an explicit SDDL.
  3. Issue DeviceIoControl targeting IOCTL_SERIAL_* codes in the range 0x1B0004 through 0x1B006C (METHOD_BUFFERED).
  4. To trigger pool allocation: send IOCTL 0x1B0008 with an input buffer whose first DWORD (greater than the current queue size at [ext+0x140]) becomes the NumberOfBytes passed to ExAllocatePoolWithTag(NonPagedPool, ...).
  5. To trigger device-extension manipulation: send IOCTL 0x1B001C passing 0x14 bytes, copied into the device extension (offsets 0x184–0x194).
  6. 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 0x1B001C timeouts, 0x1B0018 wait 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_cookie routine.
  • __security_check_cookie (sub_E6FB0) (Sim: 0.965): Cosmetic. Both are __security_check_cookie routines 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 IoCreateUnprotectedSymbolicLink at 0x12E301, device creation with no FILE_DEVICE_SECURE_OPEN and no explicit SDDL, SerialIoControl registered at MajorFunction[0xe], and an IOCTL_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).