This repository documents a vulnerability discovered through static binary analysis of vgk.sys. The finding is an IOCTL handler that writes a flag bit across all occupied process slots in a global kernel table, with no scoping to the calling process. The write is unconditional once the input buffer length constraint is satisfied.
The repository contains the full methodology, extracted code, annotated analysis scripts, and an honest characterization of what could and could not be confirmed without access to the runtime-decrypted code regions. Also i used AI to clean up the scripts because they were in horrid condition when i was using them. And the scripts still have my user directory on them so make sure to change that if you want to run anything or test it.
README.md this file
finding.md technical description of the vulnerability
methodology.md how the analysis was conducted, in order
uncertainty.md what remains unconfirmed and why
scripts/
01_section_survey.py initial binary structure survey
02_ioctl_dispatcher.py locating and decompiling the IOCTL dispatch chain
03_flag_write_primitive.py isolating and analyzing FUN_1400807b8
04_poc_validation.py static confirmation of PoC parameters
evidence-excerpts/
dispatcher_branch.txt decompiled C showing the 0x22c0f8 branch
fun_1400807b8_decompiled.txt decompiled C of the slot flag write function
handler_asm.txt raw x86-64 of the IOCTL handler block
IOCTL code 0x22c0f8 reaches a handler that calls FUN_1400807b8(0x400, 1)
unconditionally when InputBufferLength equals 0x19. That function iterates
a global array of 32 process slot entries at a hardcoded kernel address. For
every slot where the occupancy field at slot[+0x18] is nonzero, it ORs
0x400 into the flags field at slot[+0x14]. The iteration is not gated on
whether the calling process owns a given slot. One registered VGK client can
set this bit across all registered clients simultaneously.
The decompiled output of FUN_1400807b8 is unambiguous. The loop structure,
slot stride of 0x20, occupancy check, and OR operation are all readable
directly from the Ghidra decompilation. The call site at 0x1400d4525
passes hardcoded 0x400 with a set flag (param_2 = 1), confirmed by raw
disassembly. The IOCTL code and buffer length gate are confirmed from the
decompiled dispatcher in a prior analysis session and reproduced in
evidence-excerpts/dispatcher_branch.txt.
The downstream consumers of bit 0x400 live in .riot0, a section that is
encrypted on disk and decrypted at driver load time. Static analysis cannot
reach this code. Whether bit 0x400 gates a security decision, a capability
check, or something operationally irrelevant cannot be determined without
dynamic analysis or source access. The severity floor is Medium on the basis
of the confirmed unscoped write primitive. The ceiling depends on what 0x400
controls at runtime.
The validator function FUN_14008921c, called immediately before
FUN_1400807b8 on the 0x22c0f8 path, is a single-instruction thunk to
0x1401df414. That address is in a runtime-populated region and is not
statically analyzable. The decompiled dispatcher shows no conditional branch
on its return value before the flag write, but full certainty on this point
requires tracing the decrypted code at runtime.