Aug 5, 2026·6 min read·3 visits
Ghost versions 6.19.4 to 6.21.0 are vulnerable to Stored Cross-Site Scripting because they trust client-provided file MIME types during upload. Authenticated users can spoof content types to serve HTML scripts from cloud backends, compromising visitors and administrators. This issue is resolved in version 6.21.1.
CVE-2026-53948 is a stored cross-site scripting (XSS) vulnerability in the Ghost content management system. Affected versions (v6.19.4 up to v6.21.0) trusted the client-supplied Content-Type header during file uploads via the Admin API. This allowed authenticated attackers to upload benignly-named files with executable MIME types (like text/html), executing scripts in visitor browsers when hosted on integrated cloud platforms like S3 or GCS.
CVE-2026-53948 is a stored cross-site scripting (XSS) vulnerability in Ghost, an open-source content management system designed in Node.js. The vulnerability exists within the application's file upload subsystem accessed via the Admin API. Installations running versions 6.19.4 up to 6.21.0 are vulnerable to this security flaw.
Under normal operations, authorized users utilize the Admin API to upload static assets, such as images, PDFs, and documents. When configured with cloud storage backends like Amazon S3 or Google Cloud Storage, the system relies on stored metadata to determine how files are served to site visitors. Because the system trusted the user-supplied HTTP Content-Type header, attackers could map an arbitrary MIME type to a benign file extension.
The vulnerability is classified under CWE-434: Unrestricted Upload of File with Dangerous Type. Successful exploitation allows an authenticated attacker with minimal privileges to store malicious files that execute arbitrary script context within visitors' or administrators' web browsers. This compromise is particularly severe if the file is served from the same origin as the administrative dashboard.
The root cause of this vulnerability lies in the implicit trust of client-side inputs within Ghost's core server API code, specifically inside ghost/core/core/server/api/endpoints/files.js. During a file upload request via /ghost/api/admin/files/upload/, the client sends a multipart/form-data request containing the binary file and its metadata. Among these fields is the client-defined Content-Type header, which the backend reads as frame.file.mimetype.
The application server passed this user-controlled MIME type directly into the storage adapter configuration without server-side validation or sanitization. If the site is configured to store media locally, the impact is minimized because the web server typically determines the served mime-type based on the physical file extension. However, cloud storage adapters like Amazon S3 or Google Cloud Storage store the explicit metadata passed during the creation API call.
When a cloud adapter uploads the file, it registers the metadata containing the spoofed content type. The cloud platform later serves the file to the browser with the header specified during storage. Consequently, a request for a seemingly harmless file, such as report.pdf, results in the backend returning a response header of Content-Type: text/html if specified by the attacker.
The vulnerable implementation in ghost/core/core/server/api/endpoints/files.js demonstrates how the server used the incoming mimetype variable directly. The type key in the storage options object received the raw, unvalidated frame.file.mimetype value from the request structure.
// Vulnerable Code Path
const filePath = await storage.getStorage('files').save({
name: frame.file.originalname,
path: frame.file.path,
type: frame.file.mimetype // Directly trusts client input
});The patch implemented in commit d659e752d6636144d75b9aa94062cdbc88a16b21 addresses this structural oversight by replacing the reference to frame.file.mimetype with a deterministic, server-determined MIME type. The system now loads the mime-types utility and uses it to parse the extension of the uploaded file's original name.
// Patched Code Path
const storage = require('../../adapters/storage');
const mime = require('mime-types');
const controller = {
// ...
const filePath = await storage.getStorage('files').save({
name: frame.file.originalname,
path: frame.file.path,
type: mime.lookup(frame.file.originalname) || 'application/octet-stream'
});By querying the extension using mime.lookup(), the application guarantees that a file named image.png is registered and served as image/png regardless of what the user specified in the multipart request wrapper. If the extension is unrecognized, the code defaults to the safe fallback value of application/octet-stream, which prompts modern web browsers to download the resource instead of parsing or executing it natively.
Exploitation of CVE-2026-53948 requires an active account on the target Ghost CMS instance with privileges allowing file uploads, such as a Contributor, Author, or Editor. An attacker begins by crafting an HTML-compliant payload containing their malicious JavaScript. To bypass basic client-side verification or initial administrative inspection, the payload is given a legitimate extension, such as document.pdf or image.jpg.
The attacker then transmits an HTTP POST request targeting the /ghost/api/admin/files/upload/ endpoint using an intercepting proxy or a script. Within the multipart request, the attacker alters the Content-Type header linked to the uploaded file parts to text/html. The server receives the request, stores the binary content, and maps the attacker's text/html designation to the storage metadata registry.
When a victim navigates to the returned asset URL, the hosting bucket replies with the stored content-type rather than evaluating the file suffix. Because the returned header is text/html, the browser runs the embedded script under the target origin's context. This execution bypasses traditional boundaries, allowing access to standard web storage or session tokens.
The impact of a stored cross-site scripting vulnerability of this class is significant, particularly within content management systems. Because Ghost serves administrative actions and front-end visitor traffic from the same host by default, an active script payload executing on the main domain can interact directly with the Admin API on behalf of the victim.
If an administrative user is induced to view the uploaded file link, the executing script can perform actions with administrative permissions. This includes creating new admin users, altering system configurations, modifying published articles, or exfiltrating sensitive subscriber databases. For standard visitors, the script can be used to capture credentials via keystroke logging, redirect traffic to malicious domains, or deploy additional browser exploits.
The CVSS v3.1 score of 5.4 is calculated with a Low severity categorization due to the requirements for authentication (PR:L) and user interaction (UI:R). However, in environments where high-privileged users frequently review uploaded media from contributors, the real-world operational risk of administrative takeover is elevated. The impact scope is changed (S:C) because the attack crosses the storage-to-execution boundary within the browser sandbox.
The primary remediation path is upgrading the Ghost installation to version 6.21.1 or later, which completely addresses the root cause of client-trusted MIME types. For installations where immediate upgrades are restricted due to operational deployment cycles, several mitigations should be implemented immediately to reduce exposure.
Administrators should configure their edge reverse proxies or CDNs to append the X-Content-Type-Options: nosniff header to all assets served from the /content/files/ directory. This header prevents modern web browsers from sniffing the content and forces them to align rendering decisions strictly with the declared file extension, neutralizing the HTML payload contained in the pseudo-image file.
Another highly effective defense-in-depth practice is the segregation of media hosting. By configuring the cloud storage adapter to serve files from a separate origin (e.g., ghost-assets.com instead of ghost-site.com), any script execution is restricted to the isolated sandbox domain. This prevents access to administrative sessions and cookies stored on the primary Ghost CMS host under the Same-Origin Policy.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Ghost TryGhost | >= 6.19.4, < 6.21.1 | 6.21.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-434 |
| Attack Vector | Network |
| CVSS v3.1 Score | 5.4 (Medium) |
| Exploit Status | PoC Concept available |
| CISA KEV Status | Not listed |
| Impact | Stored Cross-Site Scripting (XSS) |
The product allows the attacker to upload or transfer files of dangerous types that can be automatically processed within the product's environment.
CVE-2026-70493 is a critical Regular Expression Denial of Service (ReDoS) vulnerability affecting Open WebUI from version 0.9.6 up to (but excluding) 0.11.0. An authenticated user can submit a custom, highly complex regular expression pattern to search files within the knowledge base. Because these expressions are compiled and executed synchronously using Python's standard backtracking re module inside an asynchronous event loop, the server becomes unresponsive. A single request is capable of stalling the entire platform, denying access to all concurrent users of the system.
CVE-2026-70588 is a stored Cross-Site Scripting (XSS) vulnerability in Ghost CMS versions 5.26.0 through 6.54.0. The vulnerability exists within the Universal Import feature of the Ghost Admin interface. When processing imported content from third-party platforms such as Revue, the importer fails to sanitize user-controlled HTML tags, rich-text structured JSON, or link fields before rendering them in the Ghost Admin panel and front-end template rendering contexts.
A business logic vulnerability in Ghost CMS allows unauthenticated remote users to redeem deactivated or archived promotional subscription offers by programmatically passing old offer identifiers during the checkout session initialization.
A Server-Side Request Forgery (SSRF) vulnerability exists in the Ghost content management system from version 6.0.9 up to, but not including, 6.21.1. The flaw resides in the 'request-external.js' module, where the IP address validation blocklist fails to account for fully expanded IPv4-mapped IPv6 formats. This allows unauthenticated remote attackers to bypass the private IP filter and initiate unauthorized connections to loopback services, internal subnets, or cloud instance metadata endpoints.
Ghost CMS is vulnerable to Server-Side Request Forgery (SSRF) in versions 6.0.9 through 6.21.1. Due to a Time-of-Check to Time-of-Use (TOCTOU) race condition in its outbound fetch validation logic, an attacker can bypass IP blocklists via DNS Rebinding. This allows unauthorized interaction with private networks and local services.
A Server-Side Request Forgery (SSRF) vulnerability exists in the Mobiledoc post-rendering component of Ghost CMS versions 6.19.4 through 6.21.0. This allows authenticated staff users with post creation or editing privileges to force the application server to perform arbitrary outbound HTTP GET requests, targeting internal endpoints, local loopback interfaces, or cloud metadata endpoints.