GHSA-489g-7rxv-6c8q
MEDIUMMCP Atlassian: DNS-rebinding TOCTOU bypass of the SSRF fix (CVE-2026-27826)
Blast Radius
mcp-atlassianReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
GHSA-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.
Relationship to CVE-2026-27826 / GHSA-7r34-79r5-rcc9 (incomplete fix — please read first)
This 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).
Affected
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).
Vulnerable code
utils/urls.py validate_url_for_ssrf (≈184-205) resolves + validates, then returns a string verdict, not a pinned IP:
def validate_url_for_ssrf(url: str) -> str | None: # returns an error string or None — NO IP is pinned
...
# resolves the host, checks each resolved IP is global, then DISCARDS the IP
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.
PoC (executed — boundary demonstration)
The 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:
[CHECK ] validate_url_for_ssrf('http://rebind.attacker.example') -> None (getaddrinfo#1 = 93.184.216.34 global -> guard PASSED)
[CONNECT] getaddrinfo call #2 returned 169.254.169.254 -> the socket connects HERE
[PROOF ] guard validated IP 93.184.216.34 but connection targets 169.254.169.254 => SSRF on the PATCHED build
[CONTROL] if guard SAW 169.254.169.254 at check time -> blocks it correctly
[PIN ] validate_url_for_ssrf returns a verdict (None), NOT an IP; dependencies.py builds url=raw hostname -> NO pin
ALL PoC ASSERTIONS PASSED — DNS-rebind TOCTOU bypass demonstrated.
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.
Impact
Same 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.
Severity
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.
Remediation
Pin 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.
Dedup / freshness (re-verified live 2026-06-27)
Advisories 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/#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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | mcp-atlassian | all versions | 0.22.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for mcp-atlassian. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update mcp-atlassian to 0.22.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-489g-7rxv-6c8q is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-489g-7rxv-6c8q is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-489g-7rxv-6c8q. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-489g-7rxv-6c8q in your dependencies?
O3 detects GHSA-489g-7rxv-6c8q across PyPI dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.