{"id":"GHSA-489g-7rxv-6c8q","title":"MCP Atlassian: DNS-rebinding TOCTOU bypass of the SSRF fix (CVE-2026-27826)","summary":"MCP Atlassian: DNS-rebinding TOCTOU bypass of the SSRF fix (CVE-2026-27826)","severity":"medium","cvss":6.5,"cwe":["CWE-918"],"vendor":"mcp-atlassian","product":"mcp-atlassian","ecosystem":"pip","affected":["mcp-atlassian < 0.22.0"],"patched":["mcp-atlassian 0.22.0"],"published":"2026-07-10","updated":"2026-07-10","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-489g-7rxv-6c8q","references":[{"url":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-489g-7rxv-6c8q"},{"url":"https://github.com/advisories/GHSA-489g-7rxv-6c8q"}],"tags":["ghsa","pip"],"ingestedAt":"2026-07-10T18:05:36.791Z","slug":"GHSA-489g-7rxv-6c8q","body":"## Overview\n\n### Summary\nGHSA-7r34-79r5-rcc9's fix added `validate_url_for_ssrf`, which resolves the attacker-controlled `X-Atlassian-{Jira,Confluence}-Url` header host **once at middleware time** and trusts the result. But the outbound request is later built with the **raw hostname** and **re-resolves at connect time with no IP pinning**. An attacker-controlled rebinding DNS name returns a public IP on the guard's lookup (validation passes) and `169.254.169.254` / an internal IP on the request's lookup (the socket connects there) → unauthenticated SSRF to cloud metadata / internal services on the **patched** build.\n\n### Relationship to CVE-2026-27826 / GHSA-7r34-79r5-rcc9 (incomplete fix — please read first)\nThis is an **incomplete-fix sibling** of the published `GHSA-7r34-79r5-rcc9` (the `X-Atlassian-*-Url` header SSRF). That fix (PR #986/#1005) added a single middleware-time resolve + allowlist DNS-skip, but **does not pin the validated IP to the connection** — the fetcher re-resolves the raw hostname at connect time, so the documented SSRF mitigation is incomplete against DNS-rebinding. The other advisory `GHSA-xjgw-4wvw-rgm4` (file-write) is unrelated. Verified live (2026-06-27): neither advisory, nor any open PR/issue (`rebind`/`TOCTOU`/`getaddrinfo`/`pin` → 0), covers connect-time re-resolution. Filing as an incomplete-fix of GHSA-7r34 (not a standalone fresh SSRF).\n\n### Affected\n`src/mcp_atlassian/utils/urls.py` + `servers/main.py` + `servers/dependencies.py`, HEAD `ba72540` (PyPI `mcp-atlassian`, patched ≥0.17.0). **CWE-918** (SSRF) via **CWE-367** (TOCTOU).\n\n### Vulnerable code\n`utils/urls.py` `validate_url_for_ssrf` (≈184-205) resolves + validates, then returns a **string verdict, not a pinned IP**:\n```python\ndef validate_url_for_ssrf(url: str) -> str | None:   # returns an error string or None — NO IP is pinned\n    ...\n    # resolves the host, checks each resolved IP is global, then DISCARDS the IP\n```\n`servers/main.py:526,534` calls it once in middleware. `servers/dependencies.py:544-561` then builds the fetcher with `url = <raw header hostname>` (no pinned IP, no custom resolver / cached-getaddrinfo adapter), so the actual request re-resolves the name.\n\n### PoC (executed — boundary demonstration)\nThe PoC loads the **real** `urls.py` by path (`importlib`, `sha256` printed) and drives the genuine `validate_url_for_ssrf`, simulating the two resolutions via `getaddrinfo`:\n```\n[CHECK ] validate_url_for_ssrf('http://rebind.attacker.example') -> None        (getaddrinfo#1 = 93.184.216.34 global -> guard PASSED)\n[CONNECT] getaddrinfo call #2 returned 169.254.169.254 -> the socket connects HERE\n[PROOF ] guard validated IP 93.184.216.34 but connection targets 169.254.169.254  => SSRF on the PATCHED build\n[CONTROL] if guard SAW 169.254.169.254 at check time -> blocks it correctly\n[PIN   ] validate_url_for_ssrf returns a verdict (None), NOT an IP; dependencies.py builds url=raw hostname -> NO pin\nALL PoC ASSERTIONS PASSED — DNS-rebind TOCTOU bypass demonstrated.\n```\n**Honest scope of the PoC:** this is a **boundary** demonstration — it proves the structural TOCTOU (the guard validates an IP it then discards; the connection re-resolves an unpinned hostname). It does **not** demonstrate a live end-to-end SSRF on a running server; that additionally requires an attacker-controlled fast-rebinding authoritative DNS responder winning the resolve→connect window. Flagging this explicitly rather than overclaiming.\n\n### Impact\nSame as parent GHSA-7r34 (unauth read of cloud-metadata IAM creds / internal-service reach), reachable again on the patched version. The `X-Atlassian-*-Url` headers are processed in `UserTokenMiddleware` before fetcher creation, so an unauthenticated/low-priv caller controls the host.\n\n### Severity\n**High — CVSS v3.1 `AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N` ≈ 7.x**, aligned to the parent (8.2) with `AC:H` for the rebinding-race precondition. Honest caveat (above): the executed PoC proves the missing IP-pin structurally; a live exploit additionally needs an attacker rebinding-DNS. Not Critical.\n\n### Remediation\nPin the connection to the IP that `validate_url_for_ssrf` validated: use a custom resolver / cached-`getaddrinfo` `requests`-adapter (or pass the validated IP with a `Host` header), so the connect cannot re-resolve to a different address.\n\n### Dedup / freshness (re-verified live 2026-06-27)\nAdvisories `GHSA-7r34-79r5-rcc9` (original header SSRF this bypasses) + `GHSA-xjgw-4wvw-rgm4` (file-write, unrelated). Neither covers connect-time re-resolution / rebinding. PR [#986](https://github.com/sooperset/mcp-atlassian/pull/986)/[#1005](https://github.com/sooperset/mcp-atlassian/pull/1005) (the fix) add a single middleware-time resolve + allowlist DNS-skip, no pinning. `gh search prs/issues` for rebind/TOCTOU/getaddrinfo/pin → 0. First-party code. **Fresh** at HEAD `ba72540`.\n\n## Affected packages\n\n- `mcp-atlassian < 0.22.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `mcp-atlassian 0.22.0`","depth":"sunlit","depthScore":36,"depthScoreParts":{"impact":35.8,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}