CVE-2026-103275 Overview
Ghost versions 5.42.2 through 6.58.0 contain an information disclosure vulnerability in the Admin API bulk post and page edit and delete endpoints. The endpoints accept filters on restricted fields such as authors.password, enabling observable discrepancy attacks [CWE-203]. The flaw stems from an incomplete fix for CVE-2026-70590. Staff-level attackers can infer other staff users' password hashes based on which filters match, then perform offline password-guessing attacks against the recovered hashes.
Critical Impact
Authenticated staff users can extract password hashes of other staff members, including administrators, through boolean inference on filter responses. Successful offline cracking leads to account takeover.
Affected Products
- Ghost versions 5.42.2 and later
- Ghost versions prior to 6.58.0
- Self-hosted Ghost Admin API deployments exposing bulk post and page endpoints
Discovery Timeline
- 2026-10-01 - CVE-2026-103275 published to the National Vulnerability Database
- 2026-10-06 - Last updated in NVD database
Technical Details for CVE-2026-103275
Vulnerability Analysis
The Ghost Admin API exposes bulk edit and bulk delete endpoints for posts and pages. These endpoints accept a filter query parameter that is passed to the underlying model layer. The parameter allows filtering on fields that should be restricted, including authors.password.
Because the response reflects whether records matched the supplied filter, an authenticated staff user can submit filters that compare the authors.password column against candidate values character by character. The server's observable response differences disclose whether each comparison succeeded, enabling full hash recovery. This classifies as an observable discrepancy issue [CWE-203].
The original fix for CVE-2026-70590 restricted filterable fields on some endpoints but did not cover the bulk post and page edit and delete handlers. The incomplete coverage left the Admin API inference channel open against staff password hashes.
Root Cause
The bulkEdit and bulkDestroy handlers in ghost/core/core/server/api/endpoints/posts.js and pages.js forwarded frame.options.filter directly into models.Post.findAll without validating that the filter referenced only permitted columns. The permitted-field allowlist from the earlier fix was not applied to these bulk code paths.
Attack Vector
An attacker with staff-level Admin API credentials sends crafted bulk requests. Each request includes a filter expression that tests a byte or substring of another user's authors.password value. The attacker observes which bulk operations affect records, then iteratively narrows the hash value. Recovered bcrypt hashes are then subjected to offline cracking.
// Patch: ghost/core/core/server/api/endpoints/posts.js and pages.js
// Source: https://github.com/TryGhost/Ghost/commit/5ef16abe85e9b71ac3f1b1e2cbc462802efe3b5b
method: 'destroy'
},
async query(frame) {
- const postsToDelete = await models.Post.findAll({
- filter: frame.options.filter,
- status: 'all',
- columns: ['status']
- });
-
- const allDraft = postsToDelete.length > 0 && postsToDelete.every((post) => {
- return post.get('status') === 'draft';
- });
- if (allDraft) {
+ const result = await postsService.bulkDestroy(frame.options);
+ if (result.allDraft) {
frame.setHeader('X-Cache-Invalidate', '');
}
- return await postsService.bulkDestroy(frame.options);
+ return result;
}
},
The patch routes bulk destroy operations through postsService.bulkDestroy, which enforces the field allowlist and no longer passes untrusted filter expressions into direct model queries.
Detection Methods for CVE-2026-103275
Indicators of Compromise
- Admin API requests to /ghost/api/admin/posts/bulk/ or /ghost/api/admin/pages/bulk/ containing filter expressions that reference authors.password, password, or users.password.
- Repeated bulk edit or delete requests from the same staff session with incrementally varied filter strings suggesting brute-force inference.
- High-volume Admin API traffic from a single staff account targeting bulk endpoints without corresponding content changes.
Detection Strategies
- Parse Admin API access logs for filter= query parameters that include restricted field names such as password, secret, or token.
- Alert on bulk endpoints invoked with filter strings exceeding a normal complexity threshold or using regex-like comparison operators against user fields.
- Correlate staff user login events with sustained bulk API query bursts to identify credential-mining behavior.
Monitoring Recommendations
- Enable verbose Admin API request logging on Ghost deployments and forward logs to a centralized analytics platform.
- Baseline normal bulk-edit volumes per staff role and alert on deviations exceeding established thresholds.
- Review staff account activity after upgrading, and rotate any passwords suspected of exposure prior to patching.
How to Mitigate CVE-2026-103275
Immediate Actions Required
- Upgrade Ghost to version 6.58.0 or later, which enforces an allowlist on filter fields in bulk endpoints.
- Rotate passwords for all staff users on affected deployments, prioritizing Administrator and Owner roles.
- Audit Admin API access logs for prior exploitation attempts referencing restricted field names in filters.
Patch Information
The fix is contained in the commit TryGhost/Ghost 5ef16ab and documented in the GitHub Security Advisory GHSA-ghq6-q78f-2cpg. Additional analysis is available in the VulnCheck Advisory on Ghost. The patch moves bulk destroy and edit logic through postsService.bulkDestroy, which validates filter columns against an allowlist.
Workarounds
- Restrict Admin API network exposure to trusted operator IP ranges using a reverse proxy or firewall rule.
- Place a web application firewall rule in front of Ghost that blocks requests to /ghost/api/admin/posts/bulk/ and /ghost/api/admin/pages/bulk/ containing the substring password in query parameters.
- Reduce the number of staff-level accounts and enforce strong, unique passwords with multi-factor authentication to limit abuse potential.
# Example nginx rule to block filter abuse until patching completes
location ~ ^/ghost/api/admin/(posts|pages)/bulk/ {
if ($args ~* "(password|secret|token)") {
return 403;
}
proxy_pass http://ghost_upstream;
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.