ioctlance: windows driver vulnerability detection

overview

ioctlance - enhanced fork of the vulnerability detection tool for windows driver model (wdm) drivers using symbolic execution and taint analysis.

  • detects 13 types of kernel vulnerabilities (original had ~11)
  • original teamt5 version found 117 vulnerabilities resulting in 41 cves
  • this enhanced version finds additional vulnerabilities the original missed
  • 25-35% faster than original implementation
  • plugin-based architecture for extensibility
  • discovers critical buffer overflows with controllable pc (rce)

background

the problem

windows kernel drivers operate with highest privileges. vulnerabilities enable:

  • privilege escalation to system/kernel
  • arbitrary physical memory access
  • kernel code execution
  • byovd (bring your own vulnerable driver) attacks

notable examples:

  • rtcore64.sys: used in blackbyte ransomware
  • hw.sys: candiru spyware rootkit installation
  • amdpowerprofile.sys: cve-2023-20562 arbitrary memory access

symbolic execution approach

ioctlance uses angr to:

  1. find ioctl handler: trace driverentry to locate irp_mj_device_control
  2. create symbolic inputs: make user buffers symbolic variables
  3. explore execution paths: analyze all possible code paths
  4. detect vulnerabilities: monitor dangerous api calls and memory access

enhanced detection capabilities

detector comparison

original ioctlance (~11 detectors):

  • map physical memory (mmmapiosspace, zwmapviewofsection)
  • controllable process handle (zwopenprocess, obopenobjectbypointer)
  • read/write controllable address
  • null pointer dereference
  • arbitrary shellcode execution
  • dangerous file operations
  • arbitrary process termination
  • basic buffer overflow (memcpy size check)
  • arbitrary wrmsr (cpu instruction)
  • arbitrary out (i/o port)
  • dest/src controllable (memcpy/memmove)

enhanced version (13 plugin detectors + cpu hooks):

newly added detectors (6):

  • use after free - detects access to freed memory
  • double free - catches multiple free operations
  • race conditions - toctou vulnerabilities
  • integer overflow - arithmetic vulnerabilities
  • format string - format string bugs
  • probe bypass - detects actual probeforread/write bypasses (vs just tracking)

enhanced detectors:

  • stack buffer overflow - now detects controllable pc/rce (vs basic size check)
  • process termination - merged with process handle abuse detection

recently restored from original:

  • ObOpenObjectByPointer - process handle manipulation for privilege escalation
  • dest/src controllable - arbitrary read/write via controlled memcpy addresses

new vulnerabilities found

example: ilp60x64_3.sys analysis

  • original ioctlance: 19 vulnerabilities (read/write primitives)
  • enhanced version: 35 vulnerabilities total
    • includes all 19 original detections
    • plus 10 critical buffer overflows with controllable pc
    • plus 6 additional read/write vulnerabilities

why original missed these:

# original: stopped at initial vulnerability
simgr = proj.factory.simgr(state)  # save_unconstrained=False

# enhanced: continues to find exploitation paths
simgr = proj.factory.simgr(state, save_unconstrained=True)
if state.regs.pc.symbolic:  # detects control flow hijack
    report_buffer_overflow_rce()

the difference: original finds “unlocked door” (write primitive), enhanced proves “can take over building” (rce).

vulnerability types detected

memory vulnerabilities

  • physical memory mapping: mmmapiosspace with controllable parameters
  • arbitrary read/write: user controls memory addresses
  • null pointer dereference: unvalidated null pointers
  • buffer overflow: stack and heap overflows
  • use after free: accessing freed memory

control flow vulnerabilities

  • arbitrary code execution: controllable function pointers
  • arbitrary wrmsr: write to model-specific registers
  • arbitrary out: i/o port operations

access control vulnerabilities

  • process handle abuse: missing obj_force_access_check
  • dangerous file operations: privileged file access
  • process termination: unauthorized process killing

architecture

plugin-based detectors

from ioctlance.detectors import VulnerabilityDetector

