{"id":"GHSA-c6q7-wqw2-h65w","summary":"NationalSecurityAgency/emissary has an incomplete fix for CVE-2026-35571","details":"### Summary\n\nThe URL validation regex `^(https?:/)?/.*` added to emissary's `NavAction.java` allows protocol-relative URLs (`//evil.com/...`) and single-slash scheme variants (`http:/evil.com`) to bypass validation, enabling SSRF and open redirect attacks.\n\n### Affected Package\n\n- **Ecosystem:** Maven\n- **Package:** NationalSecurityAgency/emissary\n- **Affected versions:** \u003c e2078417464b\n- **Patched versions:** \u003e= e2078417464b\n\n### Severity\n\nMedium\n\n### CWE\n\nCWE-918 — Server-Side Request Forgery (SSRF)\n\n### Details\n\nThe fix in `NavAction.java` (line 47) introduces a `VALID_LINK` pattern:\n\n```java\nPattern.compile(\"^(https?:/)?/.*\")\n```\n\nThis regex is intended to restrict navigation links to relative paths and HTTP/HTTPS URLs. However, it has multiple bypass vectors.\n\nProtocol-relative URLs (`//evil.com/...`): The group `(https?:/)?` is optional, so the pattern reduces to `//.*` when the group is absent. This matches `//evil.com/steal?c=SESSION`, which browsers resolve to the current page's protocol plus the attacker's host.\n\nSingle-slash scheme (`http:/evil.com`): The regex group captures `http:/` as a valid match, and browsers normalize `http:/evil.com` to `http://evil.com`, creating an open redirect to the attacker's server.\n\nProtocol-relative to cloud metadata (`//169.254.169.254/...`): This enables SSRF to cloud instance metadata endpoints, which is particularly dangerous in AWS/GCP environments where IAM credentials can be retrieved.\n\nThe pre-fix code had no validation at all, accepting any URL. The fix blocks `javascript:`, `ftp:`, and `data:` URIs, but the protocol-relative and single-slash bypasses remain exploitable.\n\n### PoC\n\n```python\n\"\"\"\nCVE-2026-35571 - NationalSecurityAgency/emissary SSRF via NavAction\n\nThe fix in commit e2078417464b adds URL scheme validation using:\n    Pattern.compile(\"^(https?:/)?/.*\")\n\nThis allows protocol-relative URLs (//evil.com) and single-slash variants\n(http:/evil.com) to bypass the validation.\n\nTested against: emissary@0d4dc56 (pre-fix) and patch e207841\nExtracted from: src/main/java/emissary/server/mvc/NavAction.java:47\n\"\"\"\n\nimport re\nimport sys\n\nFIXED_PATTERN = re.compile(r\"^(https?:/)?/.*\")\n\n\ndef vulnerable_accepts(link: str) -\u003e bool:\n    \"\"\"Pre-fix behaviour: no validation, everything passes.\"\"\"\n    return True\n\n\ndef fixed_accepts(link: str) -\u003e bool:\n    \"\"\"Post-fix behaviour: must match VALID_LINK pattern.\"\"\"\n    return bool(FIXED_PATTERN.match(link))\n\n\nTEST_VECTORS = [\n    (\"javascript: XSS payload\",\n     \"javascript:alert(document.cookie)\", True),\n    (\"ftp: scheme\",\n     \"ftp://evil.com/exfil\", True),\n    (\"data: URI\",\n     \"data:text/html,\u003cscript\u003ealert(1)\u003c/script\u003e\", True),\n\n    (\"Relative path\",\n     \"/path/to/resource\", False),\n    (\"HTTP URL\",\n     \"http://trusted.internal:8080/api\", False),\n    (\"HTTPS URL\",\n     \"https://trusted.internal/api\", False),\n\n    (\"Protocol-relative URL to attacker host (//evil.com)\",\n     \"//evil.com/steal?c=SESSION\", True),\n    (\"Protocol-relative with embedded credentials\",\n     \"//user:pass@evil.com/path\", True),\n    (\"Protocol-relative to cloud metadata (//169.254.169.254)\",\n     \"//169.254.169.254/latest/meta-data/\", True),\n    (\"Backslash trick (\\\\\\\\evil.com)\",\n     \"\\\\\\\\evil.com/path\", True),\n    (\"Tab in scheme (java\\\\tscript:...)\",\n     \"java\\tscript:alert(1)\", True),\n    (\"Path starting with attacker-like host (/evil.com)\",\n     \"/evil.com\", False),\n    (\"Triple slash (///evil.com)\",\n     \"///evil.com/etc/passwd\", True),\n    (\"http:/ single-slash to attacker host\",\n     \"http:/evil.com/steal\", True),\n    (\"https:/ single-slash to attacker host\",\n     \"https:/evil.com/steal\", True),\n    (\"URL-encoded javascript scheme (%6a...)\",\n     \"%6aavascript:alert(1)\", True),\n    (\"CRLF injection in path\",\n     \"/legit\\r\\nLocation: http://evil.com\", True),\n]\n\n\ndef main():\n    print(\"=\" * 72)\n    print(\"CVE-2026-35571 -- Emissary NavAction SSRF / Open-Redirect PoC\")\n    print(\"Pattern under test: ^(https?:/)?/.*\")\n    print(\"=\" * 72)\n    print()\n\n    bypasses_found = []\n    results_table = []\n\n    for desc, url, should_block in TEST_VECTORS:\n        vuln_result = vulnerable_accepts(url)\n        fixed_result = fixed_accepts(url)\n\n        if should_block and fixed_result:\n            status = \"BYPASS\"\n            bypasses_found.append((desc, url))\n        elif should_block and not fixed_result:\n            status = \"BLOCKED (good)\"\n        elif not should_block and fixed_result:\n            status = \"ALLOWED (good)\"\n        elif not should_block and not fixed_result:\n            status = \"FALSE-POS\"\n        else:\n            status = \"UNKNOWN\"\n\n        results_table.append((desc, url, vuln_result, fixed_result, status))\n\n    for desc, url, vuln, fixed, status in results_table:\n        display_url = repr(url) if len(url) \u003e 50 else url\n        print(f\"[{status:\u003e15}]  {desc}\")\n        print(f\"{'':\u003e17}  URL: {display_url}\")\n        print(f\"{'':\u003e17}  pre-fix={vuln}  post-fix={fixed}\")\n        print()\n\n    print(\"=\" * 72)\n    print(f\"Total vectors tested : {len(TEST_VECTORS)}\")\n    print(f\"Bypasses found       : {len(bypasses_found)}\")\n    print()\n\n    if bypasses_found:\n        print(\"VULNERABILITY CONFIRMED -- the following vectors bypass the fix:\")\n        print()\n        for desc, url in bypasses_found:\n            print(f\"  - {desc}\")\n            print(f\"    URL: {repr(url)}\")\n        print()\n        print(\"Key finding: The regex ^(https?:/)?/.* allows protocol-relative\")\n        print(\"URLs (//evil.com/...) because the optional group (https?:/) can be\")\n        print(\"absent, leaving //.* which matches //attacker.com. This enables\")\n        print(\"SSRF to arbitrary external hosts via navigation links.\")\n        print()\n        print(\"Additionally, http:/evil.com and https:/evil.com match the regex\")\n        print(\"(group 1 captures 'http:/' or 'https:/') and browsers normalise\")\n        print(\"these to http://evil.com, enabling open redirect / SSRF.\")\n        sys.exit(0)\n    else:\n        print(\"No bypasses found -- fix appears complete.\")\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n\n```\n\n**Steps to reproduce:**\n1. Run `python3 poc.py`\n2. Observe that protocol-relative and single-slash URLs bypass the regex\n\n**Expected output:**\n```\nVULNERABILITY CONFIRMED\nProtocol-relative URLs (//evil.com/) and single-slash variants (http:/evil.com) bypass the regex validation.\n```\n\n### Impact\n\nAn attacker can craft navigation links that bypass URL validation to redirect users to attacker-controlled servers (open redirect) or make the emissary server issue requests to internal services (SSRF). The `//169.254.169.254/latest/meta-data/` bypass is particularly dangerous in cloud environments, enabling retrieval of IAM credentials.\n\n### Suggested Remediation\n\nReplace the regex with one that explicitly rejects protocol-relative URLs. Alternatively, parse the URL with `java.net.URI` and validate the scheme and host against an allowlist rather than relying on regex.\n\n### References\n\n- Incomplete fix commit: https://github.com/NationalSecurityAgency/emissary/commit/e2078417464b9004620dde28dcbca2f73ea06c13\n- Original CVE: CVE-2026-35571","aliases":["CVE-2026-54442"],"modified":"2026-10-30T23:00:04.359105890Z","published":"2026-10-30T22:51:19Z","database_specific":{"github_reviewed_at":"2026-10-30T22:51:19Z","nvd_published_at":null,"cwe_ids":["CWE-918"],"severity":"MODERATE","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/NationalSecurityAgency/emissary/security/advisories/GHSA-c6q7-wqw2-h65w"},{"type":"WEB","url":"https://github.com/NationalSecurityAgency/emissary/security/advisories/GHSA-cpm7-cfpx-3hvp"},{"type":"WEB","url":"https://github.com/NationalSecurityAgency/emissary/commit/e2078417464b9004620dde28dcbca2f73ea06c13"},{"type":"PACKAGE","url":"https://github.com/NationalSecurityAgency/emissary"},{"type":"WEB","url":"https://github.com/NationalSecurityAgency/emissary/releases/tag/8.39.0"}],"affected":[{"package":{"name":"gov.nsa.emissary:emissary","ecosystem":"Maven","purl":"pkg:maven/gov.nsa.emissary/emissary"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"8.51.0"}]}],"versions":["8.0.0","8.1.0","8.10.0","8.11.0","8.11.1","8.12.0","8.13.0","8.14.0","8.15.0","8.16.0","8.17.0","8.18.0","8.19.0","8.19.1","8.2.0","8.20.0","8.21.0","8.22.0","8.23.0","8.24.0","8.25.0","8.26.0","8.27.0","8.28.0","8.29.0","8.3.0","8.30.0","8.31.0","8.32.0","8.33.0","8.34.0","8.35.0","8.36.0","8.37.0","8.38.0","8.39.0","8.4.0","8.40.0","8.41.0","8.42.0","8.43.0","8.44.0","8.45.0","8.46.0","8.47.0","8.48.0","8.49.0","8.5.0","8.50.0","8.6.0","8.7.0","8.7.1","8.8.0","8.9.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-c6q7-wqw2-h65w/GHSA-c6q7-wqw2-h65w.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N"}]}