Privilege Escalation
Privilege Escalation at a glance
Overview
Privilege escalation occurs when a user can perform actions or access resources beyond their assigned role or permission level. This can be vertical (gaining admin rights) or horizontal (accessing another user's account with same privilege level).
Common causes include missing role validation on privileged endpoints, accepting role information from client requests, inconsistent authorization checks across the application, and relying on obscurity rather than enforcement.
Where it occurs
Privilege escalation vulnerabilities arise from endpoints missing role validation, accepting user_role or permissions from request parameters, authorization middleware not applied to all routes, client-side only access control checks, and predictable URLs to admin functions.
Impact
Privilege escalation enables complete system compromise, unauthorized data access and manipulation, ability to create or delete users and resources, compliance violations, and can serve as a stepping stone for further attacks.
Prevention
Implement centralized authorization that checks roles and permissions for every request. Never accept role or permission information from the client. Apply authorization middleware by default to all routes. Use deny-by-default policies. Test role boundaries with automated tests.
Examples
Switch tabs to view language/framework variants.
Admin routes accessible without role validation
Admin endpoints lack authorization middleware, allowing any authenticated user to perform privileged operations.
// Vulnerable: No role check on admin route
const express = require('express');
const app = express();
// Authentication middleware (only checks if user is logged in)
function requireAuth(req, res, next) {
if (!req.session.userId) {
return res.status(401).json({ error: 'Not authenticated' });
}
// Fetch user from DB
req.user = getUserById(req.session.userId);
next();
}
// Admin route - only checks authentication, not role!
app.delete('/api/admin/users/:id', requireAuth, (req, res) => {
const userIdToDelete = req.params.id;
// Missing: Check if req.user.role === 'admin'
deleteUser(userIdToDelete);
res.json({ success: true, message: 'User deleted' });
});
// Any authenticated user can call this!
app.get('/api/admin/analytics', requireAuth, (req, res) => {
const revenue = getTotalRevenue();
const userStats = getAllUserStats();
res.json({ revenue, userStats });
});- Line 14:
- Line 23:
The vulnerable code only verifies that a user is authenticated (logged in) but never checks their role or permissions. The requireAuth middleware confirms a valid session exists but doesn't validate authorization for privileged operations.
Admin routes are protected only by authentication, meaning any user with a valid session cookie can access sensitive business analytics, delete users, and perform other administrative actions.
This violates the principle of least privilege - users should only have access to resources and operations necessary for their role.
// Secure: Centralized role-based authorization
const express = require('express');
const app = express();
// Authentication middleware
function requireAuth(req, res, next) {
if (!req.session.userId) {
return res.status(401).json({ error: 'Not authenticated' });
}
req.user = getUserById(req.session.userId);
next();
}
// Authorization middleware - reusable role checker
function requireRole(role) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' });
}
if (req.user.role !== role) {
// Log unauthorized access attempt
logger.warn('Unauthorized access attempt', {
userId: req.user.id,
userRole: req.user.role,
requiredRole: role,
endpoint: req.path
});
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient privileges'
});
}
next();
};
}
// Protected admin routes
app.delete('/api/admin/users/:id',
requireAuth,
requireRole('admin'), // ← Role check enforced
(req, res) => {
const userIdToDelete = req.params.id;
// Additional check: prevent self-deletion
if (userIdToDelete === req.user.id) {
return res.status(400).json({ error: 'Cannot delete own account' });
}
deleteUser(userIdToDelete);
// Audit log
auditLog.record({
action: 'USER_DELETED',
adminId: req.user.id,
targetUserId: userIdToDelete
});
res.json({ success: true, message: 'User deleted' });
}
);
app.get('/api/admin/analytics',
requireAuth,
requireRole('admin'), // ← Role check enforced
(req, res) => {
const revenue = getTotalRevenue();
const userStats = getAllUserStats();
res.json({ revenue, userStats });
}
);- Line 16:
- Line 25:
- Line 50:
The secure implementation introduces a requireRole() middleware factory that generates role-specific authorization checks. This middleware verifies both authentication and the user's role before allowing access.
Authorization is enforced at the route level by chaining middleware: requireAuth → requireRole('admin') → handler. This ensures no admin operation can be performed without proper role validation.
Failed authorization attempts are logged with context (user ID, required role, endpoint) enabling security teams to detect and respond to potential privilege escalation attempts.
Additional safeguards like preventing self-deletion and audit logging of sensitive operations provide defense in depth.
Engineer Checklist
-
Require explicit role checks on all privileged endpoints
-
Use centralized authorization middleware
-
Never accept role/permission data from client
-
Apply authorization by default (deny-by-default)
-
Test role boundaries with automated tests
-
Log all privilege changes and admin actions
-
Implement least privilege for all accounts
-
Regularly audit privileged operations
End-to-End Example
A regular user accesses admin functionality by directly calling privileged API endpoints without proper authorization checks.
# Vulnerable: No role check on admin endpoint
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from .models import User
@login_required
def delete_user(request, user_id):
# Missing role authorization check!
user = User.objects.get(id=user_id)
user.delete()
return JsonResponse({'status': 'deleted'})# Secure: Enforce role-based access control
from django.http import JsonResponse, HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from functools import wraps
from .models import User
def require_role(role):
def decorator(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden('Authentication required')
if request.user.role != role:
return HttpResponseForbidden('Insufficient privileges')
return view_func(request, *args, **kwargs)
return wrapper
return decorator
@login_required
@require_role('admin')
def delete_user(request, user_id):
# Only admins can reach this code
user = User.objects.get(id=user_id)
user.delete()
return JsonResponse({'status': 'deleted'})Discovery
Test if role/privilege fields can be manipulated in requests or if authorization checks are missing for privileged operations.
-
1. Test mass assignment on user update
httpAction
Include role field in profile update to test for mass assignment
Request
PUT https://api.example.com/api/users/meHeaders:Authorization: Bearer regular-user-tokenContent-Type: application/jsonBody:{ "name": "Alice", "email": "alice@example.com", "role": "admin", "is_superuser": true }Response
Status: 200Body:{ "id": 1042, "name": "Alice", "email": "alice@example.com", "role": "admin", "is_superuser": true, "message": "Profile updated successfully" }Artifacts
mass_assignment_vuln privilege_escalation admin_access_granted -
2. Test unprotected admin endpoint
httpAction
Access admin-only endpoint without admin role
Request
GET https://api.example.com/api/admin/usersHeaders:Authorization: Bearer regular-user-tokenResponse
Status: 200Body:[ { "id": 1, "username": "admin", "email": "admin@company.com", "role": "admin" }, { "id": 2, "username": "alice", "email": "alice@example.com", "role": "user" }, "... (full user directory exposed)" ]Artifacts
missing_authorization admin_endpoint_accessible user_enumeration
Exploit steps
Attacker escalates privileges by manipulating role fields or accessing unprotected admin endpoints, gaining full administrative control.
-
1. Escalate to admin via mass assignment
Grant admin role through profile update
httpAction
Update profile with role='admin' to gain elevated privileges
Request
PUT https://api.example.com/api/users/meHeaders:Authorization: Bearer attacker-tokenContent-Type: application/jsonBody:{ "role": "admin", "permissions": [ "users:read", "users:write", "users:delete", "system:admin" ] }Response
Status: 200Body:{ "id": 9999, "username": "attacker", "role": "admin", "permissions": [ "users:read", "users:write", "users:delete", "system:admin" ], "message": "You now have admin access" }Artifacts
admin_privileges full_system_access mass_assignment_exploit -
2. Delete other user accounts
Use new admin privileges to delete users
httpAction
Delete competitor admin accounts
Request
DELETE https://api.example.com/api/admin/users/1Headers:Authorization: Bearer attacker-token-now-adminResponse
Status: 200Body:{ "message": "User admin (ID: 1) deleted successfully", "deleted_user": { "username": "admin", "email": "admin@company.com" } }Artifacts
account_deletion admin_account_removed system_takeover
Specific Impact
Complete system compromise through elevated privileges, unauthorized administrative actions.
Fix
Implement centralized authorization decorators that verify user roles before allowing access to privileged operations. Never trust client-provided role information. Apply authorization checks by default using middleware, with explicit opt-out only for public endpoints. Use deny-by-default policies and log all privileged operations.
Detect This Vulnerability in Your Code
Sourcery automatically identifies privilege escalation vulnerabilities and many other security issues in your codebase.
Scan Your Code for Free