HIGH axumprivilege escalation

Privilege Escalation in Axum

How Privilege Escalation Manifests in Axum

In Axum applications, privilege escalation typically emerges from two flawed assumptions: that authentication equals authorization, and that client-provided identifiers are trustworthy. Axum's extractor-based handler design, while ergonomic, can obscure authorization logic. A common vulnerable pattern is a handler that extracts a user ID from the request path or body and uses it to fetch or modify data without verifying that the authenticated user has rights to operate on that specific resource.

Horizontal Escalation (BOLA/IDOR): Consider an endpoint that allows users to update their profile. A vulnerable implementation might directly use the id path parameter to query the database, assuming the authenticated user can only access their own record. An attacker can simply change the path value (e.g., PUT /users/123 to PUT /users/456) to manipulate another user's data if no ownership check exists.

// VULNERABLE: No authorization check beyond authentication
#[derive(Deserialize)]
struct UpdateUserParams {
    id: u64,
    email: Option,
}

async fn update_user_handler(
    Extension(user): Extension<User>, // User from auth middleware
    Json(params): Json<UpdateUserParams>,
) -> impl IntoResponse {
    let user = sqlx::query!(
        "SELECT * FROM users WHERE id = ?",
        params.id
    ).fetch_one(&pool).await.unwrap();
    // ... update logic
}

Vertical Escalation (BFLA): Axum apps often have role-based endpoints (e.g., /admin/users). A flaw occurs when role validation is missing or based on client-controlled data. For instance, an admin creation endpoint that trusts a role field in the request body allows any authenticated user to grant themselves administrative privileges.

// VULNERABLE: Role is taken from request body, not server-side session
#[derive(Deserialize)]
struct CreateUserRequest {
    username: String,
    password: String,
    role: String, // Attacker sets this to "admin"
}

async fn create_user_handler(
    Extension(user): Extension<User>,
    Json(req): Json<CreateUserRequest>,
) -> impl IntoResponse {
    // Only checking if user is authenticated, not if they have 'admin' role
    sqlx::query!(
        "INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)",
        req.username,
        hash(req.password),
        req.role // Dangerous: client-controlled
    ).execute(&pool).await.unwrap();
}

Axum's flexibility with extractors (e.g., Json<T>, Path<T>) can lead to implicit trust in deserialized data. Additionally, when using OpenAPI generators like utoipa, missing security or securitySchemes definitions in the spec can lead to runtime endpoints that lack documented authorization requirements, creating a gap between design and implementation.

Axum-Specific Detection

Detecting privilege escalation in Axum APIs requires testing for unauthorized access across different user contexts. Manual testing involves:

  • Using tools like curl or Postman to send requests with different authentication tokens (e.g., user vs. admin) to the same endpoint and observing if access rights change unexpectedly.
  • Manipulating path parameters (e.g., /users/{id}) or JSON body fields (e.g., {"role":"admin"}) to probe for horizontal or vertical escalation.

Automated scanning with middleBrick systematically probes for Broken Function Level Authorization (BFLA) and Broken Object Level Authorization (BOLA). When you submit an Axum API endpoint, middleBrick:

  • Identifies all actionable endpoints from the OpenAPI spec (if available) or by crawling, including those with path parameters like /users/{id}.
  • Tests each endpoint with multiple privilege contexts: it first establishes a baseline authenticated session (often as a low-privilege user), then sends crafted requests that attempt to access higher-privilege functions or objects belonging to other users.
  • Analyzes responses for status codes (e.g., 200 OK instead of 403 Forbidden), data leakage, or state changes that indicate successful escalation.

For Axum APIs that publish an OpenAPI spec (e.g., via utoipa), middleBrick cross-references the spec's declared security requirements with runtime behavior. If the spec marks an endpoint as requiring admin scope but the runtime endpoint responds with 200 for a non-admin token, that's a clear finding.

You can scan your Axum API from the terminal using the middleBrick CLI:

middlebrick scan https://api.youraxumapp.com

Or integrate scanning into your CI/CD pipeline for Axum projects using the middleBrick GitHub Action. This allows you to fail builds if a new privilege escalation vulnerability is introduced or if the security score drops below a threshold.

