BraveCMS's role assignment endpoint is missing the middleware that restricts it to authorized users. The POST route for /rights/update-role/{id} is registered in routes/web.php without the checkUserPermissions:assign-user-roles middleware that protects every equivalent privileged action. Any authenticated user, including one with the lowest possible role, can send a crafted request to promote themselves or any other account to Super Admin without any additional prerequisites.
Summary
| Field |
Value |
| CVE ID |
CVE-2026-35182 |
| Advisory |
GHSA-g58h-mvjw-f4hv |
| Severity |
High |
| CWE |
CWE-862 (Missing Authorization) |
| Package |
BraveCMS (PHP) |
| Affected |
2.0.0 |
| Patched |
2.0.6 |
| Vulnerability |
Missing middleware on role assignment route |
| Impact |
Horizontal and vertical privilege escalation to Super Admin |
BraveCMS uses a middleware named checkUserPermissions to gate privileged dashboard actions behind specific permission strings. The route responsible for updating a user's role, POST /dashboard/users/rights/update-role/{id}, was registered without this middleware. The controller method behind it accepts a role_id from the POST body and writes it directly to the target user record. Because nothing in the request lifecycle verifies that the caller holds the assign-user-roles permission, any authenticated user can invoke it freely.
How it works
The vulnerable route definition in routes/web.php is missing the permission middleware that every equivalent sensitive route carries:
// routes/web.php (vulnerable)
// All other privileged routes carry ->middleware('checkUserPermissions:some-permission')
// This one does not:
Route::post('/rights/update-role/{id}', [UserRightsController::class, 'update_role'])
->name('update-role');
// checkUserPermissions:assign-user-roles is absent
The controller method processes the request unconditionally:
// UserRightsController.php
public function update_role(Request $request, $id)
{
$user = User::findOrFail($id);
// No authorization check: role_id comes directly from POST body
$user->role_id = $request->input('role_id');
$user->save();
return redirect()->back()->with('success', 'Role updated.');
}
The role ID values map to fixed privilege levels in the application. Role ID 4 corresponds to Super Admin, which carries unrestricted access to every dashboard feature including user management, plugin installation, content modification, and system settings.
Root Cause
A single missing middleware declaration on one route. The checkUserPermissions middleware is correctly applied to every other sensitive route in the application. The role assignment route was either added after the security review that covered the rest of the routes, or the middleware was accidentally omitted during a refactor. Because Laravel route definitions are applied independently per route, omitting the middleware from one entry silently opens it to all authenticated users regardless of their role.
Prerequisites
- A valid BraveCMS account with any role, including the lowest privilege level (role_id 1).
- Knowledge of the role update endpoint URL, which follows the standard dashboard URL pattern and is discoverable by browsing the application or reading the source.
- No victim interaction is required. The attacker promotes their own account and no administrator needs to take any action.
Fix
Add the missing middleware to the route definition in routes/web.php:
// routes/web.php (fixed)
Route::post('/rights/update-role/{id}', [UserRightsController::class, 'update_role'])
->name('update-role')
->middleware('checkUserPermissions:assign-user-roles');
As an additional layer of defence, the controller itself should also validate that the caller holds the required permission, following the defence-in-depth principle that authorization should not rely solely on the routing layer:
// UserRightsController.php (hardened)
public function update_role(Request $request, $id)
{
// Explicit authorization check as a second layer
if (!auth()->user()->hasPermission('assign-user-roles')) {
abort(403, 'Unauthorized');
}
$user = User::findOrFail($id);
// Validate that role_id is a known, valid role
$validRoles = Role::pluck('id')->toArray();
$request->validate(['role_id' => 'required|in:' . implode(',', $validRoles)]);
$user->role_id = $request->input('role_id');
$user->save();
return redirect()->back()->with('success', 'Role updated.');
}
The validation of role_id against known role IDs also prevents an attacker from assigning a non-existent or reserved role value to cause unexpected application behavior.
Key Takeaways
- Middleware-based authorization must be applied consistently to every route that touches a privileged action. A single unprotected route is enough to bypass the entire permission model regardless of how well every other route is protected.
- Authorization should be enforced at both the routing layer and the controller layer. If the middleware is accidentally removed or bypassed, a controller-level check acts as a second barrier. Relying on routing alone creates a single point of failure.
- Low-privilege accounts are a meaningful starting point for privilege escalation. The attacker only needs the ability to send authenticated HTTP requests, which any registered user has. The missing check turns every user account into a potential Super Admin.
- Post-exploitation persistence makes delayed patching dangerous. Once an attacker promotes their account and creates a backdoor, patching the route does not revoke the elevated access already granted. Any incident response must include an audit of all user role assignments, not just a code fix.