Aug 5, 2026·6 min read·24 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.
A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.
AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.
A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.
CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.
CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.
CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.