← Back to Attack Research

NVIDIAScape: a three-line Dockerfile that escapes the NVIDIA Container Toolkit to host root

CVE-2025-23266 turns an attacker-supplied container image into host root on multi-tenant GPU nodes. The privileged OCI createContainer hook inherits environment variables from the image and runs out of the container root filesystem, so an LD_PRELOAD pointed at a bundled shared library loads attacker code with the hook's privileges. Here is the mechanism, why Adjacent plus low-privilege still rates a 9.0, and how to close it.

NVIDIAScape, tracked as CVE-2025-23266 and disclosed by Wiz Research, is a critical flaw in the NVIDIA Container Toolkit and the NVIDIA GPU Operator: a container image whose Dockerfile is barely three lines long can break out of its container and run code as root on the underlying host. It is not a kernel bug and not a namespace escape. It is a search-path problem, CWE-426, in how the toolkit's privileged startup hook resolves and loads a shared library. NVIDIA rates it 9.0 Critical. It is not on CISA's Known Exploited Vulnerabilities catalog, and there is no confirmed in-the-wild exploitation. The exposure is real anyway, because the exact deployments where it matters most are the ones built to run other people's containers on shared GPUs.

Let us be precise about what an attacker needs, because the precision is the point. The CVSS vector is AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H. That is Adjacent attack vector and Low privileges required, not Network and not unauthenticated. To trigger NVIDIAScape you must be able to get a container image scheduled onto a GPU node, which means you are already a tenant of the platform, or you can convince the platform to run your image. That sounds like a meaningful barrier until you remember what a multi-tenant GPU cloud is for: it exists specifically to accept and run untrusted container images from many customers on shared hardware. On those platforms the precondition is not a hurdle, it is the product. That is why a bug that needs you to be able to run an image is still a 9.0. Verifiable security.

The three lines, and the mechanism underneath them

When the container runtime starts a workload, it fires a sequence of OCI lifecycle hooks. The NVIDIA Container Toolkit registers a createContainer hook, a privileged helper that runs on the host to inject GPU devices, driver libraries, and CDI configuration into the new container before the workload's own process starts. Two properties of how that hook runs are what make NVIDIAScape work.

First, the hook process inherits environment variables from the container image unless the runtime explicitly strips them. A container image gets to declare environment variables through ENV lines in its Dockerfile, and those variables were flowing into the privileged hook's own process environment. Second, the hook runs with its working directory set to the container's own root filesystem. So /proc/self/cwd, from the hook's point of view, resolves to a path the attacker fully controls: the root of their own image.

Put those two facts together and the exploit writes itself. The attacker sets LD_PRELOAD in the image to a path relative to /proc/self/cwd, ships a matching malicious shared library at that path inside the image, and waits for the runtime to fire the hook. The dynamic linker in the privileged hook process reads the inherited LD_PRELOAD, resolves it through the working directory the attacker owns, and loads the attacker's library, executing its constructor with the hook's elevated privileges. That is the untrusted search path, CWE-426: a trusted, privileged process resolving a library name against a location an untrusted party controls.

NVIDIAScape (CVE-2025-23266) createContainer hook abuse attacker image (ENV LD_PRELOAD + bundled poc.so) | | scheduled onto a GPU node as a normal tenant workload v runtime fires OCI createContainer hook (PRIVILEGED, runs on host) | | (1) inherits ENV from the image --> LD_PRELOAD is honored | (2) cwd = container rootfs --> /proc/self/cwd = attacker fs v dynamic linker loads /proc/self/cwd/poc.so as the privileged hook | v attacker code executes as ROOT on the host [ S:C scope change ] CWE-426 Untrusted Search Path | NOT a kernel or namespace escape

The privileged hook honors an image-supplied LD_PRELOAD and resolves it against a filesystem the attacker owns. Illustrative of the disclosed mechanism, not a runnable payload.

An illustrative image is about as short as the disclosure describes. Conceptually it is a base image, one ENV line that points LD_PRELOAD at a library reachable through the working directory, and a copy of that library into the image so it lands where the hook will look. Three meaningful lines. We are not publishing a working library or a copy-paste exploit here, because we do not need to. The lesson is in the shape, not in a weaponized artifact: a privileged host process was trusting inputs, both the environment and the working directory, that the least-trusted party in the system got to define.

Why Adjacent and low-privilege is not reassurance here

