01Summary

Privilege chaining occurs when two or more individually safe permissions or capabilities, when combined, allow an actor to perform actions that should be restricted. Each permission alone may be harmless, but their interaction creates an unintended escalation path. This weakness is particularly dangerous because it can bypass access controls that were designed with only single permissions in mind.

02How It Happens

Privilege chaining arises when an application grants multiple permissions without considering their cumulative effect. For example, a user might have permission to create content and permission to modify system settings, but the application doesn't prevent them from using the content-creation interface to alter settings. Alternatively, a role might have permission to read sensitive data and permission to export it; combined, this allows unauthorized data exfiltration. The vulnerability typically stems from a lack of holistic access control review—developers implement individual permission checks but fail to audit whether certain combinations of permissions create unintended capabilities.

03Real-World Impact

Privilege chaining can lead to unauthorized data access, account takeover, or system compromise. A user with seemingly limited permissions might escalate their access to perform administrative actions, modify other users' data, or extract confidential information. In multi-tenant systems, this can result in cross-tenant data leakage. The impact depends on which permissions are chained, but the result is always a bypass of intended security boundaries.

04Vulnerable & Fixed Patterns

Vulnerable pattern
def user_action(user, action_type, target_id):
    # Check individual permissions
    if user.has_permission("create_content"):
        if action_type == "create":
            return create_content(target_id)
    
    if user.has_permission("modify_settings"):
        if action_type == "modify_settings":
            return modify_settings(target_id)
    
    # No check for whether create_content + modify_settings together
    # allow bypassing intended restrictions
    if user.has_permission("create_content") and user.has_permission("modify_settings"):
        # Attacker can chain these to perform unintended actions
        return execute_privileged_action(target_id)

Why it's vulnerable:
The code checks individual permissions but does not validate whether the combination of permissions should allow the requested action. An attacker with both "create_content" and "modify_settings" can chain them to perform actions neither permission alone should permit.

Fixed pattern
def user_action(user, action_type, target_id):
    # Define what permission combinations are allowed for each action
    required_permissions = {
        "create": ["create_content"],
        "modify_settings": ["modify_settings"],
        "execute_privileged": ["admin"],  # Require explicit admin role
    }
    
    # Check that user has ALL required permissions for this specific action
    if action_type not in required_permissions:
        raise ValueError("Unknown action")
    
    required = required_permissions[action_type]
    if not all(user.has_permission(perm) for perm in required):
        raise PermissionError(f"User lacks required permissions for {action_type}")
    
    # Perform the action only after explicit permission check
    if action_type == "create":
        return create_content(target_id)
    elif action_type == "modify_settings":
        return modify_settings(target_id)
Vulnerable pattern
<?php
function user_action($user, $action_type, $target_id) {
    // Check individual capabilities
    if ($user->has_cap('create_content') && $action_type === 'create') {
        return create_content($target_id);
    }
    
    if ($user->has_cap('export_data') && $action_type === 'export') {
        return export_data($target_id);
    }
    
    // No check for whether create_content + export_data together
    // allow unauthorized data exfiltration
    if ($user->has_cap('create_content') && $user->has_cap('export_data')) {
        // Attacker chains these capabilities
        return export_sensitive_data($target_id);
    }
}
?>

Why it's vulnerable:
Individual capability checks exist, but the code does not prevent a user with both "create_content" and "export_data" from combining them to export data they should not access.

Fixed pattern
<?php
function user_action($user, $action_type, $target_id) {
    // Define explicit permission requirements per action
    $action_permissions = array(
        'create'  => array('create_content'),
        'export'  => array('export_data', 'admin'),  // Require admin for export
        'modify'  => array('modify_own_content'),
    );
    
    if (!isset($action_permissions[$action_type])) {
        wp_die('Invalid action');
    }
    
    // Check that user has ALL required capabilities
    $required = $action_permissions[$action_type];
    foreach ($required as $cap) {
        if (!$user->has_cap($cap)) {
            wp_die('Insufficient permissions');
        }
    }
    
    // Perform action only after explicit permission check
    if ($action_type === 'create') {
        return create_content($target_id);
    } elseif ($action_type === 'export') {
        return export_data($target_id);
    }
}
?>

05Prevention Checklist

Map all permission combinations:
Document which permissions should and should not coexist, and audit your codebase for unintended interactions.
Use explicit role-based access control (RBAC):
Define roles with specific, limited permission sets rather than assigning individual permissions that may combine unexpectedly.
Require explicit authorization per sensitive action:
Do not rely on the presence of multiple individual permissions; check for a specific, dedicated permission or role for each sensitive operation.
Test permission boundaries:
Write tests that verify users with various permission combinations cannot perform actions outside their intended scope.
Review permission grants during code review:
When adding new permissions or roles, explicitly discuss and document what combinations are safe.
Implement principle of least privilege:
Grant only the minimum permissions necessary for each role; avoid "just in case" permissions.

06Signs You May Already Be Affected

Review your access logs and user activity for unexpected administrative actions performed by non-admin accounts, or data exports by users who should not have export access. Check your role and permission definitions for overlapping or redundant permissions that, when combined, might bypass intended restrictions. If you find users with seemingly unrelated permissions (e.g., both "create content" and "modify system settings"), investigate whether those combinations are intentional and properly guarded.

07Related Recent Vulnerabilities