Pular para o conteúdo
Categoria: Pentest9 min de leitura

SSRF Descomplicado: Explorando Cloud Metadata em Lab AWS Local

Por Lucas Andrade ·

Reproducao etica de SSRF contra IMDS usando LocalStack, com payloads reais, captura de credenciais simuladas e bloqueio definitivo via IMDSv2.

SSRF Descomplicado: Explorando Cloud Metadata em Lab AWS Local

O endpoint 169.254.169.254 queimou mais carreiras de SRE do que qualquer outro IP na historia da nuvem. A Capital One em 2019 perdeu cerca de 100 milhoes de registros porque uma WAF mal configurada deixou um Server-Side Request Forgery alcancar o Instance Metadata Service e puxar credenciais temporarias do role de EC2. Anos depois ainda recebemos relatorios de bug bounty com o padrao identico: uma app busca uma imagem de uma URL fornecida pelo usuario, ninguem valida o destino, IMDSv1 fica ativo, game over. Vamos reconstruir esse ataque do zero num lab AWS local com LocalStack, entender exatamente por que o IMDSv2 muda a conta, e fechar com um checklist de hardening que voce pode subir hoje. Tudo roda na sua propria maquina, entao nao ha alvo alem do seu proprio lab.

O que e SSRF e o que o metadata service faz

Server-Side Request Forgery e uma classe de bug em que um atacante controla o destino de uma requisicao que o servidor faz em nome dele. A aplicacao e o deputado confuso: tem posicao de rede e identidade que o atacante nao tem, e se conecta de bom grado para onde ele apontar. Na AWS, o destino interno de maior valor e o Instance Metadata Service (IMDS) em 169.254.169.254, um endereco link-local que toda instancia EC2 alcanca. Ele expoe dados de instancia e, critico, credenciais temporarias de role IAM sob /latest/meta-data/iam/security-credentials/. Um SSRF que o alcanca transforma um inocente fetch de imagem em roubo de credenciais, e por isso e o alvo de maior consequencia no pentest de nuvem.

Contexto de ameaca: por que isso continua acontecendo

O padrao se repete porque o codigo vulneravel parece razoavel. Um produto precisa buscar um avatar, renderizar um preview de link ou fazer proxy de um webhook, entao aceita uma URL e a chama do lado servidor. O desenvolvedor pensa em imagens, nao num endpoint de metadata link-local que distribui credenciais de nuvem. Enquanto isso, o IMDSv1 ficou como default em instancias antigas por anos, entao o cofre de credenciais estava a uma requisicao sem validacao de distancia. A mesma forma aparece em Pentest de APIs REST e GraphQL: Checklist Tecnico para Bug Bounty Legal sempre que endpoints de webhook ou image-proxy aceitam URLs internas sem allowlist. Entender o padrao e o que permite acha-lo num alvo e, mais importante, mata-lo no seu proprio codigo.

Montando o lab com LocalStack

Ambiente primeiro. Suba LocalStack Pro 4.x via docker-compose com ec2, iam, sts e s3 habilitados, mais um container Flask exposto na porta 5000 rodando uma API de fetch-image deliberadamente vulneravel. O codigo vulneravel tem umas dezoito linhas: recebe ?url=, chama requests.get sem validacao e devolve o body. Pra simular IMDS dentro do LocalStack voce usa o mock de metadata do ec2 ou um mock dedicado ligado a 169.254.169.254 via network namespace. Quem quer fidelidade maxima usa uma t3.micro real por uns centavos por hora, mas o LocalStack cobre cerca de 95% do aprendizado sem cartao de credito. O setup base de containers que voce reaproveita aqui esta coberto em Pentest Web do Zero: Montando um Lab Seguro com DVWA, Juice Shop e Burp Suite.

Explorando IMDSv1 passo a passo

Com o lab no ar, o payload classico do IMDSv1 e literalmente um GET. Do Burp, intercepte a requisicao da app e troque o parametro url por http://169.254.169.254/latest/meta-data/iam/security-credentials/. A resposta vaza o nome do role anexado, digamos app-server-role. Depois peca http://169.254.169.254/latest/meta-data/iam/security-credentials/app-server-role e voce recebe 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 os blue teams pulam das cadeiras durante a demo, porque um unico parametro sem validacao acabou de virar identidade completa de role.

Escalando com credenciais roubadas

A escalada depende do que o role pode fazer. No nosso lab anexamos 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 . puxa o dump, e aws iam list-attached-role-policies mapeia o caminho pra escalada de privilegio via PassRole. Ferramentas como Pacu, ScoutSuite e cloudfox automatizam essa enumeracao pos-comprometimento pra voce ver o raio de explosao rapido. Carimbe timestamp em cada comando, porque um relatorio de bug bounty sem um PoC reproduzivel paga zero, e uma linha do tempo limpa e o que transforma um achado num relatorio triado e pago. Se voce quer aprofundar em pivoting interno depois de pegar credenciais, Pivoting com Chisel e Ligolo-ng: Redes Segmentadas em Lab de Pentest e a proxima parada.

Por que o IMDSv2 quebra o ataque