It is tempting to read AV:A and PR:L as mitigating factors and move the ticket down the queue. Resist that on GPU infrastructure specifically. The Adjacent vector and the low-privilege requirement describe a tenant who can run a container on a node. In a single-tenant cluster you own, where every image comes from your own registry and your own build pipeline, that population is small and trusted, and the practical risk is lower. In a multi-tenant GPU cloud, a managed notebook service, a model-training platform that runs customer jobs, a shared research cluster, or any environment where a CI system builds and runs images from pull requests, that population is by definition untrusted and often anonymous. The privilege the vector asks for is exactly the privilege those platforms hand out on signup.

On a GPU cloud, the precondition for NVIDIAScape is not a barrier. Running an untrusted container image on shared hardware is the entire business model.

The scope change in the vector, S:C, is the other half of the story. A container escape does not stay inside the security authority that contained it. Root on the host means authority over every other tenant's container on that node, their GPU memory and workloads, any secrets or credentials mounted on the host, the kubelet or node agent, and the lateral path from one compromised node into the rest of the cluster. One customer's three-line image becomes a position of control over every neighbor sharing that machine. For a platform whose value proposition is that tenants are isolated from each other, that is close to the worst case.

Getting the version story right

Affected and fixed versions matter for this one, and the public sources do not all agree, so we are going to be explicit rather than tidy.

For the NVIDIA Container Toolkit, versions up to and including 1.17.7 are affected, and the fix is in 1.17.8. There is a nuance worth carrying: for toolkit versions earlier than 1.17.5, the exposure is described as applying to CDI mode specifically, whereas the later affected versions are exposed more broadly. If you are pinning a build, treat anything at or below 1.17.7 as needing the upgrade and confirm your mode of operation rather than assuming CDI is not in play.

For the NVIDIA GPU Operator, the sources diverge, and we are flagging it rather than silently picking a number. NVD records the affected GPU Operator as up to and including 25.3.0. Wiz Research, the disclosing party, describes the affected range as up to and including 25.3.1. That is a one-patch-version discrepancy that changes whether a 25.3.1 deployment reads as affected or already fixed, so it is not cosmetic. Do not resolve it from a blog, this one included. Confirm the exact fixed GPU Operator version against NVIDIA's own official security bulletin for CVE-2025-23266, which is the authoritative source for the vendor's fixed-version statement, and validate the running version in your own cluster before you decide you are clear.

Practitioner note: when a disclosing researcher and the NVD entry disagree on an affected range by a single patch release, the vendor's security bulletin is the tiebreaker, not whichever number you saw first. Record which source you acted on so the decision is auditable later.

None of this is a claim of active exploitation. NVIDIAScape came out of Wiz Research as a coordinated disclosure, it is not currently listed on CISA's KEV catalog, and we have seen no confirmed in-the-wild use. The urgency is not "attackers are using this right now." The urgency is that the precondition is trivially satisfied on exactly the platforms this affects, the payload is small and well-understood from the disclosure, and the fix is a straightforward upgrade. That combination is one you close before it is weaponized, not after.

What to do about it

Close NVIDIAScape on GPU nodes that run untrusted images

How Celvex catches this

Find. Prove. Fix. Verify.

Find

A read-only sweep enumerates GPU nodes, fingerprints the installed Container Toolkit and GPU Operator versions, and flags any at or below the affected bounds, reconciling the NVD and vendor version statements instead of trusting one source.

Prove

A confirmed affected, untrusted-image-accepting node becomes an Ed25519-signed Proof Capsule carrying the node, the toolkit and Operator version evidence, and the CVE-2025-23266 match, reproducible offline by you or your auditor.

Fix

The capsule's remediation block names the steps: upgrade the toolkit to 1.17.8, move the GPU Operator to the bulletin-named fixed release, and gate image submission with provenance and admission control.

Verify

A fresh sweep confirms the patched versions and that the privileged hook no longer honors an image-supplied search path. The finding closes and the verified-fix event is recorded.

The durable lesson in NVIDIAScape is not about GPUs at all. It is that a privileged process on the host was trusting two inputs, its inherited environment and its working directory, that the least-trusted party in the system got to define. The environment came from the image. The working directory was the image's own filesystem. Neither should have been load-bearing for a root-privileged hook, and the fix is to stop the hook from resolving anything against attacker-controlled state. Anywhere a privileged helper runs on behalf of an untrusted workload, the boundary question is the same: which of the inputs this process trusts can the workload set. Answer that, and search-path escapes like this one stop being surprises.

Verifiable security. Find it. Prove it. Fix it. Verify the fix held. That is what we ship.

Sources

Do any of your GPU nodes still run an affected NVIDIA Container Toolkit?

Free Exposure Check, no signup required. We enumerate your GPU nodes, fingerprint the Container Toolkit and GPU Operator versions against CVE-2025-23266, and ship a signed Proof Capsule for the highest-confidence finding.

Run a Free Scan →