CWE-269: Improper Privilege Management
Overview
Improper privilege management occurs when a process, service, or account holds more privilege than its current task requires, keeps elevated privilege after the privileged operation is complete, or can have its privilege level raised through an unprotected path. It is a lifecycle problem - the standing privilege level granted to an identity over time - not a single resource's permission bits and not a single request's authorization decision.
Relationship to Other CWEs
CWE-269 is a Class-level child of CWE-284 (Improper Access Control), and MITRE marks it Discouraged for mapping new findings: it covers the whole privilege lifecycle, so a finding recorded against it usually has a more precise child. Prefer that child where one fits, and use this page for the general shape or when an existing finding is already tagged CWE-269. This page and the children with dedicated guidance here are:
- CWE-269 (this page) - the standing privilege level an identity holds over time, rather than a single resource's permission bits or a single request's authorization decision.
- CWE-250 (Execution with Unnecessary Privileges) - a component configured to run at a higher privilege than any of its operations need, for its whole lifetime.
- CWE-272 (Least Privilege Violation) and CWE-273 (Improper Check for Dropped Privileges) - performing the drop, and confirming it actually happened. Both sit under the intermediate class CWE-271 (Privilege Dropping / Lowering Errors), which has no page here.
- CWE-274 (Improper Handling of Insufficient Privileges) - the product mishandling a privilege it was denied, rather than misusing one it holds.
Two neighboring areas of the tree look similar but govern different things. CWE-732 (Incorrect Permission Assignment for Critical Resource) is about a resource's permission bits or ACL being set incorrectly; CWE-269 is about the standing privilege level of the process or account acting on resources. CWE-862 (Missing Authorization) and CWE-863 (Incorrect Authorization) govern whether a single request should be allowed at the caller's current privilege level; CWE-269 governs whether that privilege level was acquired, retained, or escalated correctly over time. If the finding is about a specific permission assignment or a specific request's access decision, prefer those pages instead.
OWASP Classification
A06:2025 - Insecure Design
Risk
High: A process or account running with unnecessary privilege turns any compromise of that process - a code execution bug, a dependency vulnerability, a hijacked session - into a full privilege escalation instead of a contained failure. An unprotected elevation path lets an attacker grant themselves standing access that persists well beyond the original exploit.
Remediation Steps
Core Principle: Grant only the minimum privilege needed to start, drop to a lower privilege level immediately after the privileged step finishes, and gate any privilege change behind an authenticated, authorized, auditable path with a defined expiry or revocation.
Trace the Privilege Lifecycle
- Source: The point where a process, service, or account acquires elevated privilege - a setuid/setgid call, a service account role binding, an admin flag, a container capability grant
- Sink: Every operation the process or account performs while holding that elevated privilege, including operations that no longer need it
- Missing Controls: No point in the code path where privilege is dropped after the privileged operation completes, or an elevation path that changes privilege without authentication, authorization, or an audit trail
Apply Least Privilege and Drop Elevation Promptly (Primary Defense)
- Start processes, services, and accounts with the minimum privilege level required, not the broadest available
- Acquire elevated privilege immediately before the operation that requires it, and drop back to a lower-privileged identity immediately after - do not let a process or session continue running at a higher level than its remaining work needs
- Prefer capability-scoped or role-scoped grants over broad administrative or root-equivalent privilege, even when the broader grant is more convenient to configure
Gate Elevation Paths (Defense in Depth)
- Treat privilege elevation as a controlled transition: require explicit authentication and authorization, log who requested and approved it, and bind temporary grants to an expiry or explicit revocation step
- Log privilege acquisition and drop events, alert on processes holding elevated privilege longer than expected, and periodically review privileged accounts for drift
Test the Privilege Boundary
- Verify the process or account cannot perform privileged actions after the drop point
- Verify elevation paths reject unauthorized or expired requests
- Attempt to hold an elevated grant past its expected lifetime and confirm it is revoked or expires
- Re-scan with the security scanner or run a privilege-escalation-focused review to confirm the finding is resolved
Common Vulnerable Patterns
// VULNERABLE - pseudo-code
elevateToAdmin()
readConfig()
writeLog()
processRequest() // still running as admin - no longer needed
// Attack: any vulnerability in processRequest() now executes with admin privilege
// Result: a low-severity bug elsewhere becomes a full privilege escalation
Why this is vulnerable: every line after readConfig() and writeLog() runs with a privilege none of it needs, so the privilege no longer serves the code; it sets the ceiling on every defect the code contains. Nothing here is exploitable on its own, which is why the drop gets deferred. What it decides is how bad the next bug is: an injection that would have run as a service account runs as admin, and a file write confined to one directory reaches the whole system.
This is a scope problem rather than a missing control. The elevation is required for the two calls that follow it and is never given back, because the program behaves identically whether or not the privilege is dropped, so the omission has no symptom until something else goes wrong. Where the platform allows it, acquire the privilege for the duration of the operation and restore it in a construct that runs on every exit path, including the ones that throw, so there is nothing to remember.
Secure Patterns
// SECURE - pseudo-code
elevateToAdmin()
readConfig()
writeLog()
dropToStandardUser() // privilege released as soon as the privileged step is done
processRequest()
Why this works: The window during which the process holds elevated privilege is limited to the specific operation that requires it. A subsequent compromise of processRequest() inherits only the standard-user privilege level, containing the blast radius instead of granting the attacker administrative capability.
Common Pitfalls
- Elevating for the whole request instead of the specific call: Wrapping an entire request handler in an elevated context because one internal step needs it, rather than scoping the elevation tightly around that one step.
- Treating a container or service account's broad IAM role as a convenient default: Granting a wide role, such as full admin on a cloud account, because it avoids configuring a narrower one, which turns any compromise of that service into a full-account compromise.
- Building an elevation endpoint without an expiry: Adding a "grant temporary admin" feature that never automatically revokes, so a forgotten or unrevoked grant becomes a permanent standing privilege.