This document introduces "VectorFreed", a vulnerability chain that begins with a use-after-free I (@rafabd1) found in librsvg (CVE-2026-96889). In a Node.js/Sharp/libvips build, this bug led to command execution. It covers the bug, the paths confirmed in applications so far, and the fixes available now.
An SVG can include another SVG through XInclude. In the vulnerable path, librsvg starts parsing an included document while libxml2 is still expanding an entity in the outer one. If the included SVG declares an entity with the same name, librsvg replaces and frees the first entity. libxml2 still holds a pointer to it.
When the included parse ends, libxml2 carries on with that old pointer. The memory may already belong to something else by then, so later writes can corrupt it. A crash is one result. In other cases, this also led to command execution.
The attacker needs crafted SVG markup to reach librsvg. That can happen when an application accepts an SVG file, but it can also happen when the application builds an SVG from user input. Merely using librsvg somewhere in the dependency tree does not establish an exploitable path; the input has to reach an affected build when the image is rendered.
The Next.js route we tested accepted text instead of an SVG upload. It placed that text inside an inline SVG for the Node.js version of ImageResponse. A separate escaping bug in Satori let the text change the generated SVG markup. Sharp/libvips then passed that SVG to librsvg. Vercel tracked the Next.js path as CVE-2026-94545. The Edge version of ImageResponse is not affected by this path. Here is a short Next.js reproduction.
Ghost has a different path. A staff user, including a Contributor, could create a bookmark card for a site they control. Processing the card image could then run commands on the Ghost server. Ghost published this as CVE-2026-105642.
During this research, I identified affected input paths in multiple downstream products and confirmed command execution in several of them, including the Next.js and Ghost cases above. How that input reaches librsvg varies from product to product. That is why an upstream image parser bug can turn up in places that do not seem related at first; the recent libheif case is another example. It does not mean every product using librsvg is remotely exploitable.
- librsvg: 2.63.2 has the fix, with backports in 2.62.4, 2.61.5, 2.60.3, 2.57.5, and 2.56.6. If you use an older vendor build, check whether it includes the fix. See the librsvg advisory.
- Next.js: Versions 16.2.0 through 16.3.5 are affected when an application puts attacker-controlled values into SVG content, attributes, or styles on the Node.js
ImageResponsepath. Upgrade to 16.3.6. - Ghost: Ghost lists versions >= 6.56.0 and < 6.67.0 as affected. Upgrade to 6.67.0.
- Satori: If you use it directly, 0.33.5 fixes the separate escaping bug used in the Next.js route.
If you use Sharp's prebuilt binaries, check the @img/sharp-libvips-* package your app installed. It bundles libvips and its dependencies, including librsvg. Updating the system copy of librsvg may leave your app using the old one.
The UAF PoC includes an SVG generator for several input paths and can be used to check the use-after-free at the start of the chain.
The EQSTLab Next.js RCE PoC is a working example of the full chain for its pinned Node.js/Sharp build. It generates and sends the payload automatically. A working exploit is public now, so update affected dependencies or mitigate the input path as soon as possible.
Warning
There is no universal RCE PoC for this chain. The EQSTLab example targets its pinned build; other targets may need different payloads. PoCs that rely on SVG <foreignObject> for command execution do not demonstrate the chain described here.
For initial validation on other targets, the UAF PoC is more practical. I'll also publish the technical write-up in the coming weeks, with the steps from the UAF to command execution and a post-mortem covering this past month of research into the chain.