O IMDSv2 quebra o ataque classico exigindo uma sessao de token. O fluxo correto e um PUT pra http://169.254.169.254/latest/api/token com header X-aws-ec2-metadata-token-ttl-seconds: 21600, depois um GET carregando o header X-aws-ec2-metadata-token. Um SSRF que so faz um GET, sem controle sobre metodo ou headers, simplesmente trava: nao consegue obter um token, entao nao consegue ler credenciais. Force o IMDSv2 com aws ec2 modify-instance-metadata-options --http-tokens required --http-put-response-hop-limit 1. O hop-limit de 1 e crucial: impede que containers em rede bridge alcancem o IMDS via o NAT do host, fechando um caminho comum de container pra metadata. Combine isso com um bloqueio de egress de 169.254.0.0/16 no security group e a app perde qualquer rota pro endpoint magico.

Defesa em profundidade alem do IMDS

A defesa nao termina no IMDS. Valide URLs na aplicacao, 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 e fd00::/8, e resolva o hostname antes da requisicao pra derrotar DNS rebinding, depois fixe esse IP resolvido pra conexao real. Use uma biblioteca auditada como ssrf-protect ou implemente com getaddrinfo mais checagens de IP por familia; uma blocklist ingenua de strings e burlada trivialmente com IPs decimais, enderecos IPv6-mapeados ou redirects. Corte as permissoes do role ao minimo, prefira IRSA no EKS pra que os pods recebam credenciais de vida curta e escopadas, habilite GuardDuty pra uso anomalo de credenciais, e rode scanners como Prowler regularmente. Tecnicas web relacionadas vivem 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 no bug bounty corporativo.

Deteccao e monitoramento

Assuma que a prevencao vai falhar de vez em quando e instrumente pra deteccao. Roubo de credenciais via IMDS tem um sinal: o mesmo role IAM de repente autenticando de um IP fora da sua infraestrutura. O finding UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration do GuardDuty foi feito exatamente pra isso, entao habilite e roteie pra um canal de plantao. Na camada de aplicacao, logue cada URL de saida que o fetcher resolve e alerte em qualquer requisicao cujo destino caia numa faixa privada ou link-local, porque um fetch legitimo de avatar nunca aponta pra 169.254.169.254. Alimente esses logs no mesmo pipeline que voce usa pra outras deteccoes pra que uma tentativa de SSRF vire um alerta que toca em vez de uma linha que ninguem le.

Armadilhas comuns

Vale nomear os erros recorrentes. Times bloqueiam o string literal 169.254.169.254 e perdem 2852039166, sua forma decimal, ou um hostname controlado pelo atacante que resolve pra ele. Validam a URL uma vez, depois seguem redirects que apontam direto de volta pro metadata service. Forcam IMDSv2 em instancias novas mas deixam uma cauda longa de antigas em optional. Poem hop-limit mas esquecem a regra de egress, ou o inverso. E dao ao role da instancia muito mais do que ele precisa, entao ate um vazamento breve de credenciais vira um comprometimento total de conta. Defesa em profundidade existe justamente porque qualquer um desses pontos vai falhar em algum momento.

Checklist de hardening

Suba isso hoje: rode aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens]' sobre seu inventario agora, e qualquer instancia que retorne optional esta exposta ao SSRF classico; force required em massa via um SSM Automation Document; ponha hop-limit em 1; adicione um bloqueio de egress pra 169.254.0.0/16 onde for viavel; audite roles com policies *:* e corte-as; valide e fixe URLs de saida na app com uma biblioteca auditada; habilite GuardDuty e roteie o finding de exfiltracao pro plantao; e adicione um teste E2E no CI que tenta alcancar 169.254.169.254 do container da app e falha o build numa resposta 200.

FAQ: IMDSv2 sozinho basta pra parar SSRF?

O IMDSv2 derrota o caminho especifico de SSRF pra roubo de credenciais quando o SSRF se limita a GETs simples, o que cobre a grande maioria dos casos reais. Nao e um fix completo de SSRF. Um atacante que consegue controlar metodos e headers, ou que pivota pra outros servicos internos alem do IMDS, ainda tem espaco. Trate o IMDSv2 como um controle obrigatorio e de alto valor, e adicione validacao de URL, roles de minimo privilegio e restricoes de egress pra que o metadata service nao seja sua unica linha de defesa.

FAQ: Da pra praticar isso com seguranca sem fatura da AWS?

Sim. O LocalStack Pro te da EC2, IAM, STS e S3 mais um mock de metadata, que reproduz cerca de 95% do aprendizado sem gasto de nuvem e sem risco de tocar em nada que nao seja seu. Mantenha a app Flask vulneravel e o mock de metadata estritamente numa rede docker local. Quando eventualmente voce quiser fidelidade maxima pra um edge case especifico, uma t3.micro real custa uns centavos por hora, mas nunca aponte nada desse tooling pra infraestrutura que voce nao esta autorizado a testar.

Conclusao

Takeaway pratico: leva uns dezessete minutos pra reproduzir o padrao da Capital One no lab, e leva os mesmos dezessete minutos pra fechar o buraco em producao. Rode a query de inventario agora, force IMDSv2 em todo lugar, corte os roles super-permissivos, valide URLs de saida nas suas apps, e adicione o teste de CI que falha o build se o container da app alcanca 169.254.169.254. SSRF contra metadata de nuvem nao e exotico; e um problema de configuracao padrao com um fix bem entendido. Os times que sofrem breach nao sao os que nao tinham o conhecimento, mas os que nunca rodaram a query.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly