CVE-2026-44589
nuxt-og-image SSRF — bypass of GHSA-pqhr-mp3f-hrpp / v6.2.5 fix (IPv6 + redirect)
CVE-2026-44589 is a low vulnerability (CVSS 3.7) in Nuxt-modules Og-image, published May 7, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.17%, the 6th percentile. Fixed in nuxt-og-image 6.4.9.
Nuxt OG Image generates OG Images with Vue templates in Nuxt. The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. It has an incomplete IPv6 prefix list and is missing redirect re-validation. This vulnerability is fixed in 6.4.9.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
3.7
nuxt-og-image SSRF — bypass of GHSA-pqhr-mp3f-hrpp / v6.2.5 fix (IPv6 + redirect)
Nuxt OG Image generates OG Images with Vue templates in Nuxt. The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. It has an incomplete IPv6 prefix list and is missing redirect re-validation. This vulnerability is fixed in 6.4.9.
- Record
CVE-2026-44589- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2026-30364
3.7
Nuxt OG Image generates OG Images with Vue templates in Nuxt. The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. It has an incomplete IPv6 prefix list and is missing redirect re-validation. This vulnerability is fixed in 6.4.9.
- Record
EUVD-2026-30364- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-c2rm-g55x-8hr5
3.7
nuxt-og-image SSRF — bypass of GHSA-pqhr-mp3f-hrpp / v6.2.5 fix (IPv6 + redirect)
Summary
The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. The patch advisory states "Decimal/hexadecimal IP encoding bypasses are also handled" — that part is true (Node's WHATWG URL parser canonicalizes those forms before validation), but the v6.2.5 implementation misses two independent surfaces in the latest release 6.4.8:
-
IPv6 prefix list is incomplete. The IPv6 branch checks only
bare === "::1" || startsWith("fc") || startsWith("fd") || startsWith("fe80"). It misses:[::ffff:7f00:1]— IPv6-mapped IPv4 loopback in pure-hex form (RE_MAPPED_V4 regex requires dotted-quad). Reaches 127.0.0.1 on a single-stack-IPv4 host with no other primitive needed.[fec0::/10](RFC 3879 site-local — deprecated but still routable on legacy networks)[5f00::/16](RFC 9602 SRv6 SIDs)[3fff::/20](RFC 9637 IPv6 documentation v2)[64:ff9b:1::/48](RFC 8215 NAT64 local-use, including embedded IPv4 loopback[64:ff9b:1::7f00:1])
-
No redirect re-validation.
isBlockedUrlruns once on the initial<img src>. The subsequent$fetch(decodedSrc, ...)(ofetch, default redirect-follow) follows 30x responses with no second-pass validation. Any allowed origin that returns a 302 to an internal IP — S3 redirect rules, GCS, Azure, CloudFront, any user-content CDN where the attacker can place a single redirect — completes the SSRF.
The net result is that the v6.2.5 SSRF advisory is bypassable in two distinct ways. The same root family as #29 / #38 (ipx) but in a different code path with different gaps — nuxt-og-image does not delegate to ipx, it ships its own validator, and that validator has fresh issues that survived the prior fix.
Affected
| Package | Version | Role |
|------------------|-------------------|-----------------------------------------------------|
| nuxt-og-image | 6.4.8 (latest) | default OG-image generator for Nuxt apps |
| @nuxtjs/og-image (alias) | same | re-export, same code path |
The vulnerable code lives in dist/runtime/server/og-image/core/plugins/imageSrc.js and is enforced for every <img src> (and style="background-image: url(...)") inside an OG image component, on production builds (!import.meta.dev).
Vulnerable code (imageSrc.js, verbatim)
function isPrivateIPv4(a, b) {
if (a === 127) return true;
if (a === 10) return true;
if (a === 172 && b >= 16 && b <= 31) return true;
if (a === 192 && b === 168) return true;
if (a === 169 && b === 254) return true;
if (a === 0) return true;
return false;
}
function isBlockedUrl(url) {
let parsed;
try { parsed = new URL(url); } catch { return true; }
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return true;
const hostname = parsed.hostname.toLowerCase();
const bare = hostname.replace(RE_IPV6_BRACKETS, "");
if (bare === "localhost" || bare.endsWith(".localhost")) return true;
const mappedV4 = bare.match(RE_MAPPED_V4); // /^::ffff:(\d+\.\d+\.\d+\.\d+)$/
const ip = mappedV4 ? mappedV4[1] : bare;
const parts = ip.split(".");
if (parts.length === 4 && parts.every((p) => RE_DIGIT_ONLY.test(p))) {
/* dotted-decimal IPv4 path */
}
if (RE_INT_IP.test(ip)) {
/* single-integer IPv4 path */
}
if (bare === "::1" || bare.startsWith("fc") || bare.startsWith("fd") || bare.startsWith("fe80"))
return true; // ← gap: only 4 IPv6 prefixes
return false; // ← everything else is "public"
}
// Then:
async function doResolveSrcToBuffer(src, kind, ctx) {
...
if (!import.meta.dev && isBlockedUrl(decodedSrc)) {
return { blocked: true };
}
const buffer = await $fetch(decodedSrc, { // ← follows 30x by…
- Record
GHSA-c2rm-g55x-8hr5- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
NVD
3.7
Nuxt OG Image generates OG Images with Vue templates in Nuxt. The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. It has an incomplete IPv6 prefix list and is missing redirect re-validation. This vulnerability is fixed in 6.4.9.
- Record
CVE-2026-44589- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSV
3.7
nuxt-og-image SSRF — bypass of GHSA-pqhr-mp3f-hrpp / v6.2.5 fix (IPv6 + redirect)
Nuxt OG Image generates OG Images with Vue templates in Nuxt. The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. It has an incomplete IPv6 prefix list and is missing redirect re-validation. This vulnerability is fixed in 6.4.9.
- Record
CVE-2026-44589- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSVGHSA-c2rm-g55x-8hr5
3.7
nuxt-og-image SSRF — bypass of GHSA-pqhr-mp3f-hrpp / v6.2.5 fix (IPv6 + redirect)
Summary
The isBlockedUrl() denylist introduced in [email protected] to remediate GHSA-pqhr-mp3f-hrpp (Dmitry Prokhorov / Positive Technologies, March 2026) is incomplete. The patch advisory states "Decimal/hexadecimal IP encoding bypasses are also handled" — that part is true (Node's WHATWG URL parser canonicalizes those forms before validation), but the v6.2.5 implementation misses two independent surfaces in the latest release 6.4.8:
-
IPv6 prefix list is incomplete. The IPv6 branch checks only
bare === "::1" || startsWith("fc") || startsWith("fd") || startsWith("fe80"). It misses:[::ffff:7f00:1]— IPv6-mapped IPv4 loopback in pure-hex form (RE_MAPPED_V4 regex requires dotted-quad). Reaches 127.0.0.1 on a single-stack-IPv4 host with no other primitive needed.[fec0::/10](RFC 3879 site-local — deprecated but still routable on legacy networks)[5f00::/16](RFC 9602 SRv6 SIDs)[3fff::/20](RFC 9637 IPv6 documentation v2)[64:ff9b:1::/48](RFC 8215 NAT64 local-use, including embedded IPv4 loopback[64:ff9b:1::7f00:1])
-
No redirect re-validation.
isBlockedUrlruns once on the initial<img src>. The subsequent$fetch(decodedSrc, ...)(ofetch, default redirect-follow) follows 30x responses with no second-pass validation. Any allowed origin that returns a 302 to an internal IP — S3 redirect rules, GCS, Azure, CloudFront, any user-content CDN where the attacker can place a single redirect — completes the SSRF.
The net result is that the v6.2.5 SSRF advisory is bypassable in two distinct ways. The same root family as #29 / #38 (ipx) but in a different code path with different gaps — nuxt-og-image does not delegate to ipx, it ships its own validator, and that validator has fresh issues that survived the prior fix.
Affected
| Package | Version | Role |
|------------------|-------------------|-----------------------------------------------------|
| nuxt-og-image | 6.4.8 (latest) | default OG-image generator for Nuxt apps |
| @nuxtjs/og-image (alias) | same | re-export, same code path |
The vulnerable code lives in dist/runtime/server/og-image/core/plugins/imageSrc.js and is enforced for every <img src> (and style="background-image: url(...)") inside an OG image component, on production builds (!import.meta.dev).
Vulnerable code (imageSrc.js, verbatim)
function isPrivateIPv4(a, b) {
if (a === 127) return true;
if (a === 10) return true;
if (a === 172 && b >= 16 && b <= 31) return true;
if (a === 192 && b === 168) return true;
if (a === 169 && b === 254) return true;
if (a === 0) return true;
return false;
}
function isBlockedUrl(url) {
let parsed;
try { parsed = new URL(url); } catch { return true; }
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return true;
const hostname = parsed.hostname.toLowerCase();
const bare = hostname.replace(RE_IPV6_BRACKETS, "");
if (bare === "localhost" || bare.endsWith(".localhost")) return true;
const mappedV4 = bare.match(RE_MAPPED_V4); // /^::ffff:(\d+\.\d+\.\d+\.\d+)$/
const ip = mappedV4 ? mappedV4[1] : bare;
const parts = ip.split(".");
if (parts.length === 4 && parts.every((p) => RE_DIGIT_ONLY.test(p))) {
/* dotted-decimal IPv4 path */
}
if (RE_INT_IP.test(ip)) {
/* single-integer IPv4 path */
}
if (bare === "::1" || bare.startsWith("fc") || bare.startsWith("fd") || bare.startsWith("fe80"))
return true; // ← gap: only 4 IPv6 prefixes
return false; // ← everything else is "public"
}
// Then:
async function doResolveSrcToBuffer(src, kind, ctx) {
...
if (!import.meta.dev && isBlockedUrl(decodedSrc)) {
return { blocked: true };
}
const buffer = await $fetch(decodedSrc, { // ← follows 30x by…
- Record
GHSA-c2rm-g55x-8hr5- CVSS 3.1
- 3.7 Low
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
nuxt-og-imagenpm |
6.2.5 up to 6.4.9 | 6.4.9 |
History
| When | Changed | Sources |
|---|---|---|
| 2026-11-02 | created | CVE List, EUVD, GitHub Advisories, NVD, OSV |
References
Left out here: the rest of 2 source descriptions. The JSON record has them.