$npx -y skills add Prohao42/aimy-skill --skill idor-broken-object-authorization--- name: idor-broken-object-authorization description: >- IDOR and broken object authorization testing playbook. Use when requests expose object identifiers, tenant boundaries, writable fields, or missing object-level authorization checks. ---
| 1 | # SKILL: IDOR / Broken Object Level Authorization — Expert Attack Playbook |
| 2 | |
| 3 | > **AI LOAD INSTRUCTION**: IDOR is the #1 bug bounty finding. This skill covers non-obvious IDOR surfaces, all attack vectors (not just URL params), A-B testing methodology, BOLA vs BFLA distinction, chaining IDOR to higher impact, and what testers repeatedly miss. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## 1. IDOR vs BOLA vs BFLA |
| 8 | |
| 9 | | Term | Meaning | Impact | |
| 10 | |---|---|---| |
| 11 | | IDOR | Insecure Direct Object Reference | Read/modify other users' data | |
| 12 | | BOLA | Broken Object Level Authorization (OWASP API Top 10 A1) | Same as IDOR, API terminology | |
| 13 | | BFLA | Broken Function Level Authorization | Low-priv user accesses HIGH-PRIV functions (e.g., admin endpoints) | |
| 14 | |
| 15 | **Key distinction**: |
| 16 | - BOLA = accessing **object** you shouldn't own (data belonging to other users) |
| 17 | - BFLA = accessing **function** you shouldn't be authorized for (admin CRUD operations, bulk actions, user management) |
| 18 | |
| 19 | --- |
| 20 | |
| 21 | ## 2. WHERE TO FIND OBJECT IDs (ALL LOCATIONS) |
| 22 | |
| 23 | Don't stop at URL path parameters — IDs appear in: |
| 24 | |
| 25 | ``` |
| 26 | URL path: GET /api/v1/users/1234/profile |
| 27 | URL query: GET /orders?order_id=982 |
| 28 | Request body: {"userId": 1234, "action": "view"} |
| 29 | JSON fields: {"resource": {"id": 5678, "type": "invoice"}} |
| 30 | Headers: X-User-ID: 1234 |
| 31 | X-Account-ID: 9999 |
| 32 | Cookies: user_id=1234; account=org_5678 |
| 33 | GraphQL args: query { user(id: "1234") { ... } } |
| 34 | Form fields: <input name="documentId" value="5678"> |
| 35 | WebSocket msgs: {"event":"subscribe","channel_id":9999} |
| 36 | ``` |
| 37 | |
| 38 | --- |
| 39 | |
| 40 | ## 3. A-B TESTING METHODOLOGY |
| 41 | |
| 42 | The most systematic IDOR test approach: |
| 43 | |
| 44 | ``` |
| 45 | Step 1: Create two test accounts: UserA and UserB |
| 46 | Step 2: Perform all actions as UserA, capture all requests |
| 47 | (profile edit, order view, password change, file access, etc.) |
| 48 | Step 3: Note every object ID created or accessed by UserA |
| 49 | Step 4: Authenticate as UserB |
| 50 | Step 5: Replay UserA's requests using UserB's session token |
| 51 | Step 6: If UserB can read/modify UserA's data → BOLA confirmed |
| 52 | |
| 53 | Victim matters: for real bugs, target existing users, not test accounts. |
| 54 | Report evidence: show UserA owns the resource, UserB accessed it. |
| 55 | ``` |
| 56 | |
| 57 | --- |
| 58 | |
| 59 | ## 4. ID TYPE ITS IMPLICATIONS |
| 60 | |
| 61 | | ID Pattern | Example | Notes | |
| 62 | |---|---|---| |
| 63 | | Sequential int | `id=1001` → `id=1002` | Easy prediction, high hit rate | |
| 64 | | UUID v4 | `550e8400-...` | Need to find UUID from other endpoints | |
| 65 | | UUID v1 | Clock-based UUID | Time-predictable! Extract timestamp/MAC | |
| 66 | | GUIDs from own data | See in responses | Collect all UUIDs from your own account data first | |
| 67 | | Hashed IDs | `md5(user_id)` | Try hashing sequential ints | |
| 68 | | Encoded IDs | base64(`{"id":1001}`) | Decode → modify → re-encode | |
| 69 | | Compound IDs | `/api/users/1/orders/5` | Both IDs may be independently verifiable | |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## 5. HORIZONTAL vs VERTICAL PRIVILEGE ESCALATION |
| 74 | |
| 75 | **Horizontal**: UserA accesses UserB's data (same privilege level) |
| 76 | ``` |
| 77 | GET /api/account/1234/statement ← you are user 5678 |
| 78 | ``` |
| 79 | |
| 80 | **Vertical**: Low-priv user accesses admin-only functions |
| 81 | ``` |
| 82 | POST /api/admin/users/delete ← normal user calling admin endpoint |
| 83 | GET /api/admin/all-users |
| 84 | PUT /api/users/1234/role {"role":"admin"} |
| 85 | ``` |
| 86 | |
| 87 | **Combined**: Low-priv IDOR that grants privilege escalation |
| 88 | ``` |
| 89 | GET /api/v1/users/1/details → read admin user's auth token |
| 90 | ``` |
| 91 | |
| 92 | --- |
| 93 | |
| 94 | ## 6. HTTP METHOD ESCALATION |
| 95 | |
| 96 | When `GET /resource/1234` is properly restricted, test ALL other verbs: |
| 97 | |
| 98 | ```http |
| 99 | GET /api/v1/users/UserA_ID ← might be blocked |
| 100 | POST /api/v1/users/UserA_ID ← different code path, might not check authz |
| 101 | PUT /api/v1/users/UserA_ID ← update another user's data |
| 102 | DELETE /api/v1/users/UserA_ID ← delete another user's account |
| 103 | PATCH /api/v1/users/UserA_ID ← partial update (often missed in authz checks) |
| 104 | ``` |
| 105 | |
| 106 | **Why this works**: Authorization logic is often implemented per-method, and developers forget edge cases. |
| 107 | |
| 108 | --- |
| 109 | |
| 110 | ## 7. PARAMETER POLLUTION & TYPE CONFUSION |
| 111 | |
| 112 | When `id=1234` is validated, try: |
| 113 | ``` |
| 114 | id[]=1234&id[]=5678 ← array — app may use first or last |
| 115 | id=5678&id=1234 ← duplicate — app may prefer first or last |
| 116 | {"id": "1234"} ← string vs int: might hit different code path |
| 117 | {"id": [1234]} ← array in JSON |
| 118 | {"userId": 1234, "id": 5678} ← two ID fields — which is used for authz? |
| 119 | ``` |
| 120 | |
| 121 | **JSON Type Confusion**: |
| 122 | ```json |
| 123 | {"userId": "1234"} vs {"userId": 1234} |
| 124 | ``` |
| 125 | Some ORMs handle string vs integer differently in queries. |
| 126 | |
| 127 | --- |
| 128 | |
| 129 | ## 8. BFLA (FUNCTION LEVEL) ATTACKS |
| 130 | |
| 131 | ### Common BFLA Endpoints to Test |
| 132 | |
| 133 | ```http |
| 134 | # User management (admin-only in design): |
| 135 | GET /api/v1/admin/users |
| 136 | DELETE /api/v1/users/{any_user_id} |
| 137 | PUT |