Skip to main content

Native IDOR and broken object-level authorization detection engine

Project description

PhaseAccess

The most thorough open-source IDOR / BOLA scanner available.
PhaseAccess goes beyond simple ID enumeration — it understands ownership, sessions, and evidence.

pip install phaseaccess

# Install in virtual env
python3 -m venv .venv
source .venv/bin/activate
pip install phaseaccess

Point it at a target. Get findings. Drop it in a pipeline.


Why use PhaseAccess?

  • Semantic diffing — compares ownership fields (user_id, email, owner_id) across responses, not just status codes
  • Dual-session mode — use two sets of credentials; get CONFIRMED findings when user B's data appears in user A's request
  • Variance-aware baseline — two baseline fetches detect per-request noise (nonces, timestamps, session tokens) so they don't pollute the diff
  • JWT tampering — extracts and fuzzes JWT claims from Authorization headers
  • Cookie extraction — scans individual cookie values for ID-like patterns
  • Timing oracle — flags significant latency deltas as a supporting signal
  • HAR / Burp Suite import — replay your proxy traffic directly
  • curl reproduction — every finding ships a ready-to-paste curl command

Quick start

# Single-session: enumerate IDs around /users/42
phaseaccess -u "https://api.example.com/users/42" \
  -H "Authorization: Bearer <your_token>"

# Dual-session: owner vs attacker — gets you CONFIRMED findings
phaseaccess -u "https://api.example.com/users/42" \
  -H "Authorization: Bearer <owner_token>" --label-a owner \
  --header-b "Authorization: Bearer <attacker_token>" --label-b attacker

# Crawl the whole app and test every discovered endpoint
phaseaccess -u "https://api.example.com/" \
  -H "Authorization: Bearer <token>" --crawl --crawl-pages 100 --crawl-depth 4

# JS-rendered SPA — use headless Chromium for endpoint discovery
phaseaccess -u "https://api.example.com/" \
  -H "Authorization: Bearer <token>" --browser-crawl

# Form login — authenticate both sessions before scanning
phaseaccess -u "https://app.example.com/profile" \
  --login-url "https://app.example.com/login" \
  --login-user alice --login-pass alice_pw \
  --login-url-b "https://app.example.com/login" \
  --login-user-b bob --login-pass-b bob_pw

# Import all endpoints from an OpenAPI / Swagger spec
phaseaccess -u "https://api.example.com/" \
  -H "Authorization: Bearer <token>" \
  --openapi https://api.example.com/openapi.json

# Stored IDOR chainer: alice creates a resource, check if bob can read it
phaseaccess -u "https://api.example.com/documents/1" \
  -H "Authorization: Bearer <alice_token>" --label-a alice \
  --header-b "Authorization: Bearer <bob_token>" --label-b bob \
  --chain-create "POST:https://api.example.com/documents" \
  --chain-body '{"title":"test"}' \
  --chain-read "https://api.example.com/documents/{id}"

# POST endpoint
phaseaccess -u "https://api.example.com/orders" \
  -X POST -d '{"order_id": 1001}' \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json"

# Import your Burp Suite export and scan everything at once
phaseaccess -u "https://api.example.com/users/1" \
  -H "Authorization: Bearer <token>" \
  --targets burp_export.xml

# Save a JSON report, suppress noise, gate on medium+
phaseaccess -u "https://api.example.com/users/42" \
  -H "Authorization: Bearer <token>" \
  --min-confidence medium --json -o report.json

What it detects

Type Description
Horizontal IDOR User A reads/modifies user B's objects
Vertical IDOR Low-priv user accesses admin/elevated objects
Method bypass Endpoint blocks GET but not PUT/DELETE/PATCH
Param pollution ?id=own&id=victim — server picks the wrong one
Mass assignment Injected user_id/owner_id fields accepted by server
Soft-delete bypass ?include_deleted=true reveals logically deleted resources
Blind IDOR Status code flips 403→200 with no body — side-effect may have occurred
JWT claim tampering sub, user_id, role mutations with alg:none test
JWT kid injection kid header path traversal (../../dev/null, ../../../../etc/passwd)
JWT algorithm confusion RS256 → HS256 downgrade with empty signature
JWT exp removal Strip expiry claim to test for missing validation
Hex ID enumeration Detects and mutates short hex identifiers (e.g. fa1, 1a2b3c)
Direct cross-session check Session B fetches session A's URL; confirms horizontal IDOR when ownership values leak
Stored IDOR chaining Session A creates resource, session B reads via --chain-read template

