Auth Bypass → Impact Escalation
You have a suspected auth weakness. Your job is to turn it into the
strongest impact you can demonstrate — admin access, cross-tenant data
read, token forgery, or session takeover — and persist a finding with the
specific action that proves it. Reporting "missing auth check" without
showing what it gets you is not a finding.
When this skill applies
- A route responds 200 with no
Authorization / session cookie when it
should require auth (compare to a known-auth'd record's response).
- JWT verification accepts
alg: none, a key-confused HMAC secret, or a
token signed with the public key as HMAC secret.
- A session ID is predictable (sequential, time-based, or short).
- A request body / query has a role/tenant/user field that, when changed,
changes the response (
role=user → role=admin, tenant=A → tenant=B).
- Audit harness flagged CWE-287, CWE-862, CWE-863, CWE-639, CWE-284, or
CWE-639 — even theoretical.
Workflow
1. Establish baselines
Use list_auth_sessions to see which auth contexts already exist for the
host. Use auth_session_lookup to pull the actual headers for at least
two roles if you have them (anon + user is enough; user + admin
is ideal). remember the role boundaries as auth-roles.
If only one role is available, use query_records to find an
unauthenticated record for the same endpoint to anchor the no-auth
baseline.
2. Classify the weakness
Pick one mechanism to attack — don't shotgun:
- Missing auth on route: pick the suspect endpoint, send the request
with
replay_request and extra_headers: {} to strip cookies. If you
still get the privileged response, the route doesn't check auth at all.
- Weak JWT: decode the token (base64 each segment). Look at
alg,
iss, aud, exp, sub, custom claims (role, tenant). Re-mint:
alg: none: build header {"alg":"none","typ":"JWT"}, keep payload,
drop signature (empty 3rd segment).
- Key confusion: sign HMAC with the public key as the secret.
- Empty / weak HMAC secret: try
"", secret, key, the app name.
- Session prediction: collect 5-10 valid session IDs from
query_records
on login responses. If they're sequential, time-encoded, or short
hex/base64, forge a neighboring ID via replay_request.
- Privilege field client-controlled: send the request with the role
field flipped (
role=admin, is_admin=true, tenant_id=<other>).
3. Confirm escalation, don't stop at "different"
A 200 with different content is suspicious, not proof. Confirm with
ONE of:
- The response body contains data the original role couldn't see (admin
email, another user's email, a hidden flag).
- A privileged action succeeds:
DELETE /api/users/X, POST /api/admin/X,
GET /api/users/all returns a list of more than just the current user.
- A subsequent request using the forged session is accepted as that user
(look up the user's email or role in the response).
Send the smallest privileged request that proves the boundary was
crossed. remember it with key auth-bypass-proof and include the
exact request line.
4. Map the blast radius
Briefly enumerate the surface the bypass unlocks. Don't enumerate at
scale — show:
- 1 admin-only endpoint reachable.
- 1 cross-tenant record visible.
- 1 sensitive write that succeeds (if write paths exist; do not
destructively delete or modify real data — read or create-with-rollback
if possible).
This sizes the severity in the finding without spamming requests.
5. Persist the finding
Call report_finding:
severity: critical if you reached admin or full cross-tenant; high
for same-tenant privilege escalation or read of another user's data.
title: name the mechanism and the proven action — "JWT alg=none accepted; forged admin token reads /api/admin/users (1234 users)".
cwe_id: CWE-287 (auth missing), CWE-863 (broken auth check),
CWE-639 (IDOR-style), CWE-345 (insufficient verification of data),
CWE-347 (weak signature) — pick the one that matches the mechanism.
description: 3 sentences. Mechanism, proof, blast radius. Include the
decoded JWT header / forged session ID / mutated field value in the
description body so the reproduction is unambiguous.
If the audit harness already raised this, update_finding to triaged
status referencing the new proof.
Pitfalls
- Do not perform destructive writes (
DELETE, PUT overwriting,
password resets) on someone else's account. Read-only proofs are
sufficient for severity.
- A forged token that returns 200 with an empty body proves nothing.
The body must contain data the original role didn't have access to.
- Some apps return 200 with
error: "unauthorized" in the JSON body —
read the body, not just the status, before claiming bypass.
- If the app has a "demo admin" account that's publicly accessible by
design, that's not a bypass — confirm by checking documentation or
a comment in the code (
--source mode).
- IDOR (object-level missing auth) is a related but distinct pattern —
if your finding is "can read another user's order by changing the
ID", use
idor-blast-radius instead.
Output expectations
- One
report_finding with the strongest proven impact.
- A
remember note with the canonical bypass request.
- Plan item marked
done.
1---2name: escalate-auth-bypass3description: Turn a suspected or confirmed authentication/authorization bypass into impact — admin access, session takeover, privilege escalation, or cross-tenant read. Use when you find a missing auth check on a route, a weak JWT verifier, a session cookie that's predictable or reusable across users, a privilege field client-controllable, or an audit finding tagged CWE-287/CWE-863/CWE-639. Walks from probe to admin-equivalent capability and persists a finding with the highest-impact action you reached.4license: MIT5---67# Auth Bypass → Impact Escalation89You have a suspected auth weakness. Your job is to turn it into the10strongest impact you can demonstrate — admin access, cross-tenant data11read, token forgery, or session takeover — and persist a finding with the12specific action that proves it. Reporting "missing auth check" without13showing what it gets you is not a finding.1415## When this skill applies1617- A route responds 200 with no `Authorization` / session cookie when it18 should require auth (compare to a known-auth'd record's response).19- JWT verification accepts `alg: none`, a key-confused HMAC secret, or a20 token signed with the public key as HMAC secret.21- A session ID is predictable (sequential, time-based, or short).22- A request body / query has a role/tenant/user field that, when changed,23 changes the response (`role=user` → `role=admin`, `tenant=A` → `tenant=B`).24- Audit harness flagged CWE-287, CWE-862, CWE-863, CWE-639, CWE-284, or25 CWE-639 — even theoretical.2627## Workflow2829### 1. Establish baselines3031Use `list_auth_sessions` to see which auth contexts already exist for the32host. Use `auth_session_lookup` to pull the actual headers for at least33two roles if you have them (`anon` + `user` is enough; `user` + `admin`34is ideal). `remember` the role boundaries as `auth-roles`.3536If only one role is available, use `query_records` to find an37unauthenticated record for the same endpoint to anchor the no-auth38baseline.3940### 2. Classify the weakness4142Pick **one** mechanism to attack — don't shotgun:4344- **Missing auth on route**: pick the suspect endpoint, send the request45 with `replay_request` and `extra_headers: {}` to strip cookies. If you46 still get the privileged response, the route doesn't check auth at all.47- **Weak JWT**: decode the token (base64 each segment). Look at `alg`,48 `iss`, `aud`, `exp`, `sub`, custom claims (`role`, `tenant`). Re-mint:49 - `alg: none`: build header `{"alg":"none","typ":"JWT"}`, keep payload,50 drop signature (empty 3rd segment).51 - Key confusion: sign HMAC with the public key as the secret.52 - Empty / weak HMAC secret: try `""`, `secret`, `key`, the app name.53- **Session prediction**: collect 5-10 valid session IDs from `query_records`54 on login responses. If they're sequential, time-encoded, or short55 hex/base64, forge a neighboring ID via `replay_request`.56- **Privilege field client-controlled**: send the request with the role57 field flipped (`role=admin`, `is_admin=true`, `tenant_id=<other>`).5859### 3. Confirm escalation, don't stop at "different"6061A 200 with different content is *suspicious*, not proof. Confirm with62ONE of:6364- The response body contains data the original role couldn't see (admin65 email, another user's email, a hidden flag).66- A privileged action succeeds: `DELETE /api/users/X`, `POST /api/admin/X`,67 `GET /api/users/all` returns a list of more than just the current user.68- A subsequent request using the forged session is accepted as that user69 (look up the user's email or role in the response).7071Send the **smallest privileged request that proves the boundary was72crossed**. `remember` it with key `auth-bypass-proof` and include the73exact request line.7475### 4. Map the blast radius7677Briefly enumerate the surface the bypass unlocks. Don't enumerate at78scale — show:7980- 1 admin-only endpoint reachable.81- 1 cross-tenant record visible.82- 1 sensitive write that succeeds (if write paths exist; do **not**83 destructively delete or modify real data — read or create-with-rollback84 if possible).8586This sizes the severity in the finding without spamming requests.8788### 5. Persist the finding8990Call `report_finding`:9192- `severity`: `critical` if you reached admin or full cross-tenant; `high`93 for same-tenant privilege escalation or read of another user's data.94- `title`: name the mechanism and the proven action — `"JWT alg=none95 accepted; forged admin token reads /api/admin/users (1234 users)"`.96- `cwe_id`: CWE-287 (auth missing), CWE-863 (broken auth check),97 CWE-639 (IDOR-style), CWE-345 (insufficient verification of data),98 CWE-347 (weak signature) — pick the one that matches the mechanism.99- `description`: 3 sentences. Mechanism, proof, blast radius. Include the100 decoded JWT header / forged session ID / mutated field value in the101 description body so the reproduction is unambiguous.102103If the audit harness already raised this, `update_finding` to triaged104status referencing the new proof.105106## Pitfalls107108- Do not perform destructive writes (`DELETE`, `PUT` overwriting,109 password resets) on someone else's account. Read-only proofs are110 sufficient for severity.111- A forged token that returns 200 with an empty body proves nothing.112 The body must contain data the original role didn't have access to.113- Some apps return 200 with `error: "unauthorized"` in the JSON body —114 read the body, not just the status, before claiming bypass.115- If the app has a "demo admin" account that's publicly accessible by116 design, that's not a bypass — confirm by checking documentation or117 a comment in the code (`--source` mode).118- IDOR (object-level missing auth) is a related but distinct pattern —119 if your finding is "can read another user's order by changing the120 ID", use `idor-blast-radius` instead.121122## Output expectations123124- One `report_finding` with the strongest proven impact.125- A `remember` note with the canonical bypass request.126- Plan item marked `done`.