{"id":"CVE-2026-102527","aliases":["EUVD-2026-102199","GHSA-2x6f-rp82-ppmj"],"status":"published","title":"Nuxt OG Image cache poisoning through derivable cache keys on unsigned dynamic image routes","description":"Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.2.5 until 6.7.0, the dynamic /_og/d/*.png route accepts unsigned requests while resolvePathCacheKey() produces cache keys from the request path, site URL, and normalized rendering options exposed through image URLs and server responses. An unauthenticated attacker can provide attacker-controlled image data through the title parameter together with a derived cacheKey and populate the cache slot used for a legitimate Open Graph image, causing spoofed image content to be served to users. This issue is fixed in version 6.7.0.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00186,"percentile":0.08261,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-349"],"published":"2026-10-15T16:55:23Z","modified":"2026-10-28T02:31:13Z","fixes":[{"ecosystem":"npm","package":"nuxt-og-image","fixed_in":["6.7.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"nuxt-og-image","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"6.2.5"},{"fixed":"6.7.0"}]}]},{"source":"osv","ecosystem":"npm","package":"nuxt-og-image","ranges":[{"type":"SEMVER","events":[{"introduced":"6.2.5"},{"fixed":"6.7.0"}]}]}],"products":[{"vendor":"nuxt-modules","product":"og-image"}],"references":[{"url":"https://github.com/nuxt-modules/og-image/security/advisories/GHSA-2x6f-rp82-ppmj","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/nuxt-modules/og-image/pull/638","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/nuxt-modules/og-image/commit/e6c5b53ce7f434ab8cbc91298dccfb2b515c56a5","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/nuxt-modules/og-image/releases/tag/v6.7.0","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-102527","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-2x6f-rp82-ppmj"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/102xxx/CVE-2026-102527.json","tags":["advisory"]},{"url":"https://github.com/nuxt-modules/og-image","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-102527","title":"Nuxt OG Image cache poisoning through derivable cache keys on unsigned dynamic image routes","description":"Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.2.5 until 6.7.0, the dynamic /_og/d/*.png route accepts unsigned requests while resolvePathCacheKey() produces cache keys from the request path, site URL, and normalized rendering options exposed through image URLs and server responses. An unauthenticated attacker can provide attacker-controlled image data through the title parameter together with a derived cacheKey and populate the cache slot used for a legitimate Open Graph image, causing spoofed image content to be served to users. This issue is fixed in version 6.7.0.","status":"published","published":"2026-10-15T16:55:23.15Z","modified":"2026-10-16T17:13:07.014Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-349"],"updated_at":"2026-11-02T13:38:54.394896Z"}],"euvd":[{"id":"EUVD-2026-102199","description":"Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.2.5 until 6.7.0, the dynamic /_og/d/*.png route accepts unsigned requests while resolvePathCacheKey() produces cache keys from the request path, site URL, and normalized rendering options exposed through image URLs and server responses. An unauthenticated attacker can provide attacker-controlled image data through the title parameter together with a derived cacheKey and populate the cache slot used for a legitimate Open Graph image, causing spoofed image content to be served to users. This issue is fixed in version 6.7.0.","status":"published","published":"2026-10-15T16:55:23Z","modified":"2026-10-16T17:13:07Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T17:00:04.331134Z"}],"ghsa":[{"id":"GHSA-2x6f-rp82-ppmj","title":"Nuxt OG Image cache poisoning through derivable cache keys on unsigned dynamic image routes","description":"**Vendor:** Nuxt\n**Product:** Nuxt OG Image\n**Version:** 6.2.5\n**Description:** Due to the specifics of caching and key generation in the Open Graph image cache created by the module, an unauthenticated attacker can modify the content of the image (for example, the `title` parameter) that is delivered to the user. \n**Impact:** Content Spoofing\n**Vulnerable component:** route `/_og/d/*.png`\n**Exploitation conditions:** Access to the application\n**Mitigation:** Upgrade to 6.7.2 and set a sufficiently secure `secret` in config `ogImage`\n**Researcher:** Dmitry Vanin (Positive Technologies)\n\n## Research\nDuring [the previous vulnerability research](https://github.com/nuxt-modules/og-image/security/advisories/GHSA-6c86-prf5-hj4h), it was discovered that version 6.2.5 changed the mechanism for generating cache indexes; it now takes into account the parameters passed during image generation.\n\nHowever, the image is still generated from data transmitted to the attacker.\n\n_Listing 1. New code for generating indexes in cache_\n```\nexport function resolvePathCacheKey(e: H3Event, path: string, resolvedOptions?: Record\u003cstring, any\u003e) {\n  const siteConfig = getSiteConfig(e, {\n    resolveRefs: true,\n  })\n  const basePath = withoutTrailingSlash(withoutLeadingSlash(normalizeKey(path)))\n  const hashParts: any[] = [\n    basePath,\n    import.meta.prerender ? '' : siteConfig.url,\n  ]\n  // Hash resolved options (not raw query string) so unknown/extra query params\n  // cannot produce unique cache keys and bypass the cache.\n  if (resolvedOptions)\n    hashParts.push(hash(resolvedOptions))\n  return [\n    (!basePath || basePath === '/') ? 'index' : basePath,\n    hash(hashParts),\n  ].join(':')\n}\n```\nIn this case, `basePath` is obtained from the `_path` parameter, `resolvedOptions` are the normalized settings of the component, for example:\n\n_Listing 2. An example of `resolvedOptions`_\n```\n{\"emojis\":\"noto\",\"extension\":\"png\",\"width\":1200,\"height\":600,\"cacheMaxAgeSeconds\":259200,\"component\":\"OgImageDefaultImageTakumi\"}\n```\nFrom the analysis of `resolvedOptions`, it was concluded that, in general, all parameters are known either from the direct link to the OG Image or from the server response.\n\n## Vulnerability reproduction\n\n1. Find the route to OG Image.\n_Figure 1. Demonstration of finding the route to OG Image_\n\u003cimg width=\"974\" height=\"361\" alt=\"image\" src=\"https://github.com/user-attachments/assets/d3ee6b54-d545-4a3b-8cbf-f82df99ed4d0\" /\u003e\n\n2. Retrieve parameters from the server’s response, especially `cacheMaxAgeSeconds` (`cache-control` header, `s-maxage` key), the other parameters are known from the meta tags as well as from the `c_\u003ccomponent name\u003e` parameter.\n_Figure 2. Obtaining parameters_\n\u003cimg width=\"974\" height=\"245\" alt=\"image\" src=\"https://github.com/user-attachments/assets/84a4cd8b-9281-47ef-b524-0047a0f6077a\" /\u003e\n\n3. Generate a cache key using a script.\n_Listing 3. Srcipt_\n```\nimport {hash} from \"ohash\";\n\nvar url = \"https://contoso.com\"\n\nvar obj = JSON.parse(`{\"emojis\":\"noto\",\"extension\":\"png\",\"width\":1200,\"height\":600,\"cacheMaxAgeSeconds\":259200,\"component\":\"OgImageBlogPostTakumi\"}`)\n\nvar basePath = '/'\n\nvar hashParts = [\n    basePath,\n    url\n  ]\n  \nif (obj)\n\thashParts.push(hash(obj))\n\nconsole.log([\n    (!basePath || basePath === '/') ? 'index' : basePath,\n    hash(hashParts),\n  ].join(':'))\n```\n4. Repeat the steps as in [the vulnerability 1](https://github.com/nuxt-modules/og-image/security/advisories/GHSA-6c86-prf5-hj4h), but transmit the obtained `cacheKey` in addition.  \n5. Thus, the same result as in [the vulnerability 1](https://github.com/nuxt-modules/og-image/security/advisories/GHSA-6c86-prf5-hj4h) was obtained.\n_Figure 4. Server response_\n\u003cimg width=\"974\" height=\"729\" alt=\"image\" src=\"https://github.com/user-attachments/assets/5895c0e8-e497-410d-bef9-f6f2f9649261\" /\u003e\n\n## Credits\nResearcher: Dmitry Vanin (Positive Technologies)","status":"published","published":"2026-10-15T20:15:27Z","modified":"2026-10-15T20:15:30Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-349"],"updated_at":"2026-11-02T14:41:32.483482Z"}],"nvd":[{"id":"CVE-2026-102527","description":"Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.2.5 until 6.7.0, the dynamic /_og/d/*.png route accepts unsigned requests while resolvePathCacheKey() produces cache keys from the request path, site URL, and normalized rendering options exposed through image URLs and server responses. An unauthenticated attacker can provide attacker-controlled image data through the title parameter together with a derived cacheKey and populate the cache slot used for a legitimate Open Graph image, causing spoofed image content to be served to users. This issue is fixed in version 6.7.0.","status":"published","published":"2026-10-15T17:17:08.973Z","modified":"2026-10-16T18:17:14.633Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/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","level":"medium","assessor":"secondary"}],"cwes":["CWE-349"],"updated_at":"2026-11-02T14:12:59.751798Z"}],"osv":[{"id":"CVE-2026-102527","title":"Nuxt OG Image cache poisoning through derivable cache keys on unsigned dynamic image routes","description":"Nuxt OG Image generates OG Images with Vue templates in Nuxt. From 6.2.5 until 6.7.0, the dynamic /_og/d/*.png route accepts unsigned requests while resolvePathCacheKey() produces cache keys from the request path, site URL, and normalized rendering options exposed through image URLs and server responses. An unauthenticated attacker can provide attacker-controlled image data through the title parameter together with a derived cacheKey and populate the cache slot used for a legitimate Open Graph image, causing spoofed image content to be served to users. This issue is fixed in version 6.7.0.","status":"published","published":"2026-10-15T16:55:23.15Z","modified":"2026-10-28T02:31:13.310289653Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-349"],"updated_at":"2026-11-02T15:44:53.83521Z"},{"id":"GHSA-2x6f-rp82-ppmj","title":"Nuxt OG Image cache poisoning through derivable cache keys on unsigned dynamic image routes","description":"**Vendor:** Nuxt\n**Product:** Nuxt OG Image\n**Version:** 6.2.5\n**Description:** Due to the specifics of caching and key generation in the Open Graph image cache created by the module, an unauthenticated attacker can modify the content of the image (for example, the `title` parameter) that is delivered to the user. \n**Impact:** Content Spoofing\n**Vulnerable component:** route `/_og/d/*.png`\n**Exploitation conditions:** Access to the application\n**Mitigation:** Upgrade to 6.7.2 and set a sufficiently secure `secret` in config `ogImage`\n**Researcher:** Dmitry Vanin (Positive Technologies)\n\n## Research\nDuring [the previous vulnerability research](https://github.com/nuxt-modules/og-image/security/advisories/GHSA-6c86-prf5-hj4h), it was discovered that version 6.2.5 changed the mechanism for generating cache indexes; it now takes into account the parameters passed during image generation.\n\nHowever, the image is still generated from data transmitted to the attacker.\n\n_Listing 1. New code for generating indexes in cache_\n```\nexport function resolvePathCacheKey(e: H3Event, path: string, resolvedOptions?: Record\u003cstring, any\u003e) {\n  const siteConfig = getSiteConfig(e, {\n    resolveRefs: true,\n  })\n  const basePath = withoutTrailingSlash(withoutLeadingSlash(normalizeKey(path)))\n  const hashParts: any[] = [\n    basePath,\n    import.meta.prerender ? '' : siteConfig.url,\n  ]\n  // Hash resolved options (not raw query string) so unknown/extra query params\n  // cannot produce unique cache keys and bypass the cache.\n  if (resolvedOptions)\n    hashParts.push(hash(resolvedOptions))\n  return [\n    (!basePath || basePath === '/') ? 'index' : basePath,\n    hash(hashParts),\n  ].join(':')\n}\n```\nIn this case, `basePath` is obtained from the `_path` parameter, `resolvedOptions` are the normalized settings of the component, for example:\n\n_Listing 2. An example of `resolvedOptions`_\n```\n{\"emojis\":\"noto\",\"extension\":\"png\",\"width\":1200,\"height\":600,\"cacheMaxAgeSeconds\":259200,\"component\":\"OgImageDefaultImageTakumi\"}\n```\nFrom the analysis of `resolvedOptions`, it was concluded that, in general, all parameters are known either from the direct link to the OG Image or from the server response.\n\n## Vulnerability reproduction\n\n1. Find the route to OG Image.\n_Figure 1. Demonstration of finding the route to OG Image_\n\u003cimg width=\"974\" height=\"361\" alt=\"image\" src=\"https://github.com/user-attachments/assets/d3ee6b54-d545-4a3b-8cbf-f82df99ed4d0\" /\u003e\n\n2. Retrieve parameters from the server’s response, especially `cacheMaxAgeSeconds` (`cache-control` header, `s-maxage` key), the other parameters are known from the meta tags as well as from the `c_\u003ccomponent name\u003e` parameter.\n_Figure 2. Obtaining parameters_\n\u003cimg width=\"974\" height=\"245\" alt=\"image\" src=\"https://github.com/user-attachments/assets/84a4cd8b-9281-47ef-b524-0047a0f6077a\" /\u003e\n\n3. Generate a cache key using a script.\n_Listing 3. Srcipt_\n```\nimport {hash} from \"ohash\";\n\nvar url = \"https://contoso.com\"\n\nvar obj = JSON.parse(`{\"emojis\":\"noto\",\"extension\":\"png\",\"width\":1200,\"height\":600,\"cacheMaxAgeSeconds\":259200,\"component\":\"OgImageBlogPostTakumi\"}`)\n\nvar basePath = '/'\n\nvar hashParts = [\n    basePath,\n    url\n  ]\n  \nif (obj)\n\thashParts.push(hash(obj))\n\nconsole.log([\n    (!basePath || basePath === '/') ? 'index' : basePath,\n    hash(hashParts),\n  ].join(':'))\n```\n4. Repeat the steps as in [the vulnerability 1](https://github.com/nuxt-modules/og-image/security/advisories/GHSA-6c86-prf5-hj4h), but transmit the obtained `cacheKey` in addition.  \n5. Thus, the same result as in [the vulnerability 1](https://github.com/nuxt-modules/og-image/security/advisories/GHSA-6c86-prf5-hj4h) was obtained.\n_Figure 4. Server response_\n\u003cimg width=\"974\" height=\"729\" alt=\"image\" src=\"https://github.com/user-attachments/assets/5895c0e8-e497-410d-bef9-f6f2f9649261\" /\u003e\n\n## Credits\nResearcher: Dmitry Vanin (Positive Technologies)","status":"published","published":"2026-10-15T20:15:27Z","modified":"2026-10-15T20:30:08.57290259Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-349"],"updated_at":"2026-11-02T15:46:35.050232Z"}]}}
