Skip to content
IBMGHSA-9hgc-g3w5-67cm

DNS TOCTOU race condition causes SSRF protection bypass (`/admin/gateways/test`)

Medium6.6CVE-2026-53708 · Published Jun 15, 2026

## 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 indepen...

GitHub advisory

Affected versions

PackageAffectedFixed in
mcp-contextforge-gateway
PyPI
< 1.0.31.0.3
Details and references

## 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 1. `MCPGATEWAY_ADMIN_API_ENABLED=true` (not the default; must be explicitly enabled by an operator). 2. The attacker holds a credential with explicit `gateways.read` permission 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

CVSS 3.1
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:L/A:N
Severity from
GitHub (reviewed advisory)
Weakness
CWE-350, CWE-367, CWE-918

More IBM advisories

All IBM
Advisory
IBM Db2: remote code execution
High7.8Jul 17
IBM API Connect: SQL injection
Critical9.1Jul 8
IBM API Connect: attacker could gain unauthorized access to the application
High8.1Jul 8
IBM: template injection
HighJun 15
Stored XSS Vulnerability Report – mcp-context-forge
MediumJun 15
IBM: improper authentication
Critical9.8Jun 15

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.