AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

IBMGHSA-9hgc-g3w5-67cm

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

IBM

CVE-2026-53708 · Published Jun 15, 2026

Medium6.6
Fix: upgrade to 1.0.3 or later
GitHub advisory

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

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