class PhysicalMemoryDetector(VulnerabilityDetector):
    name = "physical_memory_mapping"

    def check_state(self, state, event_type, **kwargs):
        # check if user controls physical address
        if self._is_tainted(physical_address):
            return self.create_vulnerability_info(
                title="Arbitrary Physical Memory Mapping",
                description="User controls MmMapIoSpace parameters",
                state=state
            )

modular hook system

# hooks organized by subsystem
hooks/
├── memory.py      # allocation and mapping
├── process.py     # process management
├── file_ops.py    # file operations
├── kernel.py      # core kernel apis
└── opcodes.py     # cpu instructions

usage

command line

# analyze single driver
uv run python -m ioctlance.cli driver.sys \
  --timeout 120 \
  --output results.json \
  --verbose

# analyze directory of drivers
uv run python -m ioctlance.cli ./drivers/ \
  --timeout 60 \
  --batch

rest api

# start api server
uv run uvicorn ioctlance.api:app --host 0.0.0.0

# upload and analyze
curl -X POST http://localhost:8080/analyze \
  -F "file=@driver.sys" \
  -F "timeout=120"

docker

# docker-compose.yml
services:
  ioctlance-api:
    image: ioctlance:latest
    ports:
      - '8080:8080'
    volumes:
      - ./drivers:/drivers

results format

{
  "driver": {
    "path": "RTCore64.sys",
    "size": 36864,
    "arch": "amd64"
  },
  "vulnerabilities": [
    {
      "title": "Arbitrary Physical Memory Mapping",
      "description": "User controls address in MmMapIoSpace",
      "eval": {
        "IoControlCode": "0x80002048",
        "SystemBuffer": "0x41414141",
        "InputBufferLength": "0x8"
      },
      "raw_data": {
        "constraints": [...],
        "memory": {...},
        "registers": {...}
      }
    }
  ]
}

performance optimizations

improvements over original

  • lru caching: frequently called functions cached
  • pre-compiled regex: 25-35% speedup for pattern matching
  • data structures: frozensets for o(1) tainted buffer checks
  • state deduplication: avoid analyzing duplicate states

benchmarks

metricoriginalrefactored
analysis speedbaseline+25-35%
memory usage~500mb~400mb
detectors713
test coverage0%48%

testing approach

real driver validation

// test_physical_memory.c - minimal vulnerable driver
NTSTATUS IoctlHandler(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    if (stack->Parameters.DeviceIoControl.IoControlCode == 0x800020C0) {
        PHYSICAL_ADDRESS PhysAddr;
        // vulnerability: user controls physical address
        RtlCopyMemory(&PhysAddr, Irp->AssociatedIrp.SystemBuffer,
                      sizeof(PHYSICAL_ADDRESS));
        MmMapIoSpace(PhysAddr, PAGE_SIZE, MmNonCached);
    }
    return STATUS_SUCCESS;
}

compile test drivers:

# build with mingw-w64
x86_64-w64-mingw32-gcc -shared -nostdlib \
  -I/usr/share/mingw-w64/include/ddk \
  -o test_driver.sys test_driver.c \
  -Wl,--subsystem,native -Wl,--entry,DriverEntry

limitations

false positives

sources of false positives:

  • try-except blocks not modeled
  • indirect validation checks
  • administrator-only access paths
  • kernel-only operations

analysis constraints

  • timeout limits exploration depth
  • path explosion in complex drivers
  • indirect jumps may be missed
  • some anti-analysis techniques work

references

original work

this enhanced fork

  • mjbommar/ioctlance - refactored version with improved detection
  • discovers vulnerabilities original missed (buffer overflows with rce)
  • 104 vulnerable driver test dataset included

frameworks and tools

  • cab-fuzz: coverage-guided kernel fuzzing
  • diode: directed fuzzing for drivers
  • screwdrivers: symbolic execution approach
  • popkorn: previous taint analysis tool

ioctlance improves on these by combining symbolic execution with comprehensive taint analysis and addressing path explosion through optimized state management.

on this page