{"id":"GHSA-489g-7rxv-6c8q","summary":"MCP Atlassian: DNS-rebinding TOCTOU bypass of the SSRF fix (CVE-2026-27826)","details":"### 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) -\u003e 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 = \u003craw header hostname\u003e` (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') -\u003e None        (getaddrinfo#1 = 93.184.216.34 global -\u003e guard PASSED)\n[CONNECT] getaddrinfo call #2 returned 169.254.169.254 -\u003e the socket connects HERE\n[PROOF ] guard validated IP 93.184.216.34 but connection targets 169.254.169.254  =\u003e SSRF on the PATCHED build\n[CONTROL] if guard SAW 169.254.169.254 at check time -\u003e blocks it correctly\n[PIN   ] validate_url_for_ssrf returns a verdict (None), NOT an IP; dependencies.py builds url=raw hostname -\u003e 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`.","aliases":["CVE-2026-73497"],"modified":"2026-09-18T11:56:00.108420927Z","published":"2026-07-10T18:01:22Z","database_specific":{"cwe_ids":["CWE-918"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-07-10T18:01:22Z","nvd_published_at":null},"references":[{"type":"WEB","url":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-489g-7rxv-6c8q"},{"type":"PACKAGE","url":"https://github.com/sooperset/mcp-atlassian"}],"affected":[{"package":{"name":"mcp-atlassian","ecosystem":"PyPI","purl":"pkg:pypi/mcp-atlassian"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.22.0"}]}],"versions":["0.1.1","0.1.10","0.1.11","0.1.12","0.1.13","0.1.14","0.1.15","0.1.16","0.1.2","0.1.3","0.1.4","0.1.6","0.1.7","0.1.8","0.1.9","0.10.0","0.10.1","0.10.2","0.10.3","0.10.4","0.10.5","0.10.6","0.11.0","0.11.1","0.11.10","0.11.11","0.11.12","0.11.2","0.11.2a2","0.11.3","0.11.4","0.11.5","0.11.6","0.11.7","0.11.8","0.11.9","0.12.0","0.13.0","0.13.1","0.14.0","0.14.1","0.14.2","0.14.3","0.15.0","0.16.0","0.16.1","0.17.0","0.18.0","0.18.1","0.19.0","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.2.6","0.20.0","0.20.1","0.21.0","0.21.1","0.3.0","0.3.1","0.4.0","0.5.0","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.7.0","0.7.1","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.9.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-489g-7rxv-6c8q/GHSA-489g-7rxv-6c8q.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N"}]}