//nefariousplan

CVE-2025-8061: LnvMSRIO Does What Its Name Says

patterns

cve

proof of concept

LnvMSRIO.sys is a Lenovo kernel driver named after what it does. Lenovo, Model-Specific Register, Input/Output. Read MSR. Write MSR. Read physical memory. Write physical memory. CVE-2025-8061 calls this "a potential insufficient access control vulnerability." The driver's name calls it the spec.

The driver's name is the contract

Lenovo's Dispatcher utility ships a kernel driver, LnvMSRIO.sys, registered as the service LnvMSRIO and exposing the device path WinMsrDev (under the standard NT global object namespace prefix). The four IOCTL codes the driver implements are not buried in some debug-only branch. They are the driver's public surface, and three published proof-of-concept repositories define them with the same constants.

#define READ_MSR    0x9C402084   // read Model-Specific Register
#define WRITE_MSR   0x9C402088   // write Model-Specific Register
#define READ_PHYS   0x9C406104   // read physical memory
#define WRITE_PHYS  0x9C40A108   // write physical memory

That is the entire API. Two MSR primitives, two physical-memory primitives, all reached through a CreateFileW call against a device whose name a user-mode process is allowed to open. The PoC's wrapper class does it with no privilege elevation:

m_dev = CreateFileW(device, GENERIC_READ | GENERIC_WRITE,
    0, NULL, OPEN_EXISTING, 0, NULL);

A standard user, logged into Windows, with no admin rights, opens this device and calls any of the four IOCTLs.

The CVE description picked the wrong word

NVD says: "A potential insufficient access control vulnerability was reported in the Lenovo Dispatcher 3.0 and Dispatcher 3.1 drivers." CWE-782, "Exposed IOCTL with Insufficient Access Control."

"Insufficient" implies that some access control existed and was not strong enough. That is not what the source presents. The published exploits do not bypass an ACL, do not race against a check, do not impersonate. They open the device. They send IOCTLs. They get back kernel reads and writes.

The scoring picks up the same euphemism. The CVSS:3.1 vector contains AC:H, "high attack complexity." The complete weaponized chain is in spawn451's repository at fourteen stars, segura2010's repository at four stars, and Symeon Paraschoudis published a token-stealing PoC for Windows 11 24H2 that landed at 121 stars in October 2025. None of these chains are complex. They open the documented device, call the documented IOCTLs, and walk a public Outflank technique to translate kernel virtual addresses to physical ones. The score reflects neither the attack surface nor the weaponization rate.

The exploit primitive is the IOCTLs called in order

Once a user-mode handle to the device is open, the chain has been public since Quarkslab's writeup by Luis Casvella. It does not require novel research.

Step one: read IA32_LSTAR via IOCTL 0x9C402084. That MSR holds the address of nt!KiSystemCall64, the syscall entry point, in kernel virtual memory. A user-mode read of an MSR is already over the line; the value it returns is also a complete KASLR bypass.

uint64_t lstar = mem.read_msr(0xC0000082);
// lstar is now a kernel virtual address inside ntoskrnl.

Step two: translate that virtual address to a physical address. The driver only exposes physical memory R/W, not virtual. The Outflank Superfetch technique is the public method. Query NtQuerySystemInformation with SystemSuperfetchInformation, walk PFN entries, build a VA-to-PA table from user mode. segura2010's Rust PoC wraps this in a single call:

let mut sf_trans = superfetchrs::superfetch::Superfetchtranslator::new();
sf_trans.query_pfn_and_cache()?;
let phys_kernel_fn = sf_trans.translate(virt_kernel_fn)?;

Step three: write shellcode into a kernel function via IOCTL 0x9C40A108. segura2010 picks nt!NtAddAtom because it is short, easily reached via a syscall, and invoked from user mode without setup. The shellcode toggles bit 20 of CR4 to disable SMEP, jumps to user-mode shellcode that walks the EPROCESS list, copies the PID-4 (System) token into the current process, restores CR4, and returns. After the call lands, whoami on the unprivileged shell prints SYSTEM under the NT AUTHORITY domain.

The four IOCTLs are not building blocks of an exploit. They are the exploit, called in the order the documentation would describe if the driver had documentation.

What the patch changes, and what it cannot

Lenovo's advisory directs users to Dispatcher driver 3.1.0.41 or later (the 3.2 branch). The advisory does not detail the diff, but the CWE narrows the shape. CWE-782 fixes are a small family. They restrict the device DACL so non-admin users cannot open the handle. Or they add an ExGetPreviousMode-style check inside the IOCTL handler. Or both.

