Drupal core - Moderately critical - Access bypass - SA-CORE-2024-004
Description
A vulnerability in Drupal Core allows Privilege Escalation.This issue affects Drupal Core: from 8.0.0 before 10.2.11, from 10.3.0 before 10.3.9, from 11.0.0 before 11.0.8.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Drupal Core's uniqueness constraint for user fields is case-insensitive, enabling account takeover via duplicate emails due to database collation mismatch.
Vulnerability
Analysis
CVE-2024-55634 is a privilege escalation vulnerability in Drupal Core that arises from an inconsistency in the enforcement of uniqueness constraints for certain user fields, particularly email addresses. The root cause is a lack of case-sensitivity in the uniqueness validation logic: the UniqueFieldConstraint in Drupal’s validation plugin is configured to treat values like 'FOO' and 'foo' as equivalent by default, but the actual database check does not consistently enforce this based on the underlying database engine and its collation settings [3]. This means that under certain collations (e.g., case-insensitive collations in MySQL), the system may allow registration of a new account with an email address that differs only in case from an existing user’s email, thereby bypassing the intended uniqueness check [1][3].
Exploitation
Exploitation does not require any prior authentication; an attacker can simply attempt to register a new user account with an email address that is identical to a victim’s email but with altered character casing (e.g., 'Victim@example.com' vs. 'victim@example.com'). The vulnerable validation path in UniqueFieldConstraint.php only performs a case-insensitive comparison when the $constraint->caseSensitive property is set, but the default is FALSE, and the database query for uniqueness uses a case-sensitive IN operator only if that property is explicitly TRUE [3]. Consequently, the database may still treat the two addresses as distinct, allowing the registration to succeed even though the intended validation should have blocked it.
Impact
Successful exploitation can lead to data integrity issues and privilege escalation because an attacker could effectively create an account that overlaps with an existing user’s identity. This may allow the attacker to receive password reset emails or other notifications intended for the victim, potentially leading to account takeover or confusion in user administration [2]. The vulnerability affects a wide range of Drupal Core versions, from 8.0.0 up to (but not including) 10.2.11, 10.3.9, and 11.0.8 [4].
Mitigation
The official solution is to update Drupal Core to the latest patched versions: 10.2.11, 10.3.9, or 11.0.8, depending on the minor branch [4]. The fix introduces a corrected query that uses LIKE with escapeLike() for case-insensitive matching, ensuring the uniqueness constraint is properly enforced irrespective of database collation [3]. Note that already-affected accounts will not be automatically fixed; administrators should follow guidance from the Drupal Security Team on rectifying duplicate email accounts [4]. Drupal 7 and any unsupported version (e.g., Drupal 8, 9, and pre-10.2 releases) are either not affected or are end-of-life and should be upgraded immediately.
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
drupal/corePackagist | >= 8.0.0, < 10.2.11 | 10.2.11 |
drupal/corePackagist | >= 10.3.0, < 10.3.9 | 10.3.9 |
drupal/corePackagist | >= 11.0.0, < 11.0.8 | 11.0.8 |
drupal/core-recommendedPackagist | >= 8.0.0, < 10.2.11 | 10.2.11 |
drupal/core-recommendedPackagist | >= 10.3.0, < 10.3.9 | 10.3.9 |
drupal/core-recommendedPackagist | >= 11.0.0, < 11.0.8 | 11.0.8 |
drupal/drupalPackagist | >= 8.0.0, < 10.2.11 | 10.2.11 |
drupal/drupalPackagist | >= 10.3.0, < 10.3.9 | 10.3.9 |
drupal/drupalPackagist | >= 11.0.0, < 11.0.8 | 11.0.8 |
Affected products
6- Range: >=8.0.0, <10.2.11 || >=10.3.0, <10.3.9 || >=11.0.0, <11.0.8
- osv-coords4 versionspkg:bitnami/drupalpkg:composer/drupal/corepkg:composer/drupal/core-recommendedpkg:composer/drupal/drupal
>= 8.0.0, < 10.3.9+ 3 more
- (no CPE)range: >= 8.0.0, < 10.3.9
- (no CPE)range: >= 8.0.0, < 10.2.11
- (no CPE)range: >= 8.0.0, < 10.2.11
- (no CPE)range: >= 8.0.0, < 10.2.11
- Drupal/Drupal Corev5Range: 8.0.0
Patches
17ae0e8f1824eSA-CORE-2024-004 by zengenuity, cilefen, kristiaanvandeneynde, mcdruid, larowlan
4 files changed · +61 −13
lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php+10 −0 modified@@ -17,6 +17,16 @@ class UniqueFieldConstraint extends SymfonyConstraint { public $message = 'A @entity_type with @field_name %value already exists.'; + /** + * This constraint is case-insensitive by default. + * + * For example "FOO" and "foo" would be considered as equivalent, and + * validation of the constraint would fail. + * + * @var bool + */ + public $caseSensitive = FALSE; + /** * {@inheritdoc} */
lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php+12 −1 modified@@ -64,12 +64,23 @@ public function validate($items, Constraint $constraint): void { ->getStorage($entity_type_id) ->getAggregateQuery() ->accessCheck(FALSE) - ->condition($field_name, $item_values, 'IN') ->groupBy("$field_name.$property_name"); if (!$is_new) { $entity_id = $entity->id(); $query->condition($id_key, $entity_id, '<>'); } + + if ($constraint->caseSensitive) { + $query->condition($field_name, $item_values, 'IN'); + } + else { + $or_group = $query->orConditionGroup(); + foreach ($item_values as $item_value) { + $or_group->condition($field_name, \Drupal::database()->escapeLike($item_value), 'LIKE'); + } + $query->condition($or_group); + } + $results = $query->execute(); if (!empty($results)) {
modules/file/src/Plugin/Validation/Constraint/FileUriUnique.php+14 −3 modified@@ -4,7 +4,8 @@ use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Validation\Attribute\Constraint; -use Symfony\Component\Validator\Constraint as SymfonyConstraint; +use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint; +use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator; /** * Supports validating file URIs. @@ -13,15 +14,25 @@ id: 'FileUriUnique', label: new TranslatableMarkup('File URI', [], ['context' => 'Validation']) )] -class FileUriUnique extends SymfonyConstraint { +class FileUriUnique extends UniqueFieldConstraint { public $message = 'The file %value already exists. Enter a unique file URI.'; + /** + * This constraint is case-sensitive. + * + * For example "public://foo.txt" and "public://FOO.txt" are treated as + * different values, and can co-exist. + * + * @var bool + */ + public $caseSensitive = TRUE; + /** * {@inheritdoc} */ public function validatedBy(): string { - return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator'; + return UniqueFieldValueValidator::class; } }
modules/user/user.install+25 −9 modified@@ -98,6 +98,7 @@ function user_requirements($phase): array { if ($phase !== 'runtime') { return []; } + $return = []; $result = (bool) \Drupal::entityQuery('user') ->accessCheck(FALSE) @@ -106,17 +107,32 @@ function user_requirements($phase): array { ->execute(); if ($result === FALSE) { - return [ - 'anonymous user' => [ - 'title' => t('Anonymous user'), - 'description' => t('The anonymous user does not exist. See the <a href=":url">restore the anonymous (user ID 0) user record</a> for more information', [ - ':url' => 'https://www.drupal.org/node/1029506', - ]), - 'severity' => REQUIREMENT_WARNING, - ], + $return['anonymous user'] = [ + 'title' => t('Anonymous user'), + 'description' => t('The anonymous user does not exist. See the <a href=":url">restore the anonymous (user ID 0) user record</a> for more information', [ + ':url' => 'https://www.drupal.org/node/1029506', + ]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + + $query = \Drupal::database()->select('users_field_data'); + $query->addExpression('LOWER(mail)', 'lower_mail'); + $query->groupBy('lower_mail'); + $query->having('COUNT(uid) > :matches', [':matches' => 1]); + $conflicts = $query->countQuery()->execute()->fetchField(); + + if ($conflicts > 0) { + $return['conflicting emails'] = [ + 'title' => t('Conflicting user emails'), + 'description' => t('Some user accounts have email addresses that differ only by case. For example, one account might have alice@example.com and another might have Alice@Example.com. See <a href=":url">Conflicting User Emails</a> for more information.', [ + ':url' => 'https://www.drupal.org/node/3486109', + ]), + 'severity' => REQUIREMENT_WARNING, ]; } - return []; + + return $return; } /**
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
1- Drupal core - Moderately critical - Access bypass - SA-CORE-2024-004Drupal Security Advisories · Nov 20, 2024