high · 9.1CVE-2026-75837Sep 17, 2026

CVE-2026-75837: Grav Group Blueprint Missing Authorization Allows Privilege Escalation to Super-Admin

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A delegated Grav admin who can manage users but is not a super-admin can edit a user group to grant themselves full super-admin rights, because the group permission form never checks whether the…

Packagegetgrav/grav
Ecosystemcomposer
Affected< 2.0.14
Fixed in2.0.14

The problem

The core Flex group blueprint (system/blueprints/user/group.yaml, lines 48-55) defines an access field but omits the security@: admin.super guard that the sibling account.yaml blueprint carries on its equivalent fields.

Any authenticated operator holding admin.users.update (but not admin.super) can POST to the group-edit endpoint and include access[admin][super]=true in the body. Because no field-level guard exists, Blueprint::dynamicSecurity() never marks the field as ignored, so BlueprintSchema::filterArray() retains it and FlexObject::save() writes it to user/config/groups.yaml.

Every member of that group is now a super-admin on the next request, opening the scheduler (cron to RCE) and Twig evaluation.

Proof of concept

A working proof-of-concept for CVE-2026-75837 in getgrav/grav, with the exact payload below.

http
POST /admin/accounts/groups/ops HTTP/1.1
Host: target.example.com
Cookie: grav-site-[hash]=<session>
Content-Type: application/x-www-form-urlencoded

data[access][admin][super]=1

The guard mechanism works like this: when a blueprint field carries security@: admin.super, Blueprint::dynamicSecurity() (Blueprint.php:644-662) sets validate.ignore=true for non-super callers, and BlueprintSchema::filterArray() then drops the field before FlexObject::save() is reached.

The account.yaml blueprint was patched (CVE-2026-42613) to add this guard on its access and groups fields, but the functionally identical access block in group.yaml never received it.

The field's check_authorize: false annotation provides zero PHP-level enforcement (grep confirms no PHP consumers), so security@ was the only real control. Its absence means the nested admin.super: true leaf passes through Validation::filterArray (type: array, value_type: bool) without obstruction.

The fix in 2.0.14 adds security@: admin.super to the access field in group.yaml, mirroring the guard already present in account.yaml, so non-super callers have the field stripped before it reaches storage.

The fix

Upgrade to getgrav/grav 2.0.14 (patch commit 5192316f5bf0f033636f43561829d7fc13a4ab64). The fix adds security@: admin.super to the access field in system/blueprints/user/group.yaml. As an interim control, remove admin.users.update from any account that should not hold super-admin, or restrict group-edit routes to super-admin only via your web server.

Reported by zx (Jace).

References: [1][2][3][4]

Related research