Skip to content

Module: IDOR

Moiz Bootwala edited this page Jan 19, 2026 · 1 revision

Overview

Module Name: idor
Requires Sink: SQLite

Insecure Direct Object Reference (IDOR) is an access control vulnerability where applications expose direct references to internal objects (like database IDs) without proper authorization checks. This allows attackers to access resources belonging to other users by manipulating reference values.

FlawFactory's IDOR module creates real IDOR vulnerabilities by querying the SQLite database based on user-provided IDs without proper authorization checks.

Supported Placements

Placement Description Example Request
query_param URL query string GET /profile?user_id=1
path_param URL path segment GET /users/1/profile
form_field POST form data POST /view with id=1
json_field JSON body field POST /api/user with {"id": 1}
header HTTP header X-User-Id: 1
cookie Cookie value Cookie: user_id=1

Configuration Options

variant (string)

The type of ID/reference being used.

Value Description Example
numeric Sequential integer IDs 1, 2, 3
uuid UUID/GUID format a1b2c3d4-...
encoded Base64 or encoded values MTIz (base64 of "123")
predictable Pattern-based IDs user_001, user_002

Default: numeric

query_template (string) - Required

SQL query template with {input} placeholder.

query_template: "SELECT * FROM users WHERE id = {input}"

show_errors (boolean)

Whether to return detailed error messages.

Default: true

access_control (string)

Simulated access control mechanism.

Value Description How It "Protects"
none No access control Fully vulnerable
weak_header Requires X-User-ID header Checks header exists, not ownership
weak_cookie Requires user_id cookie Checks cookie exists, not ownership
role_based Checks X-User-Role header Allows "admin" role bypass
predictable_token Requires Bearer user_<id> Token format is predictable

Default: none

Data Requirements

IDOR requires the data section to define tables with sensitive information:

data:
  tables:
    TABLE_NAME:
      columns: [COL1, COL2, COL3, COL4, COL5]
      rows:
        - [COL1_ENTRY, COL2_ENTRY, COL3_ENTRY, COL4_ENTRY, COL5_ENTRY]
        - [COL1_ENTRY, COL2_ENTRY, COL3_ENTRY, COL4_ENTRY, COL5_ENTRY]
        - [COL1_ENTRY, COL2_ENTRY, COL3_ENTRY, COL4_ENTRY, COL5_ENTRY]
data:
  tables:
    users:
      columns: [id, username, email, ssn, salary, role]
      rows:
        - [1, "admin", "admin@company.com", "123-45-6789", "150000", "admin"]
        - [2, "john", "john@company.com", "234-56-7890", "75000", "user"]
        - [3, "jane", "jane@company.com", "345-67-8901", "80000", "user"]

Configuration Examples

Basic Numeric IDOR

app:
  name: "IDOR Lab 1"
  port: 8081

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /api/profile
    method: GET
    vulnerabilities:
      - type: idor
        placement: query_param
        param: user_id
        config:
          variant: numeric
          query_template: "SELECT * FROM users WHERE id = {input}"
          access_control: none

Request: GET /api/profile?user_id=2

{
  "results": [
    {
      "id": "2",
      "username": "john",
      "email": "john@corp.com",
      "ssn": "234-56-7890",
      "salary": "75000"
    }
  ],
  "count": 1
}

Path Parameter IDOR

app:
  name: "IDOR Lab 2"
  port: 8082

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /users/{id}/profile
    method: GET
    vulnerabilities:
      - type: idor
        placement: path_param
        param: id
        config:
          variant: numeric
          query_template: "SELECT * FROM users WHERE id = {input}"

Request: GET /users/2/profile

{
  "results": [
    {
      "id": "2",
      "username": "john",
      "email": "john@corp.com",
      "ssn": "234-56-7890",
      "salary": "75000"
    }
  ],
  "count": 1
}

With Weak Header Check

app:
  name: "IDOR Lab 3"
  port: 8083

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /api/user/data
    method: GET
    vulnerabilities:
      - type: idor
        placement: query_param
        param: id
        config:
          variant: numeric
          query_template: "SELECT * FROM users WHERE id = {input}"
          access_control: weak_header

Requires X-User-ID header but doesn't verify it matches the requested ID.

Request: GET /api/user/data?id=1 (no X-User-ID header)

{
  "error": "unauthorized: missing X-User-ID header",
  "blocked": true
}

To bypass this, we use the required header with any user.

# Blocked (no header)
curl http://localhost:8080/api/user?id=2

# Bypassed (any header value works)
curl -H "X-User-ID: 1" http://localhost:8080/api/user?id=2
curl -H "X-User-ID: anything" http://localhost:8080/api/user?id=2

With Role-Based "Protection"

app:
  name: "IDOR Lab 4"
  port: 8084

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /api/admin/user
    method: GET
    vulnerabilities:
      - type: idor
        placement: query_param
        param: id
        config:
          variant: numeric
          query_template: "SELECT * FROM users WHERE id = {input}"
          access_control: role_based

Allows access if X-User-Role: admin header is present. Admin role grants access to anything:

# Normal user - can still access other users data
curl -H "X-User-Role: user" http://localhost:8080/api/user?id=2

# Admin bypass
curl -H "X-User-Role: admin" http://localhost:8080/api/user?id=2

UUID Variant

app:
  name: "IDOR Lab 5"
  port: 8085

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /api/document
    method: GET
    vulnerabilities:
      - type: idor
        placement: query_param
        param: doc_id
        config:
          variant: uuid
          query_template: "SELECT * FROM documents WHERE uuid = '{input}'"

Encoded Variant

app:
  name: "IDOR Lab 6"
  port: 8086

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /api/resource
    method: GET
    vulnerabilities:
      - type: idor
        placement: query_param
        param: ref
        config:
          variant: encoded
          query_template: "SELECT * FROM resources WHERE id = {input}"

JSON Body IDOR

app:
  name: "IDOR Lab 7"
  port: 8087

data:
  tables:
    users:
      columns: [id, username, email, ssn, salary]
      rows:
        - [1, "admin", "admin@corp.com", "123-45-6789", "150000"]
        - [2, "john", "john@corp.com", "234-56-7890", "75000"]
        - [3, "jane", "jane@corp.com", "345-67-8901", "80000"]

endpoints:
  - path: /api/user/view
    method: POST
    vulnerabilities:
      - type: idor
        placement: json_field
        param: user_id
        config:
          variant: numeric
          query_template: "SELECT * FROM users WHERE id = {input}"

Clone this wiki locally