Skip to content

GHSA-234q-vvw3-mrfq CVE-2026-29069

Craft CMS has unauthenticated activation email trigger with potential user enumeration

Medium Packagist
Severity
0.0 / 10
Medium · CVSS 4.0
Exploit chance (EPSS)
0.32%
Chance of exploitation in the next 30 days. Higher than 24.68% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-287 Improper Authentication
CWE-639 Authorization Bypass Through User-Controlled Key
Fix
5.9.0-beta.2
Upgrade craftcms/cms to this version or later

Description

The actionSendActivationEmail() endpoint is accessible to unauthenticated users and does not require a permission check for pending users. An attacker with no prior access can trigger activation emails for any pending user account by knowing or guessing the user ID. If the attacker controls the target user’s email address, they can activate the account and gain access to the system.

The vulnerability is not that anonymous access exists - there’s a legitimate use case for it. The vulnerability is that the endpoint accepts arbitrary userId parameters without verifying ownership.

Craft CMS allows public user registration. When a user registers but doesn’t receive their activation email (spam filter, typo correction, etc.), they need a way to request a resend. This is why send-activation-email is in the allowAnonymous array - it’s intentional self-service functionality.

The Security Gap

The endpoint accepts userId as the identifier:

$userId = $this->request->getRequiredBodyParam('userId');

This allows any visitor to trigger activation emails for any pending user, not just their own registration.


Background

When administrators create new user accounts in Craft CMS, users are created in a “pending” state until they activate their account via an emailed link. The actionSendActivationEmail() function sends (or resends) this activation email.

Expected Behavior: Anonymous users should only be able to resend activation emails for their own registration.

Actual Behavior:

  1. The endpoint is listed in allowAnonymous - no login required (intentional for self-service)
  2. For pending users, there is NO ownership verification
  3. Any unauthenticated visitor can trigger activation emails for ANY pending user by ID

Attack Scenarios

Scenario 1: Targeted Account Takeover

Prerequisites: Attacker controls target user’s email (compromised email, shared mailbox, typosquatting, etc.)

1. Admin creates a user account for victim@company.com
2. User account is in PENDING state (hasn’t activated yet)
3. Attacker has compromised victim@company.com (or it’s a typo of attacker’s domain)
4. Attacker discovers user ID (brute-force, GraphQL enumeration, or insider knowledge)
5. Attacker (unauthenticated) triggers: POST /actions/users/send-activation-email
6. Activation email sent to victim@company.com (attacker-controlled)
7. Attacker clicks activation link, sets password
8. Attacker gains access as that user with pre-assigned permissions

Scenario 2: User ID Brute-Force Enumeration

1. Attacker iterates through user IDs (1, 2, 3, ...)
2. For each ID, the attacker calls send-activation-email
3. Response reveals user state:
   - "Activation email sent." = Pending user exists
   - "User not found" = No user with this ID
   - "Activation emails can only be sent to inactive or pending users" = Active user exists
4. Attacker builds a map of all user IDs and their states
5. For any pending user whose email an attacker controls → account takeover

Scenario 3: GraphQL + Targeted Attack

Prerequisites: GraphQL public schema allows user queries

1. Attacker queries GraphQL: { users { id email status } }
2. Filters for pending users
3. Cross-references with emails attacker controls
4. Triggers activation for the target user
5. Account takeover

Scenario 4: Email Spam / Harassment

1. Attacker brute-forces all pending user IDs
2. Repeatedly triggers activation emails
3. Victims receive unwanted emails from the Craft site
4. Potential for:
   - Reputation damage to the site
   - Email deliverability issues (spam reports)
   - User confusion/phishing vector

References

https://github.com/craftcms/cms/commit/c3d02d4a7246f516933f42106c0a67ce062f68d8

Affected versions

Packagist

PackageVulnerableFixed in
craftcms/cms
>= 5.0.0-RC1, < 5.9.0-beta.2
5.9.0-beta.2
craftcms/cms
>= 4.0.0-RC1, < 4.17.0-beta.2
4.17.0-beta.2

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

craftcms/cms Packagist → 5.9.0-beta.2

composer
composer require craftcms/cms:^5.9.0-beta.2

Severity breakdown

CVSS 4.0 0.0

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P

Attack vector
Network
Attack complexity
Low
Attack requirements
None
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
Low
Integrity (vulnerable system)
High
Availability (vulnerable system)
None
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None
Exploit maturity
Proof of concept

CVSS 3.1 5.3

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
None

Weakness

CWE-287: Improper Authentication

When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.

How to prevent it
  • Architecture and Design: Use an authentication framework or library such as the OWASP ESAPI Authentication feature.

CWE-639: Authorization Bypass Through User-Controlled Key

The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.

How to prevent it
  • Architecture and Design: For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
  • Architecture and Design, Implementation: Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.
  • Architecture and Design: Use encryption in order to make it more difficult to guess other legitimate values of the key or associate a digital signature with the key so that the server can verify that there has been no tampering.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-7vrx-9684-xrf2
Craft CMS stores arbitrary content provided by unauthenticated users in session files
Medium Known exploited Also affects craftcms/cms
GHSA-f3gw-9ww9-jmc3
Craft CMS Allows Remote Code Execution
Critical Known exploited Also affects craftcms/cms
GHSA-x684-96hh-833x
Craft CMS has a potential RCE with a compromised security key
High Known exploited Also affects craftcms/cms
GHSA-2p6p-9rc9-62j9
Craft CMS has potential RCE when PHP `register_argc_argv` config setting is enabled
Critical Known exploited Also affects craftcms/cms
GHSA-wg23-69c2-gjc8
Craft CMS: Passkey login accepts replayed WebAuthn assertions
Medium Also affects craftcms/cms
GHSA-957r-qf9p-67xw
Craft CMS: Arbitrary file read via SplFileObject in non-sandboxed template contexts
Medium Also affects craftcms/cms
GHSA-596p-6jv8-775v
Craft CMS: Authenticated leak of secret environment variables
Medium Also affects craftcms/cms
GHSA-xxpx-f366-4xpq
Craft CMS: Authorization bypass: view-only Categories user can modify category structure via structures/move-element
Medium Also affects craftcms/cms
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor