Aug 4, 2026·7 min read·3 visits
Ghost CMS versions 5.18.0 through 6.21.1 allow unauthenticated attackers to enumerate registered member email addresses due to distinct HTTP response codes and messages returned by the magic link sign-in endpoint. Upgrading to 6.21.1 mitigates this.
CVE-2026-53947 is an observable response discrepancy (CWE-204) in Ghost CMS that permits unauthenticated remote user enumeration via the passwordless magic link sign-in endpoint.
Ghost is an open-source, Node.js-based content management system commonly utilized for publishing, newsletters, and membership platforms. In Ghost 5.18.0, native subscription capabilities were enhanced with passwordless sign-in functionalities, allowing members to access subscriber-only content without maintaining local passwords. This capability is managed through a portal subcomponent that relies on magic links dispatched to registered email addresses.
The unauthenticated nature of the sign-in endpoint introduces a critical attack surface. Specifically, the API endpoint responsible for dispatching these magic links is accessible to any remote client without previous authentication. This design enables external entities to interact with the internal database query mechanisms responsible for verifying registered emails.
Under CWE-204, the application's failure to sanitize responses based on query outcomes yields an observable discrepancy. This behavior exposes structural state data, specifically the presence or absence of an email address in the user repository. Attackers can leverage this systemic leakage to build lists of registered users, facilitating subsequent phishing or brute-force campaigns against associated services.
The logical flaw is located within the API routing controller of Ghost's members service. When a client requests a magic link via the Portal UI, a POST request is initiated against the /members/api/send-magic-link endpoint. This request is processed by the class RouterController within the codebase at ghost/core/core/server/services/members/members-api/controllers/router-controller.js.
The controller executes a sequence of validation tasks before invoking the notification service. First, the application normalizes the inbound email string. Next, it queries the member repository database to retrieve record data. In versions prior to 6.21.1, the backend controller handled non-existent accounts by raising an explicit exception instead of simulating success.
This branching logic created two distinct outcomes. A valid member triggered an HTTP 201 response containing transactional state data. An invalid email immediately terminated the control flow with an HTTP 400 Bad Request error. The returned JSON body contained precise error messages indicating that the account was not found. This distinction allowed external entities to map the underlying database state with certainty.
To remedy the discrepancy, Kevin Ansfield committed a patch under Commit ID fb2bb634653d99de68fc42d415721d755284fe30. The fix alters the execution path for non-existent users by normalizing the return structures. Instead of raising an exception, the application generates mock data that conforms to the expected structure of a successful transaction.
// Prior to patch fb2bb634653d99de68fc42d415721d755284fe30
const member = await this._memberRepository.get({email: normalizedEmail});
if (!member) {
throw new errors.BadRequestError({
message: this._allowSelfSignup() ? tpl(messages.memberNotFoundSignUp) : tpl(messages.memberNotFound)
});
}
// Post patch fb2bb634653d99de68fc42d415721d755284fe30
const member = await this._memberRepository.get({email: normalizedEmail});
if (!member) {
// Return a fake otcRef when OTC was requested so the response
// shape is identical regardless of whether a member exists
return includeOTC ? {otcRef: crypto.randomUUID()} : {};
}This architectural change ensures that the controller returns a standard object shape. If a One-Time Code (includeOTC) is requested, the server generates a cryptographically random UUID using the native crypto module. This mimics a valid database lookup and returns an HTTP 201 Created response to the client.
Simultaneously, client-side UI configurations were modified to prevent localization leaks. Changes within apps/portal/src/components/pages/magic-link-page.js converted definitive UI assertions into conditional statements. The interface now presents the same message regardless of whether the email address is actively registered, completing the user interface side of the normalization process.
The following flowchart displays the execution path differences for registered and unregistered email lookups in both vulnerable and patched states.
Exploitation of CVE-2026-53947 does not require specialized toolkits or elevated operational privileges. An attacker only requires network-level access to the Ghost portal instance and a target list of email addresses. This process is easily automated using standard scripting environments or command-line utilities.
An attacker initiates exploitation by sending an HTTP POST request targeting the endpoint with the query email formatted in JSON. The following request represents a typical payload structure:
POST /members/api/send-magic-link HTTP/1.1
Host: target-ghost-site.com
Content-Type: application/json
{
"email": "target_user@example.com",
"emailType": "signin"
}The server's response code dictates the classification. An HTTP 400 Bad Request indicates that the email is unregistered, while an HTTP 201 Created signals that the address corresponds to a registered member. Because of the predictable nature of the endpoint, this sweep can be performed concurrently against thousands of addresses to perform large-scale user mapping.
While the logical response normalization resolves the application-layer discrepancy, security teams must note that secondary side-channels persist. Specifically, timing side-channels remain a vector for identification. The execution times of the two logical branches are fundamentally different.
In the valid user execution path, Ghost must initialize several external tasks. After verifying the member, it writes transactional tokens to the localized SQL database and initializes network sockets to communicate with SMTP relays (such as Mailgun) to dispatch the authentication email. This process introduces significant network and disk latency, often requiring 150ms to 1000ms to complete.
In contrast, the invalid user execution path bypasses the mailer queue and database serialization. The engine generates a UUID and exits the execution thread immediately, typically in under 15ms. An attacker employing statistical analysis tools can measure response latency over multiple iterations to differentiate between the two scenarios despite identical status codes.
To exploit this timing side-channel, attackers use specialized timing-analysis frameworks. They establish a baseline of the target server's local processing speeds, then send batches of requests. If a significant timing delta is observed, the email is classified as a valid account. This illustrates that complete mitigation of user enumeration requires both logical normalization and timing padding.
The impact of user enumeration via CVE-2026-53947 is categorized as a loss of confidentiality regarding membership directories. While the vulnerability does not directly expose authentication credentials or session identifiers, the information harvested has high utility for targeted attacks.
Attackers can leverage validated email lists to perform targeted spear-phishing campaigns. By knowing that an email is specifically associated with a particular Ghost instance, attackers can construct highly convincing lures impersonating the platform administration. This increases the probability of credential harvest or malware execution.
Additionally, this data feeds directly into credential stuffing pipelines. If an attacker has compromised a database from another platform, they can check those emails against the Ghost site. Upon finding active accounts, they can attempt to reuse known passwords or execute password reset attacks, expanding the threat landscape to include potential takeover of subscriber profiles.
Remediation of CVE-2026-53947 requires updating Ghost CMS to version 6.21.1 or higher. This release integrates the response normalization fixes and the UI adjustments that mitigate application-layer enumeration. Organizations should schedule this update within normal maintenance windows.
If patching is not immediately feasible, organizations should deploy proxy-level protections. Enforcing strict rate limiting on the /members/api/send-magic-link endpoint is a highly effective workaround. Security administrators can leverage Nginx or Web Application Firewalls (such as Cloudflare) to restrict access to a maximum of 3 requests per IP address per minute.
# Example Nginx Rate Limiting Configuration
limit_req_zone $binary_remote_addr zone=magic_link_limit:10m rate=3r/m;
server {
location /members/api/send-magic-link {
limit_req zone=magic_link_limit burst=5 nodelay;
proxy_pass http://ghost_backend;
}
}Additionally, security operations teams should configure detection alerts. Monitoring system logs for high frequencies of POST requests targeting the magic-link endpoint can identify scanning patterns. Alerts should be triggered when a single client IP initiates multiple requests within a short time interval, allowing rapid identification and blocking of hostile enumeration sweeps.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Ghost TryGhost | >= 5.18.0, < 6.21.1 | 6.21.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-204 (Observable Response Discrepancy) |
| Attack Vector | Network (Remote, Unauthenticated) |
| CVSS Score | 5.3 (Medium) |
| EPSS Score | 0.00206 (Percentile: 10.84%) |
| Impact | User Enumeration |
| Exploit Status | None (No public weaponized exploit available) |
| KEV Status | Not Listed in CISA KEV |
The product behaves differently when executing an operation depending on whether a given input is valid, exposing differences that allow attackers to infer state information.
A critical session fixation vulnerability exists in the Ghost Admin panel from version 2.2.0 until 6.54.1. The Express-based authentication backend fails to invalidate or rotate the session identifier during login, allowing attackers to hijack administrative sessions.
A high-severity Cross-Site Scripting (XSS) vulnerability was identified in the @tryghost/activitypub package, the social and federation client library for the Ghost publishing platform. Prior to version 3.1.0, the ActivityPub client rendered incoming federated posts from external servers directly in the web user interface without proper sanitization. A maliciously customized ActivityPub server federated with a Ghost instance could transmit crafted posts containing embedded HTML payloads. When viewed by a user inside the ActivityPub client interface, the browser executes the injected JavaScript within the security context of the Ghost application domain.
An unauthenticated remote business logic vulnerability in Ghost CMS versions 6.27.0 through 6.43.1 allows attackers to bypass paid subscription gates. By injecting reserved metadata fields into public donation Stripe Checkout Sessions, attackers can obtain premium-tier memberships for arbitrary nominal amounts.
A critical broken access control vulnerability in Open WebUI (v0.10.0 to v0.11.0) allows authenticated write-collaborators to delete shared subfolders they do not own. Because deletion triggers a backend cascade using the folder owner's identity, this results in unauthorized permanent deletion of the owner's nested chats, messages, and files.
Open WebUI is susceptible to Server-Side Request Forgery (SSRF) when deployed in networks with NAT64 translation gateways. Authenticated users can bypass host validation checks by encapsulating internal or cloud-metadata IPv4 addresses within globally-routable IPv6 transition prefixes.
A critical authorization flaw exists in Flowise, a popular drag-and-drop orchestrator for building customized Large Language Model flows. Prior to version 3.1.3, multiple OAuth2 credential endpoints do not filter database lookups by the requesting entity's workspace context. This omission, combined with the exclusion of several endpoints from the global authentication pipeline, permits unauthenticated remote actors to access, manipulate, or steal access tokens linked to external service integrations.