highAug 25, 2026

mcp-contextforge-gateway Server-Side Template Injection (SSTI) leading to RCE

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

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.

Packagemcp-contextforge-gateway
Ecosystempip
Affected< 1.0.0
Fixed in1.0.0
mcp-contextforge-gateway Server-Side Template Injection (SSTI) leading to RCE

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.

text
{{ 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.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research