CVE-2026-61793
Nuxt OG Image has unauthenticated SSRF via `fonts[].path` URL parameter
Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.0.2 until 6.7.0, nuxt-og-image exposes the unauthenticated /_og/d/** route when the documented defaults security.strict = false and security.secret = "" are used, and base64url-decodes the fonts parameter through decodeOgImageParams. Attacker-controlled fonts[].path values flow through loadDefinedFonts into the font-assets/node.js binding, which performs a server-side fetch without validating the URL scheme, origin, resolved address, or redirects. This permits blind requests to loopback, private, link-local, cloud metadata, and other internal HTTP services, while differences in the outer response status and timing can reveal service reachability. Slow targets can also occupy OG image render workers for the configured fetch and render timeouts. This issue is fixed in version 6.7.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
6.9
Nuxt OG Image has unauthenticated SSRF via `fonts[].path` URL parameter
Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.0.2 until 6.7.0, nuxt-og-image exposes the unauthenticated /_og/d/** route when the documented defaults security.strict = false and security.secret = "" are used, and base64url-decodes the fonts parameter through decodeOgImageParams. Attacker-controlled fonts[].path values flow through loadDefinedFonts into the font-assets/node.js binding, which performs a server-side fetch without validating the URL scheme, origin, resolved address, or redirects. This permits blind requests to loopback, private, link-local, cloud metadata, and other internal HTTP services, while differences in the outer response status and timing can reveal service reachability. Slow targets can also occupy OG image render workers for the configured fetch and render timeouts. This issue is fixed in version 6.7.0.
- Record
CVE-2026-61793- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:L/SI:N/SA:N - Weakness
- CWE-20, CWE-441, CWE-749, CWE-918, CWE-1188
- Updated
- 2026-11-02
EUVDEUVD-2026-81672
6.9
Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.0.2 until 6.7.0, nuxt-og-image exposes the unauthenticated /_og/d/** route when the documented defaults security.strict = false and security.secret = "" are used, and base64url-decodes the fonts parameter through decodeOgImageParams. Attacker-controlled fonts[].path values flow through loadDefinedFonts into the font-assets/node.js binding, which performs a server-side fetch without validating the URL scheme, origin, resolved address, or redirects. This permits blind requests to loopback, private, link-local, cloud metadata, and other internal HTTP services, while differences in the outer response status and timing can reveal service reachability. Slow targets can also occupy OG image render workers for the configured fetch and render timeouts. This issue is fixed in version 6.7.0.
- Record
EUVD-2026-81672- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:L/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-q8hw-4fvp-9rwv
6.9
Nuxt OG Image has unauthenticated SSRF via `fonts[].path` URL parameter
Summary
nuxt-og-image exposes an unauthenticated HTTP endpoint at /_og/d/** that base64url-decodes and JSON.parses a fonts URL segment, then passes each fonts[i].path value directly into fetch() server-side without any URL validation (no scheme allowlist, no loopback/RFC1918 block, no host allowlist, no DNS rebinding mitigation).
Under the module's documented default configuration (security.strict = false, security.secret = "", restrictRuntimeImagesToOrigin = false), any caller able to reach the deployed Nuxt site can force the Nuxt server to issue arbitrary outbound GET requests to any host reachable from the server - including loopback, RFC1918 LAN, and cloud metadata services (AWS IMDS, GCE/Azure metadata, Kubernetes kubelet, internal admin panels, Redis/etcd/Consul/Vault HTTP APIs).
The chain is blind (Satori consumes the response as font bytes and silently discards non-fonts) but a robust side-channel exists: the outer HTTP status is 500 when the SSRF target returns 2xx, and 200 when it fails or returns non-2xx. This is sufficient to (a) enumerate live internal services and open ports, (b) confirm IMDSv1 reachability, and (c) detect credential issuance on environments still allowing IMDSv1.
Demonstrated end-to-end on a stock npm create nuxt@latest install with the module's documented default usage.
Detail
Endpoint registration (unauthenticated)
The module registers /_og/d/** and /_og/s/** with no authentication / Origin check / Sec‑Fetch‑Site validation:
// dist/shared/nuxt-og-image.DdbTs-xp.mjs : 5113-5133
addServerHandler({ route: "/_og/d/**", handler: resolve("./runtime/server/routes/image") })
addServerHandler({ route: "/_og/s/**", handler: resolve("./runtime/server/routes/image") })
Default security config (permissive)
// dist/shared/nuxt-og-image.DdbTs-xp.mjs : 5618-5640
security: {
strict: config.security?.strict ?? false, // <- gate disabled
secret: config.security?.secret ?? process.env.NUXT_OG_IMAGE_SECRET ?? "",
// ↑ no signature requirement
restrictRuntimeImagesToOrigin: config.security?.restrictRuntimeImagesToOrigin ?? false,
// ↑ inbound host allowlist disabled
maxQueryParamSize: config.security?.maxQueryParamSize ?? null,
renderTimeout: config.security?.renderTimeout ?? 15000,
imageFetchTimeout: config.security?.imageFetchTimeout ?? 3000,
}
The secret/signature branch is gated on secret && (truthy), so an empty string skips it entirely:
// dist/runtime/server/og-image/context.js : 49-69
const secret = runtimeConfig.security?.secret
let paramsSegment = encodedSegment
if (secret && !import.meta.dev && !import.meta.prerender) {
// signature enforcement happens HERE - but only if secret is non-empty.
// Default install: secret === "" -> entire block skipped.
}
Attacker-controlled deserialization of fonts
fonts is enumerated as a complex parameter: its value is base64url-decoded and then JSON.parsed straight into options:
// dist/runtime/shared/urlEncoding.js : 65
const COMPLEX_PARAMS = new Set(["satori","resvg","sharp","screenshot","takumi","fonts","_query","_path"])
// dist/runtime/shared/urlEncoding.js : 184-231
export function decodeOgImageParams(encoded) {
...
for (const part of parts) {
const idx = part.search(RE_SINGLE_UNDERSCORE)
if (idx === -1) continue
const alias = part.slice(0, idx)
let value = part.slice(idx + 1)
const paramName = PARAM_ALIASES[alias] || alias
if (COMPLEX_PARAMS.has(paramName)) {
try {
const json = b64Decode(value)
options[paramName] = JSON.parse(json) // <- attacker JSON survives unchanged
} catch { options[paramName] =…
- Record
GHSA-q8hw-4fvp-9rwv- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:L/SI:N/SA:N - Weakness
- CWE-20, CWE-441, CWE-749, CWE-918, CWE-1188
- Updated
- 2026-11-02
NVD
6.9
Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.0.2 until 6.7.0, nuxt-og-image exposes the unauthenticated /_og/d/** route when the documented defaults security.strict = false and security.secret = "" are used, and base64url-decodes the fonts parameter through decodeOgImageParams. Attacker-controlled fonts[].path values flow through loadDefinedFonts into the font-assets/node.js binding, which performs a server-side fetch without validating the URL scheme, origin, resolved address, or redirects. This permits blind requests to loopback, private, link-local, cloud metadata, and other internal HTTP services, while differences in the outer response status and timing can reveal service reachability. Slow targets can also occupy OG image render workers for the configured fetch and render timeouts. This issue is fixed in version 6.7.0.
- Record
CVE-2026-61793- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/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 - Weakness
- CWE-20, CWE-441, CWE-749, CWE-918, CWE-1188
- Updated
- 2026-11-02
OSV
6.9
Nuxt OG Image has unauthenticated SSRF via `fonts[].path` URL parameter
Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.0.2 until 6.7.0, nuxt-og-image exposes the unauthenticated /_og/d/** route when the documented defaults security.strict = false and security.secret = "" are used, and base64url-decodes the fonts parameter through decodeOgImageParams. Attacker-controlled fonts[].path values flow through loadDefinedFonts into the font-assets/node.js binding, which performs a server-side fetch without validating the URL scheme, origin, resolved address, or redirects. This permits blind requests to loopback, private, link-local, cloud metadata, and other internal HTTP services, while differences in the outer response status and timing can reveal service reachability. Slow targets can also occupy OG image render workers for the configured fetch and render timeouts. This issue is fixed in version 6.7.0.
- Record
CVE-2026-61793- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:L/SI:N/SA:N - Weakness
- CWE-1188, CWE-20, CWE-441, CWE-749, CWE-918
- Updated
- 2026-11-02
OSVGHSA-q8hw-4fvp-9rwv
6.9
Nuxt OG Image has unauthenticated SSRF via `fonts[].path` URL parameter
Summary
nuxt-og-image exposes an unauthenticated HTTP endpoint at /_og/d/** that base64url-decodes and JSON.parses a fonts URL segment, then passes each fonts[i].path value directly into fetch() server-side without any URL validation (no scheme allowlist, no loopback/RFC1918 block, no host allowlist, no DNS rebinding mitigation).
Under the module's documented default configuration (security.strict = false, security.secret = "", restrictRuntimeImagesToOrigin = false), any caller able to reach the deployed Nuxt site can force the Nuxt server to issue arbitrary outbound GET requests to any host reachable from the server - including loopback, RFC1918 LAN, and cloud metadata services (AWS IMDS, GCE/Azure metadata, Kubernetes kubelet, internal admin panels, Redis/etcd/Consul/Vault HTTP APIs).
The chain is blind (Satori consumes the response as font bytes and silently discards non-fonts) but a robust side-channel exists: the outer HTTP status is 500 when the SSRF target returns 2xx, and 200 when it fails or returns non-2xx. This is sufficient to (a) enumerate live internal services and open ports, (b) confirm IMDSv1 reachability, and (c) detect credential issuance on environments still allowing IMDSv1.
Demonstrated end-to-end on a stock npm create nuxt@latest install with the module's documented default usage.
Detail
Endpoint registration (unauthenticated)
The module registers /_og/d/** and /_og/s/** with no authentication / Origin check / Sec‑Fetch‑Site validation:
// dist/shared/nuxt-og-image.DdbTs-xp.mjs : 5113-5133
addServerHandler({ route: "/_og/d/**", handler: resolve("./runtime/server/routes/image") })
addServerHandler({ route: "/_og/s/**", handler: resolve("./runtime/server/routes/image") })
Default security config (permissive)
// dist/shared/nuxt-og-image.DdbTs-xp.mjs : 5618-5640
security: {
strict: config.security?.strict ?? false, // <- gate disabled
secret: config.security?.secret ?? process.env.NUXT_OG_IMAGE_SECRET ?? "",
// ↑ no signature requirement
restrictRuntimeImagesToOrigin: config.security?.restrictRuntimeImagesToOrigin ?? false,
// ↑ inbound host allowlist disabled
maxQueryParamSize: config.security?.maxQueryParamSize ?? null,
renderTimeout: config.security?.renderTimeout ?? 15000,
imageFetchTimeout: config.security?.imageFetchTimeout ?? 3000,
}
The secret/signature branch is gated on secret && (truthy), so an empty string skips it entirely:
// dist/runtime/server/og-image/context.js : 49-69
const secret = runtimeConfig.security?.secret
let paramsSegment = encodedSegment
if (secret && !import.meta.dev && !import.meta.prerender) {
// signature enforcement happens HERE - but only if secret is non-empty.
// Default install: secret === "" -> entire block skipped.
}
Attacker-controlled deserialization of fonts
fonts is enumerated as a complex parameter: its value is base64url-decoded and then JSON.parsed straight into options:
// dist/runtime/shared/urlEncoding.js : 65
const COMPLEX_PARAMS = new Set(["satori","resvg","sharp","screenshot","takumi","fonts","_query","_path"])
// dist/runtime/shared/urlEncoding.js : 184-231
export function decodeOgImageParams(encoded) {
...
for (const part of parts) {
const idx = part.search(RE_SINGLE_UNDERSCORE)
if (idx === -1) continue
const alias = part.slice(0, idx)
let value = part.slice(idx + 1)
const paramName = PARAM_ALIASES[alias] || alias
if (COMPLEX_PARAMS.has(paramName)) {
try {
const json = b64Decode(value)
options[paramName] = JSON.parse(json) // <- attacker JSON survives unchanged
} catch { options[paramName] =…
- Record
GHSA-q8hw-4fvp-9rwv- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:L/SI:N/SA:N - Weakness
- CWE-1188, CWE-20, CWE-441, CWE-749, CWE-918
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
nuxt-og-imagenpm |
6.0.2 up to 6.7.0 | 6.7.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-61793 is a medium vulnerability (CVSS 6.9) in Nuxt-modules Og-image, published September 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.50%, the 42nd percentile. Fixed in nuxt-og-image 6.7.0.
References
github.com/nuxt-modules/og-image/security/advisories/GHSA-q8hw-4fvp-9rwv
github.com/nuxt-modules/og-image/pull/637
github.com/nuxt-modules/og-image/commit/243cac2228671d3711c2bd65e300c278fcdf5a4e
github.com/nuxt-modules/og-image/releases/tag/v6.7.0
github.com/advisories/GHSA-q8hw-4fvp-9rwv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61793.json
nvd.nist.gov/vuln/detail/CVE-2026-61793
github.com/nuxt-modules/og-image
Left out here: the rest of 2 source descriptions. The JSON record has them.