Blog / Threat Classification · R-ROLE

The Kernel as Client: Why CVE-2025-21333 Is #3, Not #2

Same outcome as Calif. Same kernel-mode privilege territory. Opposite TLCTC cluster. The Hyper-V VSP heap overflow that proves directionality, not architecture, decides role assignment.

BK
Bernhard Kreinz
~11 min read
Series · The Kernel's Role · Part 2 of 2
Part 1 · Calif M5 · Kernel as Server (#2 → #2) Part 2 · CVE-2025-21333 · Kernel as Client (#3)

Part 1 of this series took the Calif M5 exploit and showed it cleanly as #2 → #2 — two server-side kernel exploits at a single trust boundary, ending in a root shell. The temptation after a piece like that is to generalise the wrong way: any attack ending in kernel-mode privilege must be #2. CVE-2025-21333 — the January 2025 heap-based buffer overflow in the Windows Hyper-V NT Kernel Integration VSP — exists to break that habit. Same kernel-mode territory. Same SYSTEM-level elevation outcome. Different cluster. The reason is structural, not stylistic.

The bug lives in vkrnlintvsp.sys, the driver that hosts the Hyper-V Virtual Service Provider running in the host's parent partition. The VSP communicates with the NT Kernel via the I/O ring mechanism — submitting requests into the ring and consuming what the kernel writes back into it. The heap overflow lives in the VSP's processing of those returned entries. The component is in kernel mode and runs at SYSTEM privilege, but in the specific interaction where the bug fires, it is acting as a client of the NT Kernel. Under R-ROLE, that determines everything.

§ 1 · The chainThe chain, stripped to its structure

An unprivileged user inside a guest VM holds the foothold. By interacting with the integration channel exposed by vkrnlintvsp.sys, the attacker influences what the VSP issues into the NT Kernel via the I/O ring. The kernel responds, writing into the ring. The VSP's processing of those returned entries fails to validate length or shape correctly, and the heap-based overflow fires in the VSP's client-side response-handling path. Because the VSP runs in the host's kernel context, control passes into attacker-influenced state at SYSTEM privilege on the host, and the boundary from guest user to host SYSTEM is crossed.

Canonical TLCTC path
...  #3 |[privilege][@guest_user→@host_SYSTEM]| + [DRE: C, I, A]

Antecedent steps establishing the guest-VM foothold are out of scope, denoted .... A single #3 step records the client-side overflow in the VSP's response handler. The intra-system annotation carries the privilege crossing without elevating it to a cluster. Terminal DRE tags follow the System Risk Event on the host.

Note what is not in the path. No #2 despite the kernel-mode crash. No #7 Malware despite the kernel-context code execution outcome. No additional cluster for the privilege escalation. The discipline of the framework is in what it forbids, not just what it asserts.

§ 2 · RoleThe kernel is a client here

The component on the receiving side of the analysis matters in two senses, and they are easy to confuse. The Hyper-V VSP runs at kernel privilege — that is an architectural fact about where its code executes. In the specific interaction under analysis, however, the VSP initiates a call into the NT Kernel and consumes the response. That is a role assignment, governed by direction.

R-ROLE clarifies role by direction, not privilege. If the vulnerable component accepts and handles inbound requests or stimuli relative to the attacker, the step is #2 Exploiting Server. If it consumes external responses, content, or state relative to the attacker, the step is #3 Exploiting Client. The attacker controlled what the kernel would return (indirectly, via the guest-side request the VSP forwarded). The VSP then fetched and processed that response. Client-role for the VSP. #3.

This is the case that refutes Misconception 2 — "the kernel is always the server" — by direct demonstration. A kernel-mode component, owned by the host, running with SYSTEM privilege, can still be the client in a specific interaction. Privilege is architecture. Role is direction.

§ 3 · ComparisonSame outcome, different cause

This is the punchline of the two-part series. Set the two paths next to each other.

Part 1 · Calif M5
...  #2  #2 |[priv][@local_user→@root]|

User-mode caller invokes kernel via syscall. Kernel processes inbound stimulus. Two server-side kernel bugs chained. Outcome: root shell.

Part 2 · Hyper-V VSP
...  #3 |[priv][@guest_user→@host_SYSTEM]|

VSP issues call into NT Kernel. VSP mishandles the response. Client-side overflow in VSP. Outcome: host SYSTEM.

Both end at the highest local privilege available on their respective platforms. Both cross a critical intra-system boundary. Both involve a kernel-mode component as the locus of the failure. Naïve outcome-anchored classification would put both in the same bucket — they share the consequence-side fingerprint. TLCTC refuses to do this because it classifies on the cause side, and the cause sides are not the same.

Part 1 is #2 because the attacker sends; the target receives. Part 2 is #3 because the target fetches; the attacker influences what is fetched. Two clusters. Two different generic vulnerabilities. Two different control surfaces. One identical-looking outcome.

§ 4 · Not #2Why this is not #2

The most common misclassification of this CVE substitutes the architectural fact for the directional one. Microsoft's advisory describes it as an Elevation of Privilege in the Hyper-V NT Kernel Integration VSP. Most secondary reporting compresses that further into "guest VM elevates to SYSTEM via VSP bug." Both framings are operationally true and directionally silent. A reader anchored on either could land on #2 by reasoning: the vulnerability lives in kernel-mode code, an attacker triggers it from below, therefore server-side kernel exploitation.

R-ROLE is unimpressed by that reasoning. The relevant question is not where the vulnerable code runs but which direction the failing operation faces. In CVE-2025-21333, the VSP issues operations into the I/O ring shared with the NT Kernel. The kernel writes responses back into the ring. The heap-based overflow is reached when the VSP processes the kernel's returned entries — when the VSP consumes data the kernel produced, not when the VSP receives a request from a guest. The VSP's request-handling surface is not what fails; its response-handling surface is. That is client-role under R-ROLE, regardless of which ring the code runs at.

Reverse the direction and the classification reverses with it. A different VSP vulnerability — one where a guest's IOCTL or message into the integration channel is mishandled by the VSP's inbound request handler — would be the VSP acting in server-role, and the classification would be #2. The VSP family of components straddles R-ROLE depending on which interaction is under analysis. That is not a framework defect; it is the framework doing its job. The same binary can be the locus of #2 in one path and #3 in another. The classification follows the interaction, not the file name.

The R-ROLE test for any code-imperfection bug. Ask which side of the interaction the attacker controls relative to the vulnerable code. If the attacker sends and the target handles inbound — #2. If the target fetches and the attacker influences what is fetched — #3. Apply the test to the specific failing operation, not to the component's general purpose.

§ 5 · Not #7Why this is not #7

The outcome includes execution at host SYSTEM privilege. Casual reporting will describe this as "code execution in the kernel." Under R-EXEC, the rule is the same here as it was in Part 1: #7 Malware is recorded if and only if foreign executable content is interpreted, loaded, or executed by the target.

The VSP buffer overflow corrupts existing in-kernel control state. The instruction stream that subsequently runs is the host's own already-trusted code, redirected by attacker-influenced data. As with Calif, this is data-only in the structural sense — even if the operational exploit later pivots to shellcode injection or unsigned driver loading. Those would be follow-on steps recorded as their own clusters when they occur. The #3 step itself does not introduce foreign code; it deforms the data state the target's own code executes against.

The asymmetry between the two parts of the series is worth naming. Calif is #2 data-only. The VSP case is #3 data-only. The R-EXEC rule applies identically to both. The cluster differs because of direction, not because of payload mechanics.

§ 6 · SynthesisThe series synthesis

The pair makes the directionality principle concrete in a way no single case can. Reading across:

Part 1 · Calif M5 Part 2 · Hyper-V VSP
Locus of vulnerable code XNU kernel, syscall handlers (Ring 0) vkrnlintvsp.sys, I/O ring response handling
Privilege level of vulnerable code Kernel-mode Kernel-mode
Attacker action Sends inbound syscall Influences I/O ring response via guest channel
Vulnerable component's role Server (receives request) Client (consumes response)
R-ROLE classification #2 Exploiting Server #3 Exploiting Client
Foreign executable content? No (data-only) No (data-only)
R-EXEC result #7 NOT recorded #7 NOT recorded
Boundary crossed local_user → root guest_user → host_SYSTEM
Outcome family Highest local privilege Highest local privilege
Defensive surface Kernel syscall hardening, MTE-class I/O ring response validation, caller-side bounds checks

Six of those ten rows are identical or near-identical across the two cases. The two that decide everything — attacker action and vulnerable component's role — flow from a single underlying fact: the direction of the call relative to the attacker. R-ROLE collapses the privilege fact, the locus fact, and the outcome fact into background, and reads the direction. That is the entire methodological move.

§ 7 · DefenceDefensive consequence

The series ends with the same observation Part 1 closed on, sharpened. Controls against #2 harden the receiving side: validate inbound requests, restrict syscall surface, apply hardware-assisted memory safety to server handlers. Controls against #3 harden the consuming side: validate response data shapes and lengths before processing, treat returned buffers as untrusted regardless of who returned them, narrow what the client is willing to accept and parse. Both sets of controls live at the endpoint. Neither is reachable from the perimeter. But they are different controls on different code paths — and a defence programme that conflates the two will under-invest in one of them.

The Calif case showed why DETECT and RESPOND are structurally insufficient at VC-4. The VSP case shows why PROTECT itself has two distinct surfaces — request handling and response handling — that need separate hardening. A kernel that has been made bulletproof against malicious inbound calls but trusts everything it gets back from the components it calls is still routinely #3-exploitable. R-ROLE is not just a classification rule. It is a map of where to spend defensive effort.

Canonical path for this case

... → #3 |[privilege][@guest_user→@host_SYSTEM]| + [DRE: C, I, A]

Series paths, side by side

Part 1: ... → #2 → #2 |[privilege][@local_user→@root]| + [DRE: C, I, A]
Part 2: ... → #3 |[privilege][@guest_user→@host_SYSTEM]| + [DRE: C, I, A]
#3 Exploiting Client CVE-2025-21333 R-ROLE R-EXEC SG-4 Misconception 2 Vertical Stack Series

References: TLCTC v2.0 whitepaper §13.5.7 Case Study 3 (Hyper-V VSP Vulnerability — analysing CVE-2025-21333); §13.5.8 Common Misconceptions; v2.1 §4.2.5 R-* Rules and §4.2.4 Semantic Guardrails. Vendor advisory: Microsoft Security Response Center, CVE-2025-21333 (Hyper-V NT Kernel Integration VSP heap-based buffer overflow, January 2025 Patch Tuesday). Vulnerable driver: vkrnlintvsp.sys.

TLCTC Framework · v2.1 · Published under CC BY 4.0