🧠 Início da jornada

ontem foi dia de teoria. IDOR foi fácil, quase leve demais.

  • simples de entender
  • fácil de identificar
  • extremamente comum

Parecia até suspeito.

Hoje vieram os write-ups. Três deles. Tudo fazia sentido. A confiança subiu.

E como toda boa história… a prática tratou de equilibrar as coisas.

🎬 ATO 1 — Expectativa vs realidade

Nome do lab: Insecure Direct Object References

Classificação: Apprentice

Pensamento automático: "5 minutos e acabou"

A realidade observando de canto…

Objetivo

pegar a senha do Carlos e fazer login. Simples. Direto. Sem mistério.

Exploração inicial

Interface:

  • e-commerce
  • produtos
  • conta
  • nada demais

Até que… Live chat

Intuição ativada: "É aqui." (essa informação também estava na descrição…)

🎬 ATO 2 — Cadê meus requests?

Primeiro passo clássico: abrir o Caido.

Enviei mensagens no chat…

E então: apenas UMA requisição apareceu

GET /chat HTTP/1.1
Resposta: HTTP/1.1 101 Switching Protocol

E depois? Nada.

Nenhuma requisição para novas mensagens.

🧠 Pensamento interno

"Como assim não tem request?"

"Isso não faz sentido…"

Sistema quase entrando em pane.

🎬 ATO 3 — Descobrindo um novo mundo

Hora de pedir ajuda (Gemini)

E então veio a resposta: WebSockets

Diferença essencial:

HTTP:

  • Requisição → Resposta
  • Cliente inicia
  • Stateless

WebSocket:

  • Conexão aberta
  • Comunicação contínua
  • Stateful

O momento mágico

HTTP/1.1 101 Switching Protocol

Esse é o handshake

A partir daqui: conexão aberta. Troca contínua de mensagens

Ferramenta nova desbloqueada

Dentro do Caido: WS History

E lá estava tudo. As mensagens do chat… em tempo real.

MAGIA.

🎬 ATO 4 — O botão suspeito

Ainda sem IDOR, mas menos perdida.

Hora de explorar mais fundo.

botão: View transcript. Cliquei…

Nova requisição:

POST /download-transcript HTTP/1.1

Conteúdo enviado

  • histórico completo do chat
  • mensagens estruturadas

Insight

"Se a senha do Carlos existe… deve estar no histórico dele."

Pergunta do milhão

Como sair do MEU histórico e acessar o do Carlos?

🎬 ATO 5 — A pista escondida

Enviei a requisição manualmente.

Resposta:

HTTP/1.1 302 Found
Location: /download-transcript/3.txt

3.txt… Isso não era aleatório.

Próximo passo

GET /download-transcript/3.txt

✔Download realizado ✔Meu histórico

Confirmado!

isso é uma referência direta a um objeto

🎬 ATO 6 — O ataque

Hora do clássico: enviar para o Repeater

Testes:

  • 2.txt … NADA
  • 1.txt … BINGO

Conteúdo encontrado

Hi Hal, I think I've forgotten my password…

Poxa, Carlos… Jogando a senha direto no chat… vacilo.

Vulnerabilidade confirmada

IDOR clássico:

  • acesso direto a arquivos
  • sem validação
  • sem autenticação adequada

🎬 ATO 7 — Olhando por trás da cortina

Hora de entender o "porquê"

Arquivos encontrados no código-fonte

  • /resources/js/chat.js
  • /resources/js/viewTranscript.js

Foco: viewTranscript.js

O que o código faz

  • Coleta mensagens do chat
  • Monta um array:
var transcript = [];
  • Envia via POST:
var xhr = new XMLHttpRequest();
  • Após resposta:
window.location = xhr.responseURL;

O problema

O servidor responde com:

/download-transcript/3.txt

E o navegador simplesmente: acessa e baixa

Sem questionar.

🎬 ATO FINAL — O verdadeiro vilão

No servidor, provavelmente:

/var/www/app/transcripts/

Arquivos:

  • 1.txt → Carlos
  • 2.txt → Maria
  • 3.txt → você

Falha crítica

👉 nenhum controle de acesso

O servidor:

  • não verifica quem fez a requisição
  • não valida o dono do arquivo

Conclusão

O lab parecia fácil. E tecnicamente… é.

Mas trouxe aprendizados importantes:

  • nem tudo passa pelo HTTP tradicional
  • WebSockets mudam o jogo
  • IDs simples podem esconder grandes falhas

E Carlos…

não coloque senha no chat.