Axum-Specific Remediation

Remediation in Axum centers on enforcing server-side authorization checks that are independent of client input. The core principle: never trust identifiers or roles from the request; always derive authorization from the authenticated user's session and the target resource's ownership.

1. Implement Centralized Authorization Middleware
Create middleware that runs after authentication and attaches the user's roles/claims to the request Extension. This ensures every handler has access to the verified user identity.

use axum::{
    async_trait, extract::{FromRequestParts, State},
    http::{request::Parts, StatusCode},
    response::Response,
};
use std::sync::Arc;

struct AuthUser {
    id: u64,
    roles: Vec<String>,
}

#[async_trait]
implement<FromRequestParts> for AuthUser {
    async fn from_request_parts(
        parts: &mut Parts,
        state: &State<Arc<AppState>>,
    ) -> Result<Self, (StatusCode, String)> {
        // Extract token, validate, fetch user from DB, attach roles
        // This runs for every request after authentication
        let user = verify_token(parts).await?;
        Ok(user)
    }
}

// Apply globally or per-route
let app = Router::new()
    .route_layer(axum::middleware::from_fn(auth_middleware));

2. Enforce Ownership Checks in Handlers
For resource-specific endpoints (e.g., /users/{id}), compare the path id against the authenticated user's ID (or their admin status).

async fn update_user_handler(
    AuthUser { id: current_user_id, roles }: AuthUser, // From middleware
    Path(user_id): Path<u64>,
    Json(mut updates): Json<UpdateUserDto>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    // Horizontal check: users can only update themselves unless admin
    if current_user_id != user_id && !roles.contains(&"admin".to_string()) {
        return Err((StatusCode::FORBIDDEN, "Cannot update other users".into()));
    }
    // Proceed with update...
    Ok(Json(update_user(user_id, updates).await?))
}

3. Validate Role Claims for Privileged Endpoints
For admin-only routes, explicitly check for the required role. Do not rely on a role field from the client.

async fn admin_dashboard_handler(
    AuthUser { roles, .. }: AuthUser,
) -> impl IntoResponse {
    if !roles.contains(&"admin".to_string()) {
        return (StatusCode::FORBIDDEN, "Admin access required");
    }
    // Admin logic...
}

4. Keep OpenAPI Specs in Sync
If you generate an OpenAPI spec with utoipa, annotate endpoints with required security scopes. This documents intent and allows tools like middleBrick to verify compliance.

#[utoipa::path(
    post,
    path = "/admin/users",
    security(("bearer_auth" = ["admin"])), // Declares admin scope required
    responses(...)
)]
async fn create_admin_user_handler(...) -> ... { ... }

5. Adopt Least Privilege
Design your data model so that users operate within a tenant or scope. Use database row-level security (e.g., PostgreSQL RLS) as a defense-in-depth layer, but never rely on it solely—application-level checks are essential.

After implementing fixes, rescan with middleBrick to verify that the BFLA/BOLA findings are resolved and that the security score improves. The Pro plan's continuous monitoring can alert you if a future code change reintroduces a privilege escalation flaw.

Frequently Asked Questions

How does middleBrick detect privilege escalation in Axum APIs?
middleBrick sends crafted requests to each endpoint, simulating different user privilege levels. For endpoints with path parameters (e.g., /users/{id}), it substitutes IDs from other users to test for horizontal escalation. For role-based endpoints, it uses low-privilege tokens to access admin-only functions, checking for improper authorization. If an endpoint returns a 200 OK or performs state changes with unauthorized requests, middleBrick flags it as a BFLA/BOLA vulnerability. When an OpenAPI spec is provided, it also verifies that declared security requirements match runtime behavior.
Can I integrate middleBrick scans into my Axum project's CI/CD pipeline?
Yes. Use the middleBrick GitHub Action in your workflow to scan your Axum API on every push or pull request. Configure it to fail the build if the security score drops below a threshold (e.g., below 'B') or if new critical findings appear. This ensures privilege escalation and other vulnerabilities are caught before deployment. Example usage: add a step with 'uses: middlebrick/action@v1' and set 'fail-on-score-below: 80'.