Either fix leaves the driver doing exactly what its name says. Read MSR, write MSR, read physical memory, write physical memory, now restricted to administrator callers. Administrators on a Windows system already have kernel-debugger-equivalent capabilities; the driver in the post-patch state grants no new authority to anyone. Its single distinguishing feature, exposing kernel-mode primitives to user-mode code, is the feature the patch removes. The driver continues to ship.

This is the ordinary shape of an OEM kernel utility. What is unusual is naming the driver after the capability it exposes, then deciding several major versions later that the capability ought to be gated.

The signed binary does not expire

LnvMSRIO.sys 3.0 and 3.1 are code-signed by Lenovo and attested through Microsoft's Windows Hardware Quality Labs program. That signature is what allows the driver to load in the first place. Patching Dispatcher to 3.2 deploys a new signed binary; it does not invalidate the old one. Removing the old binary from the fleet is the operator's job, and on a non-managed consumer notebook there is nothing on the system that will remove it.

The vulnerable binary persists wherever a user has not patched. It also persists everywhere else. A signed kernel driver that exposes physical memory write to user mode, downloadable as a single .sys file, is the canonical Bring Your Own Vulnerable Driver primitive. An attacker who has obtained administrator on any Windows host (domain-joined or not, Lenovo or not) drops LnvMSRIO.sys 3.1.0.40, registers it as a service, and obtains the same kernel R/W against a system that has never run a Lenovo product.

Microsoft maintains a vulnerable-driver blocklist. The blocklist is opt-in for systems without HVCI, lags the WHQL signature pipeline by months, and does not retroactively invalidate signatures. The signed binary outlives the patch.

The mitigation does not exist on every notebook Lenovo sold

The advisory's second sentence is the framing that travels into vendor coverage: "This vulnerability does not affect systems when the Windows feature Core Isolation Memory Integrity is enabled. Lenovo systems preloaded with Windows 11 have this feature enabled by default."

HVCI does not unload the driver. HVCI does not stop a user from sending the IOCTLs. HVCI, when active, blocks specific kernel exploitation primitives that the published chain depends on, primarily the trick of overwriting an executable kernel page with shellcode. The chain has to be rebuilt around data-only attacks. It is rebuildable. The IOCTLs still return kernel reads, which is half of the primitive on its own.

The "default" claim covers Lenovo notebooks that shipped with Windows 11. It does not cover the Lenovo notebook fleet that shipped with Windows 10, where HVCI is off by default and remains off on most consumer installs. It does not cover notebooks where HVCI was disabled to load a third-party driver that lacks HVCI compatibility, including some Lenovo ones. The mitigation Lenovo points to is the mitigation Microsoft points to whenever a driver of this shape gets a CVE. It is present on a fraction of the fleet and shrinking as third-party driver compatibility pushes operators to disable it.

The pattern is OEM kernel utility, and it does not converge

This is a design-debt-driver of the OEM-utility class. The same class as clfs.sys on the Microsoft side, with the substrate flipped. CLFS produces use-after-free CVEs because parsing user-supplied binary log files is its job. LnvMSRIO.sys produces access-control CVEs because exposing kernel primitives to user mode is its job. The substrate is the design.

OEMs ship kernel drivers because vendor utilities want capabilities Windows does not expose to user mode. Fan curves, BIOS update channels, performance monitoring, RGB, GPU overclock, MSR I/O. Each driver is a small surface, signed by the OEM, attested by Microsoft, deployed to millions of consumer machines. The aggregate surface is the LOLDrivers list, which is a database of these binaries. New entries arrive on a cadence. Microsoft's blocklist absorbs them on a slower cadence. The substrate, the relationship between OEM trust signing and end-user kernel exposure, has not changed in two decades.

In the unpatchable-primitive sense, the bug is also terminal. Lenovo can keep the device DACL tight in 3.2, 3.3, 3.4. The 3.0 and 3.1 binaries that pre-date the gate stay valid kernel-mode loadables on every Windows system that has not enabled the blocklist. The vendor's remediation can only reach the systems the vendor's update pipeline reaches. The signature reaches every Windows system that will ever exist.

The driver does what its name says. The patch is the moment Lenovo decided that what its name says is not what a non-administrator caller should be allowed to do. The signed binaries that disagree are still on the internet.

PoC: vxqs/Lenovo-CVE-2025-8061

Lenovo can patch the driver. Lenovo cannot unsign it.