All options

Usage: phaseaccess [options]

Target:
  -u, --url            Target URL (required, or use interactive mode)
  -X, --method         HTTP method (default: GET)
  -d, --data           Request body (form-encoded or JSON)
  --extra-url URL      Additional endpoint to test (repeatable)
  --targets FILE       Import targets from HAR / Burp Suite XML / JSON file
  --crawl              BFS-crawl the target and test all discovered endpoints
  --crawl-pages N      Max pages to crawl (default: 100)
  --crawl-depth N      Max crawl depth (default: 3)
  --browser-crawl      Headless Chromium endpoint discovery for JS-rendered apps
  --openapi FILE/URL   OpenAPI/Swagger spec — imports all endpoints to scan
  --base-url URL       Base URL override when loading an OpenAPI spec

Session A — resource owner:
  -H KEY:VALUE         Request header (repeatable)
  -c, --cookie         Cookie string
  --label-a LABEL      Session label (default: session_a)
  --login-url URL      Login form URL for session A — authenticates before scanning
  --login-user USER    Username for session A form login
  --login-pass PASS    Password for session A form login

Session B — attacker (enables dual-session mode):
  --header-b KEY:VALUE Header (repeatable)
  --cookie-b           Cookie string
  --label-b LABEL      Session label (default: session_b)
  --login-url-b URL    Login form URL for session B
  --login-user-b USER  Username for session B form login
  --login-pass-b PASS  Password for session B form login

