GHSA-234q-vvw3-mrfq CVE-2026-29069
Craft CMS has unauthenticated activation email trigger with potential user enumeration
- 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
- 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:
- The endpoint is listed in
allowAnonymous- no login required (intentional for self-service) - For pending users, there is NO ownership verification
- 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
| Package | Vulnerable | Fixed 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
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why 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 |
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