SSRF Descomplicado: Explorando Cloud Metadata em Lab AWS Local
Reproducao etica de SSRF contra IMDS usando LocalStack, com payloads reais, captura de credenciais simuladas e bloqueio definitivo via IMDSv2.
O endpoint 169.254.169.254 ja torrou mais carreira de SRE do que qualquer outro IP da historia da nuvem. Capital One em 2019 perdeu 100 milhoes de registros porque um WAF mal configurado deixou um SSRF chegar ate o Instance Metadata Service e baixar credenciais temporarias da role da EC2. Sete anos depois, ainda recebemos relatorios de bug bounty com o mesmo padrao: aplicacao baixa imagem por URL fornecida pelo usuario, ninguem valida o destino, IMDSv1 esta ativo, jogo encerrado. Vamos reproduzir esse ataque do zero num lab AWS local com LocalStack, entender por que IMDSv2 muda o jogo, e fechar com um checklist de hardening que voce aplica hoje.
Primeiro, o ambiente. Suba LocalStack Pro 4.x via docker-compose com os servicos ec2, iam, sts e s3 habilitados, mais um container Flask exposto na porta 5000 rodando uma API vulneravel de fetch-image. O codigo tem dezoito linhas: recebe ?url=, chama requests.get sem validacao, retorna o body. Para simular o IMDS dentro do LocalStack precisamos do plugin localstack-ec2-metadata-mock ou subir um mock dedicado em 169.254.169.254 via netns. Quem prefere fidelidade maxima usa um lab em EC2 t3.micro real custando dois centavos a hora, mas o LocalStack fecha 95% do aprendizado sem cartao de credito. Pentest Web do Zero: Montando um Lab Seguro com DVWA, Juice Shop e Burp Suite cobre o setup base de containers vulneraveis que voce reusa aqui.
Com o lab no ar, o payload classico de IMDSv1 e literalmente um GET. Pelo Burp, intercepte a requisicao do app e troque o parametro url para http://169.254.169.254/latest/meta-data/iam/security-credentials/. A resposta retorna o nome da role anexada, digamos app-server-role. Em seguida, GET http://169.254.169.254/latest/meta-data/iam/security-credentials/app-server-role devolve um JSON com AccessKeyId, SecretAccessKey e Token. Exporte como variaveis de ambiente, rode aws sts get-caller-identity --endpoint-url http://localhost:4566 e voce esta autenticado como a aplicacao. Esse e o momento em que blue teams costumam pular da cadeira na demonstracao.
A escalada depende do que a role pode fazer. No nosso lab anexei uma policy deliberadamente permissiva com s3:* e iam:ListRoles. Com as creds roubadas: aws s3 ls revela um bucket backups-prod-2026, aws s3 cp s3://backups-prod-2026/db.dump baixa o dump, e aws iam list-attached-role-policies mapeia o caminho para privilege escalation via PassRole. Ferramentas como Pacu, ScoutSuite e cloudfox automatizam essa enumeracao pos-compromisso. Padroes parecidos aparecem em Pentest de APIs REST e GraphQL: Checklist Tecnico para Bug Bounty Legal quando endpoints de webhook ou image-proxy aceitam URLs internas sem allowlist. Documente cada comando com timestamp porque relatorio de bug bounty sem PoC reproduzivel paga zero.
IMDSv2 quebra esse ataque exigindo uma sessao com token. O fluxo correto e PUT http://169.254.169.254/latest/api/token com header X-aws-ec2-metadata-token-ttl-seconds: 21600, e depois GET com header X-aws-ec2-metadata-token. SSRF que so faz GET, sem controle de headers nem metodo, simplesmente trava. Para forcar IMDSv2 use aws ec2 modify-instance-metadata-options --http-tokens required --http-put-response-hop-limit 1. O hop-limit 1 e crucial: bloqueia containers em rede bridge de alcancar o IMDS via NAT do host. Combine com bloqueio de saida 169.254.0.0/16 no security group e a aplicacao perde qualquer caminho para o endpoint magico.
Defesa em camadas nao termina no IMDS. Valide URLs no app rejeitando 169.254.0.0/16, 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8 e resolva o hostname antes de fazer a requisicao para evitar DNS rebinding. Use uma biblioteca como ssrf-protect ou implemente com getaddrinfo + verificacao por familia de IP. Reduza permissoes da role para o minimo necessario, prefira IRSA no EKS, ative GuardDuty que detecta uso anomalo de credentials, e rode regularmente scanners como Prowler. Tecnicas correlatas de exploracao web aparecem em SQL Injection na Pratica: Explorando, Detectando e Mitigando em Lab Controlado e XSS Moderno: DOM, Stored e Reflected com Exemplos Reais em Ambiente de Teste, que junto com SSRF formam o trio mais comum em bug bounty corporativo. Para quem quer ir mais fundo em pivoting interno apos pegar credenciais, Pivoting com Chisel e Ligolo-ng: Redes Segmentadas em Lab de Pentest mostra o passo seguinte.
Takeaway pratico: rode aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens]' no seu inventario AGORA. Toda instancia que retornar optional esta vulneravel a SSRF classico. Force required em massa via SSM Automation Document, audite roles com policies *:* e configure um teste E2E no CI que tenta acessar 169.254.169.254 a partir do container da aplicacao e falha o build se obtiver resposta 200. Levou dezessete minutos para reproduzir o Capital One no laboratorio. Leva o mesmo tempo para fechar o buraco em producao.