Aug 25, 2026·7 min read·2 visits
Unsandboxed Jinja2 rendering and an unsafe str.format() fallback in mcp-contextforge-gateway prior to v1.0.0 permit remote code execution via crafted prompt templates.
A Server-Side Template Injection (SSTI) leading to Remote Code Execution (RCE) was discovered in the mcp-contextforge-gateway package before version 1.0.0. The vulnerability stems from an unsandboxed Jinja2 template rendering environment combined with an unsafe fallback mechanism using Python's native str.format() function. Attackers with template modification access could bypass static regex filters to execute arbitrary commands on the hosting platform.
The vulnerability identified as GHSA-VWF3-4XXJ-QG6H resides in the prompt templating implementation of mcp-contextforge-gateway, a component of the mcp-context-forge repository. This package provides gateway services within the ContextForge ecosystem, specifically managing system integration and prompt-based interactions. The core functionality allows users to register, manage, and render templated prompts to interact with backend models.
Because the gateway exposes APIs to register custom templates, the template rendering engine represents a significant attack surface. The system accepts user-controlled inputs during template registration and subsequent rendering operations. If the template input is not strictly validated and sanitized, it is processed directly by the rendering pipeline, exposing the application to injection attacks.
This flaw is classified under CWE-1336 (Improper Neutralization of Special Elements Used in a Template Engine) and CWE-94 (Improper Control of Generation of Code). The ultimate impact is unauthenticated or authenticated remote code execution (RCE) on the underlying host, depending on the network configuration and user authorization controls governing access to the prompt service.
The root cause of this vulnerability lies in the combination of an unsandboxed Jinja2 template environment, an inadequate regex-based blocklist, and an unsafe fallback mechanism in the rendering code. The primary engine responsible for rendering prompt templates was initialized using the standard jinja2.Environment class. This environment does not restrict access to Python runtime objects, allowing an executed template to traverse the Python object hierarchy and invoke arbitrary system APIs.
To prevent template injection, developers implemented a case-insensitive regex pattern scan called CONTENT_BLOCKED_TEMPLATE_PATTERNS. This static filter scanned raw template string input for dunder strings such as __class__ and __import__. However, static pattern-matching is fundamentally insufficient to block Jinja2 injection due to the engine's expressive parsing options. Attackers can bypass these filters by using hex-encoded strings, string concatenation, or attributes of objects like the request parameter to reconstruct blocked keywords at runtime.
Additionally, the rendering implementation in PromptService._render_template contained a nested try-except error handling block designed to handle rendering failures. If the Jinja2 compilation or execution failed, the engine caught the exception and automatically fell back to Python's built-in str.format() method to format the template. Since Python's native formatting engine also supports property access and arbitrary object traversal, this fallback reintroduced the vulnerability, providing an alternative execution pathway even if the primary Jinja2 rendering path was obstructed.
The vulnerability was located in mcpgateway/services/prompt_service.py where the Jinja2 environment was initialized and templates were rendered. Let us examine the vulnerable implementation of the environment setup and the rendering function.
# VULNERABLE CODE PATH
from jinja2 import Environment, select_autoescape
def _get_jinja_env() -> Environment:
global _JINJA_ENV
if _JINJA_ENV is None:
# Initializes a standard, unsandboxed Jinja2 Environment
_JINJA_ENV = Environment(
autoescape=select_autoescape(["html", "xml"]),
trim_blocks=True,
lstrip_blocks=True,
)
return _JINJA_ENV
def _render_template(self, template: str, arguments: Dict[str, str]) -> str:
try:
jinja_template = _compile_jinja_template(template)
return jinja_template.render(**arguments)
except Exception:
try:
# Unsafe fallback to python str.format() when Jinja rendering fails
return template.format(**arguments)
except Exception:
raise PromptError("Failed to format template")To resolve this issue, the patch transitioned the application to Jinja2's SandboxedEnvironment and hardened the exception handlers to block unsafe format fallback operations. It also introduced a multi-step pre-flight validator to inspect templates before saving them to the database.
# PATCHED CODE PATH
from jinja2.sandbox import SandboxedEnvironment
from jinja2.exceptions import SecurityError as JinjaSecurityError
def _get_jinja_env() -> SandboxedEnvironment:
global _JINJA_ENV
if _JINJA_ENV is None:
# Transition to SandboxedEnvironment blocks unsafe attribute access
_JINJA_ENV = SandboxedEnvironment(
autoescape=select_autoescape(["html", "xml"]),
trim_blocks=True,
lstrip_blocks=True,
)
return _JINJA_ENV
def _render_template(self, template: str, arguments: Dict[str, str]) -> str:
try:
jinja_template = _compile_jinja_template(template)
return jinja_template.render(**arguments)
except JinjaSecurityError as sec_err:
# Intercept Jinja security exceptions and terminate execution immediately
raise PromptError(f"Failed to render template: sandbox rejected unsafe operation ({sec_err})")
except Exception:
try:
# Fallback only executed for non-security related compilation errors
return template.format(**arguments)
except Exception:
raise PromptError("Failed to format template")While the transition to SandboxedEnvironment represents a significant security improvement, its complete security depends on the configuration of the sandbox. The default Jinja2 sandbox blocks attributes prefixed with underscores and certain methods like __import__. However, complex deployment setups with custom helper filters must ensure that they do not expose sensitive object references to the sandboxed scope.
Exploitation of this vulnerability requires the ability to register or update prompt templates in the application. Depending on the gateway's access control configurations, this could be achieved by an authenticated user with low privileges or an administrator, or via an unauthenticated endpoint if access controls are misconfigured. Once the attacker has access to template generation interfaces, they submit a malicious template payload.
To bypass the static regex scanners that block direct strings like __class__, the attacker must use obfuscated syntax. One primary mechanism uses Jinja2's attr filter paired with hexadecimal encoding. Because the regex scanner only inspects the literal template text, it does not evaluate the hex-encoded sequences at rest, allowing the payload to pass validation checks and enter the template database.
{# Payload to retrieve the class hierarchy and locate subclass references #}
{{ ""|attr("\\x5f\\x5fclass\\x5f\\x5f")|attr("\\x5f\\x5fmro\\x5f\\x5f") }}Once the template is saved, the attacker triggers its execution via the template rendering endpoints. During execution, the unsandboxed Jinja2 environment parses the hexadecimal strings, resolves the attributes, traverses the MRO hierarchy to find the <class 'subprocess.Popen'> or <module 'os'> reference, and executes arbitrary shell commands on the host system. If an error is triggered during execution, the system may fall back to the str.format() interpreter, which allows similar object-traversal exploitation via native Python format structures.
The impact of successful exploitation is critical, potentially resulting in full remote code execution under the security context of the mcp-contextforge-gateway application process. In containerized environments, this level of access allows the attacker to execute shell commands, read application configuration secrets, retrieve internal API keys, and interact with other local containers or backend cloud services.
If the gateway process is configured to run with administrative or root privileges on the host system, the attacker can leverage the execution context to achieve persistence, escalate privileges, and compromise the host operating system. The vulnerability poses a significant risk to confidentiality, integrity, and availability because all data handled by the gateway can be read, modified, or destroyed by the attacker.
No official CVE identifier is assigned to this vulnerability because it was cataloged directly under the GitHub Security Advisory (GHSA) program as GHSA-VWF3-4XXJ-QG6H. However, its severity aligns with a CVSS v3.1 base score of 9.8 (Critical) assuming unauthenticated vector access, or 8.8 (High) if authentication is strictly enforced. The vulnerability has not yet been reported as actively exploited in the wild or listed in the CISA KEV catalog.
To remediate this vulnerability, organizations must upgrade mcp-contextforge-gateway to version 1.0.0 or later. This version completely replaces the default Jinja2 environment with a secure SandboxedEnvironment and updates the error handling code to abort execution upon encountering sandbox violations, preventing unsafe fallbacks to str.format().
In addition to upgrading the package, administrators must verify that template security configurations are enabled in their environment files. Confirming that CONTENT_VALIDATE_PROMPT_TEMPLATES is set to true enforces the multi-layer pre-flight template validation pipeline. This validation pipeline runs an AST syntax analysis on templates and rejects structural anomalies before saving them to the backend database.
# Recommended Security Settings
CONTENT_VALIDATE_PROMPT_TEMPLATES=true
CONTENT_PATTERN_DETECTION_ENABLED=true
CONTENT_PATTERN_VALIDATION_MODE=strictSecurity teams should monitor application logging facilities for attempts to execute restricted template expressions. The patched code emits structured logs when template validation or sandbox security policies are violated. Alerts should be established to identify multiple rapid template parsing exceptions, which often indicate exploratory fuzzing or exploit payload testing.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
mcp-contextforge-gateway IBM | < 1.0.0 | 1.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1336, CWE-94 |
| Attack Vector | Network (with template registration privileges) |
| CVSS v3.1 Score | 9.8 (Critical) |
| Exploit Status | Proof-of-Concept |
| Patch Status | Patched in v1.0.0 |
| Vulnerability Class | Server-Side Template Injection (SSTI) |
The product uses a template engine but does not properly neutralize special elements in the template before it is rendered, allowing execution of arbitrary code.
The self-hosted HTTP transport mode of @arikusi/deepseek-mcp-server (an MCP server for DeepSeek V4) exposes its JSON-RPC endpoint (POST /mcp) without authentication in versions 1.4.2 through 1.7.0. Unauthenticated clients can establish Model Context Protocol sessions and invoke tools, consuming the host's configured DeepSeek API key.
CVE-2026-55596 is a critical DOM-based Cross-Site Scripting (XSS) vulnerability in the Plate rich-text editor framework (specifically within the @platejs/media package). The issue stems from an optimization fast-path that short-circuits safety parsing if a provider or source URL is already declared on an element. Consequently, serialized documents carrying malicious javascript: URLs bypass protocol sanitization and are loaded directly into iframe elements, leading to code execution.
The npm package 'pickem' is vulnerable to a terminal escape-sequence injection (CWE-150). Unsanitized terminal outputs allow attackers to execute arbitrary shell commands via clipboard hijacking (OSC 52) or manipulate terminal displays through Control Sequence Introducers (CSI).
CVE-2026-55537 is a server-side request forgery (SSRF) and time-of-check time-of-use (TOCTOU) vulnerability in the PraisonAI multi-agent framework before version 4.6.58. The flaw exists in the job-submission component's webhook URL validation logic. When DNS resolution fails during verification, the application fails open, enabling attackers to register unresolvable URLs. When a completed job triggers the webhook, the application performs a fresh DNS resolution that attackers can manipulate to target internal resources.
Prior to version 5.0.8, django CMS fails to respect dynamically declared Vary HTTP headers in its internal page cache. This allows remote attackers to bypass authorization, leak sensitive information across user sessions, or poison the page cache by sending requests with custom headers.
A security vulnerability in the github.com/gorilla/websocket Go library allows remote attackers to predict client-to-server frame masking keys. This occurs because the library generates 32-bit mask keys using Go's non-cryptographically secure pseudo-random number generator (math/rand). Predicting these keys enables adversaries to bypass proxy-based security protections, facilitating HTTP request smuggling and cache poisoning attacks.