Privilege Escalation
§Basic information
Privilege escalation is any path from the access you were granted to access you were not. The app already trusts you a little — a low-priv member, a scoped token, a namespaced tenant, an unprivileged local user — and escalation converts that foothold into control of the tenant, the instance, or the host. It splits into vertical (low role → admin/root/cluster-admin) and horizontal-to-vertical (a bot/service identity minted on your behalf inherits more than you have).
The mechanism is almost always a missing or wrong authorization check on a boundary you can reach: a route gated on "has any permission" instead of the specific one, a privilege attribute bound straight from a request body, a token that survives its own "removal", a privileged file-writer that follows your symlink. The job is to enumerate every boundary between what you hold and what you want, then probe each one — hidden UI is never access control.
§Methodology
- Map your effective privileges — enumerate the roles, scopes, and OS/cloud identities you actually hold, and list every action one tier above you (admin routes, other tenants' objects, SYSTEM/root files).
- Force-browse the hidden management surface with your low-priv session —
/admin,/setup,/start,/install, RBAC/group-management routes, and their.jsonsiblings. A hidden menu link does not mean an enforced check. - Run the A → B → A test. Capture a request as a high-priv user (B), replay it verbatim with your low-priv session/token (A). If it still authorizes, that's vertical escalation.
- Fuzz for privilege attributes. Add role/permission fields the form never showed you (
role,is_admin,bbp-forums-role,permissions) and see if they stick on the next read (mass-assignment). - Trace every auto-provisioned identity — API tokens, project/CI bots, integration scripts, cloud roles. Test whether the machine account's effective scope exceeds the principal that created it.
- Confirm revocation actually revokes. After "removing" a role or restricting a token, replay the old token/session — broken revocation leaves orphaned privileged rows.
- On a host, watch a privileged process (ProcMon / strace) and note every DLL it loads by bare name and every file it writes into a user-writable dir — those are your plant/redirect primitives.
#605720). Always diff .json endpoints too — they leak ids and permission maps to read-only roles.§Escalation vectors
Pick the vector that matches your surface; each has its own confirm-and-weaponize primitive.
RBAC routes & forced browsing
The UI hides an admin/group-management link, but the route is live and authorizes on "has any valid permission" instead of the specific one. Browse to it with a single low-priv permission and edit your own group's permission set upward.
Setup/bootstrap/first-run endpoints are a sub-case: often left live after install and gated more loosely than normal admin routes.
Mass-assignment of a role attribute
A registration or profile endpoint reads a role/permission field straight from request input. Set it to the highest value. Chained through a privileged victim's browser (no CSRF token), it becomes a one-click escalation.
admin), pivot through an intermediate role that grants a scripting/automation primitive — a bot role that permits an integration/webhook whose server-side script runs in app context and calls Roles.addUserRoles(you, "admin") indirectly (#501081). The deny only covered the front door.Self-modifying credentials & broken revocation
A scoped/least-privilege token that can still reach the endpoint that manages tokens is not restricted — it re-scopes itself. Likewise a token that stays authorized after the visible membership is "removed".
Bot / service identity inheritance
Identities auto-provisioned on behalf of a restricted principal frequently default to a higher privilege than that principal. Mint one, then measure its real scope.
Impersonation / "sudo" session theft
Support-login / impersonate features must never let the impersonated user see or reuse the elevated session. Where the impersonation session surfaces in the victim's own Active Sessions, they copy it, plant it, and hand themselves back the admin.
Settings / config-channel hijack
A "benign" settings permission that controls an out-of-band channel (SMTP, notifications, webhooks) is equivalent to account takeover of everyone, because it intercepts password-reset and invite mail.
Cloud IAM & Kubernetes
The escalation sinks are wildcard IAM grants, tenant-supplied config rendered into a privileged runtime, and readable secret/state stores reachable from a pod.
Windows / Linux local privilege escalation
The recurring primitives are bare-name DLL loads with a writable PATH dir, and privileged writes into a user-writable dir redirected with a junction/symlink onto a phantom-DLL or auto-run path.
§Bypasses
| Filter / control | Bypass | Seen in |
|---|---|---|
| Hidden UI menu | Force-browse the live route; authz checks "any permission", not the specific one | #605720 |
| Direct self-assign-admin blocked | Pivot through an intermediate bot role granting server-side scripting → call the privileged API | #501081 |
| Scoped-token restriction | The token reaches the token-management endpoint and PUTs its own scope; brute-force IDs (no throttle) | #1193321 |
| Membership "revoked" | Captured Authorization token stays valid after membership set to None (broken revocation) | #1596663 |
| Admin-only setup route | Bootstrap POST /start shared with editor/operator roles resets the admin password | #2197796 |
| Blacklist capability check | shop_manager blocked only from administrator, so it assigns any other role → XSS → admin | #403039 |
| Symlink guard on file restore | Vendor blocks NTFS symlinks but not directory junctions; a junction redirects the write | #980500 |
SeCreateSymbolicLink needed | Directory-junction + \RPC Control object-manager symlink needs no symlink privilege | #945122 |
| Signed-app injection guard | XPC helper checks code signature but not version → inject into an old signed build | #966494 |
DB FILE WRITE privilege | SELECT ... INTO OUTFILE never enforces it → arbitrary write as the engine OS user | #3780695 |
| Canonicalization check vs use | Policy getCanonicalPath() resolves ../ (trusted) while the loader treats it as a literal JAR entry | #3452696 |
#2279010); a read-only role that can list cluster secrets is near cluster-admin (#1249583). Rate the permission by what it can reach, not by its name.§Escalation & impact
Escalation is usually the tail of a chain that ends at total control:
- Foothold → bot/service identity → higher scope — guest →
botrole → integration script → global admin (#501081); external user → project token → internal bot reads every repo (#1193062). - Settings/config permission → full ATO — repoint SMTP → capture every password-reset/invite email → take over any account (
#2279010). See the