Network:
  --proxy URL          HTTP proxy (e.g. http://127.0.0.1:8080)
  --insecure           Disable SSL certificate verification
  --delay SECS         Delay between requests (rate limiting)
  --timeout SECS       Request timeout (default: 15)
  --user-agent STRING  Override User-Agent (default: PhaseAccess/1.0)

Scan control:
  -t, --threads N      Parallel threads (default: 5)
  --max-candidates N   Tamper candidates per parameter (default: 10)
  --no-method-bypass   Disable HTTP method bypass check
  --no-param-pollution Disable HTTP parameter pollution check
  --no-mass-assignment Disable mass assignment check
  --no-soft-delete     Disable soft-delete bypass check
  --no-blind-idor      Disable blind IDOR check
  --chain-create METHOD:URL  Stored IDOR: URL where session A creates a resource
  --chain-body BODY    Request body for the chain-create POST
  --chain-read URL     URL template where session B reads (use {id} placeholder)

Output:
  --min-confidence     Minimum confidence to report: confirmed high medium low info
  --json               Raw JSON output
  -o, --output FILE    Save report to file
  -q, --quiet          Suppress live log output
  -v, --verbose        Debug logging

Confidence levels

Level Meaning
CONFIRMED Dual-session: foreign user's ownership value appeared in response
HIGH Ownership field changed value, or 200 returned where 403 was baseline
MEDIUM JSON structure changed or substantial body diff
LOW Status code change or timing oracle signal only
INFO ID enumeration possible; no access confirmed

Dual-session mode (recommended)

Dual-session mode is the most powerful way to use PhaseAccess.
You supply two credential sets:

  • Session A — the resource owner (the user who should have access)
  • Session B — the attacker (a different user who should not have access)

PhaseAccess uses session A's response to harvest ownership values (user_id, email, etc.), then replays tampered requests as session B. If session B's response contains session A's ownership values — or vice versa — the finding is CONFIRMED.

phaseaccess \
  -u "https://api.example.com/documents/d9a3f7c2" \
  -H "Authorization: Bearer alice_token" --label-a alice \
  --header-b "Authorization: Bearer bob_token" --label-b bob

HAR / Burp Suite import

Feed your proxy traffic directly into PhaseAccess:

# Burp Suite: Proxy → HTTP history → select requests → Save items → XML
phaseaccess -u "https://api.example.com/users/1" \
  -H "Authorization: Bearer <token>" \
  --targets burp_history.xml

# HAR export (Chrome DevTools → Network → Export HAR)
phaseaccess -u "https://api.example.com/users/1" \
  -H "Authorization: Bearer <token>" \
  --targets devtools.har

All imported URLs are tested alongside the primary target.


Python API

from phaseaccess.engine.scanner import scan, ScanOptions

result = scan(
    "https://api.example.com/users/42",
    ScanOptions(
        session_a_headers={"Authorization": "Bearer alice_token"},
        session_a_label="alice",
        session_b_headers={"Authorization": "Bearer bob_token"},
        session_b_label="bob",
        max_candidates=15,
        threads=3,
    ),
)

for finding in result.findings:
    print(f"[{finding.confidence}] {finding.idor_type}{finding.parameter}")
    print(f"  {finding.curl_command}")

Sample output

  1. [CONFIRMED]  horizontal_idor
      URL       : https://api.example.com/users/42
      Param     : path[1] (path_segment)
      ID type   : integer
      Original  : '42'
      Tampered  : '43'
      Status    : 200 → 200
      Leaked    : user_id, email
      Evidence  : user_id: baseline='42', tampered='43'
      Reproduce : curl -s -X GET -H 'Authorization: Bearer ...' https://api.example.com/users/43

How it works

Target URL(s)
    │
    ├─ 0. Auth & discovery (optional)
    │      Form login (session A + B), OpenAPI import,
    │      BFS crawl or headless browser crawl
    │
    ├─ 1. Baseline (×2 fetches → variance-aware stable fingerprint)
    │      Direct cross-session check: does session B see session A's
    │      ownership values on session A's URL? → CONFIRMED immediately
    │
    ├─ 2. Extract object refs
    │      URL params, path segments, JSON body, form body,
    │      headers (X-User-Id etc.), cookies, Authorization JWT
    │
    ├─ 3. Generate tamper candidates per ref
    │      integer neighbours, UUID v1 timestamp deltas,
    │      nil/max UUID, Base64 mutations, hex neighbours,
    │      JWT claim tampering (sub/user_id/role + alg:none),
    │      JWT kid path traversal, RS256→HS256 confusion, exp removal,
    │      foreign IDs from session B's harvest
    │
    ├─ 4. Fire tampered requests (session B if dual, else session A)
    │      Compare fingerprints:
    │        ownership field values, JSON structure, body hash,
    │        status code delta, timing oracle
    │      Dual-session + leaked ownership fields → upgrade to CONFIRMED
    │
    ├─ 5. Additional checks
    │      Method bypass, param pollution, mass assignment
    │        (mass assignment follow-up GET confirms ownership change),
    │      soft-delete bypass, blind IDOR
    │
    ├─ 6. Stored IDOR chaining (--chain-create / --chain-read)
    │      Session A creates resource → harvest IDs from response
    │      Session B reads via URL template → CONFIRMED if ownership leaks
    │
    └─ 7. Deduplicate + report
           Highest-confidence per (url, param, type)
           curl reproduction command on every finding

Legal & Ethical Use

Only run PhaseAccess against applications you own or have explicit written authorization to test. Authorized use includes penetration testing engagements, bug bounty programs within defined scope, and CTF competitions.

In dual-session mode, only supply credentials for accounts you control or that belong to users who have consented as part of the authorized test.

The authors accept no liability for unauthorized or illegal use.


License

Licensed under the AGPLv3. You are free to use, modify, and distribute this software. If you run it as a service or distribute it, the source must remain open.

For commercial licensing, contact the author.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

phaseaccess-0.1.1.tar.gz (87.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

phaseaccess-0.1.1-py3-none-any.whl (75.1 kB view details)

Uploaded Python 3

File details

Details for the file phaseaccess-0.1.1.tar.gz.

File metadata

  • Download URL: phaseaccess-0.1.1.tar.gz
  • Upload date:
  • Size: 87.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for phaseaccess-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b49cc33ee9ff94c165a8a43ede74e078727211803390bbc231b6ca4d81c44712
MD5 752fb2cd9f83b889c6c57c959371853b
BLAKE2b-256 bdebc254453afc8df4de169701753804477654ae9cb9310e77d6babc6180605f

See more details on using hashes here.

File details

Details for the file phaseaccess-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: phaseaccess-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 75.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for phaseaccess-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6c68acb725328a959e2d6b1f8f5163a0d7840c9db1f092540c757291742a0139
MD5 074ee339fde6bf2c5712a7b58dccbc6a
BLAKE2b-256 58483b43b164b3ae2f0ede9ed0797a7009f213a769a30de8f4b840ae9ad13319

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page