mcp-contextforge-gateway Server-Side Template Injection (SSTI) leading to RCE
An attacker with API access to mcp-contextforge-gateway can store a malicious Jinja2 template as a prompt, which executes arbitrary OS commands on the server when the prompt is later rendered.

The problem
PromptService._render_template renders user-supplied prompt templates with a plain jinja2.Environment rather than a SandboxedEnvironment. Jinja2 imposes no restrictions on attribute traversal or built-in access in this mode, so an attacker-controlled template can walk the Python object graph to reach os.popen.
The template field is stored via POST /prompts, PUT /prompts/{id}, and the bulk-register endpoint, all of which accept attacker-controlled content from any authenticated principal holding the prompts.create or prompts.update permission. The payload fires on every subsequent call to prompts/get, giving the attacker persistent RCE.
Proof of concept
A working proof-of-concept for this issue in mcp-contextforge-gateway, with the exact payload below.
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}The root cause is CWE-1336 (unsandboxed template engine) combined with CWE-94 (code injection). A plain jinja2.Environment allows full attribute traversal: self.__init__ reaches the template object's constructor, .__globals__ exposes the Python module namespace, and .__builtins__.__import__('os') loads the os module, after which os.popen executes any shell command.
The patch (commit 4d31004, PR #4072, shipped in v1.0.0) switched to jinja2.sandbox.SandboxedEnvironment and added pre-write dangerous-pattern scanning that rejects templates containing __import__, eval(, and dunder sequences before they are stored. The str.format() fallback path was also guarded so a jinja2.exceptions.SecurityError no longer silently falls through to the less-restricted fallback renderer.
The fix
Upgrade to mcp-contextforge-gateway >= 1.0.0 (pip install --upgrade mcp-contextforge-gateway). The fix migrates _render_template to jinja2.sandbox.SandboxedEnvironment and adds input-time pattern blocking for dangerous template constructs on all prompt write endpoints.
No configuration change is required after upgrading.
Related research
- high · 7.8CVE-2026-54654CVE-2026-54654: datamodel-code-generator Code Injection via Carriage Return in --extra-template-data comment
- high · 8.8CVE-2026-54653CVE-2026-54653: datamodel-code-generator Code Injection via default_factory Schema Field
- high · 8.8CVE-2026-55585CVE-2026-55585: qwed Authenticated Remote Code Execution via Unsafe SymPy parse_expr()
- critical · 9.8CVE-2026-55546CVE-2026-55546: qwed-mcp Remote Code Execution via Unsafe SymPy parse_expr()