Quase nunca um principal IAM tem AdministratorAccess diretamente. Ele tem
iam:PassRole e lambda:CreateFunction. Ou iam:CreatePolicyVersion em uma política
anexada a ele mesmo. Ou consegue assumir uma role cuja trust policy ele pode reescrever. Nenhuma dessas
políticas, lida sozinha, parece perigosa. O perigo está na combinação, entre principais e entre contas.
Este artigo apresenta o escalint, uma ferramenta
open-source em Go que desenvolvi para encontrar essas combinações: ela lê um snapshot do IAM de uma ou
mais contas AWS, avalia as políticas do jeito que a AWS avalia, monta um grafo de quem pode agir como
quem e mostra, de forma visual e explicável, todos os caminhos que terminam em administrador, além do
menor conjunto de mudanças que encontra para cortá-los.
Há uma demo ao vivo com dados sintéticos, que roda
inteiramente no navegador.
O cenário atual
O lado ofensivo está muito bem servido
Quem ataca AWS tem ferramentas modernas e mantidas. O Pacu,
da Rhino Security Labs, é um framework de exploração com módulos que enumeram permissões e executam a
escalada. O CloudFox, da Bishop Fox, mapeia a
superfície de ataque de uma conta a partir de credenciais comprometidas. O
Stratus Red Team, da Datadog, emula técnicas
de ataque em cloud mapeadas ao MITRE ATT&CK. Esse ecossistema acompanha cada novo serviço e cada
nova técnica publicada.
O lado defensivo, nem tanto
Do lado de quem defende, o cenário de análise de acesso efetivo em IAM é bem mais fraco:
- Prowler: é o CSPM open source de referência, e com razão. Mas é amplo por natureza: centenas de checagens de configuração em vários provedores (S3 público, security groups abertos, criptografia, logging). As checagens de escalada olham política por política, procurando combinações de ações perigosas. Ele não responde "este principal, assumindo aquela role, consegue chegar a admin em dois passos?".
- PMapper: o projeto da NCC Group mostrou que IAM deve ser entendido como grafo e foi pioneiro nisso. Porém está sem atualização relevante há anos, e o catálogo de técnicas e serviços parou no tempo.
- CloudMapper e Cloudsplaining: o CloudMapper, da Duo Labs, parou em 2022. O Cloudsplaining, da Salesforce, gera bons relatórios de políticas excessivas, mas analisa cada política isoladamente e tem futuro incerto.
Em dezembro de 2025, a Datadog publicou o pathfinding.cloud, um catálogo aberto de caminhos de escalada de privilégio em AWS. No lançamento, dos 65 caminhos documentados, 27 (42%) não eram detectados por nenhuma ferramenta open source avaliada. Hoje o catálogo tem 92 caminhos. Ou seja: o atacante tem um framework que executa a técnica, e o defensor frequentemente não tem nada que a aponte antes.
Por que ver caminhos de forma visual
Existe uma frase conhecida de John Lambert, da Microsoft: "Defenders think in lists. Attackers think
in graphs. As long as this is true, attackers win." O Active Directory passou exatamente por isso. O
BloodHound nasceu como ferramenta ofensiva e acabou adotado por times de defesa, porque um grafo de
caminhos de ataque mostra o que nenhuma lista de achados mostra.
IAM na AWS tem o mesmo formato. Uma lista diz "a role X tem iam:PassRole em *" (média
severidade, perdida entre outras duzentas). Um grafo diz "o usuário de CI pode criar uma instância EC2
com a role Y, que pode anexar uma política a si mesma, que vira admin da conta". O primeiro é um
achado; o segundo é um caminho, com começo, meio e fim, e com um lugar óbvio para cortar.
Ver o caminho também muda a correção. Quando dez caminhos passam pela mesma aresta, essa aresta é um
choke point: removê-la resolve dez problemas de uma vez. Em uma lista, os dez aparecem como dez
itens independentes.
Como funciona
escalint collect (só leitura) ou aws iam get-account-authorization-details
│
parsing estrito do snapshot ─────── dados não confiáveis, limites de tamanho
│
avaliador de políticas IAM ──────── deny explícito, SCP, RCP, boundary,
│ trust policy, cross-account; trace por decisão
▼
grafo "pode agir como" ◄──────────── 39 regras adicionam arestas de escalada
│
┌──────────┬─────────┼──────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
k caminhos tiers blast radius choke points plano de correção
até Tier 0 (guloso)
│
analysis.json ──► texto · JSON · SARIF · DOT · Mermaid · UI web embutida
Avaliação fiel
O núcleo é um avaliador que segue a lógica documentada da AWS: deny explícito em qualquer política,
SCPs e RCPs por nível de OU, permissions boundaries, políticas de identidade, trust policies e regras
cross-account, incluindo NotAction, NotResource, NotPrincipal e wildcards em ações
e ARNs. Cada Allow vem com um trace dizendo qual statement decidiu. Condições que não podem ser
avaliadas estaticamente (MFA, IP de origem, horário) não são descartadas: o passo vira
conditional e aparece tracejado no grafo.
O grafo e as regras
Os nós são principais (usuários, roles, grupos) e um sumidouro por conta, o Tier 0 (admin
efetivo). As arestas vêm de 39 regras: 30 caminhos de escalada (EL-PE), 6 erros de trust
(EL-TR, por exemplo OIDC do GitHub Actions sem condição em sub) e 3 diagnósticos de
entrada (EL-PARSE). Toda regra cita a documentação da AWS, o ID do pathfinding.cloud e as
técnicas do MITRE ATT&CK correspondentes. A versão atual cobre 33 dos 92 caminhos do catálogo.
Análises
Sobre o grafo, o escalint calcula os k menores caminhos de cada principal até o Tier 0, tiers de
privilégio, raio de impacto (blast radius), ciclos de assume-role, choke points e um plano de
correção guloso: a sequência de mudanças, ordenada, que remove todos os caminhos. Para cada passo, ele
também diz quais eventos o CloudTrail registraria, ou se aquele passo é um ponto cego.
A interface
A UI é um app React em TypeScript estrito, com renderer WebGL próprio, embutido no binário. Ela roda local com escalint serve (só em 127.0.0.1, com token de uso único) ou como um único arquivo HTML offline com escalint report --html. São nove visões:
- Overview: tiers, caminhos críticos, pontos cegos do CloudTrail e o que corrigir primeiro.
- Atlas: todos os principais da conta (ou da organização), agrupados por conta, com zoom semântico; Shift+clique em dois nós mostra o menor caminho entre eles.
- Trace: um caminho passo a passo, com alternativas e choke points. Clicar em uma aresta abre o explicador de política, com o statement exato que permitiu aquele passo.
- Reach: o que um principal alcança, e quem o alcança, em anéis por número de saltos.
- Exposure, Findings, Fix plan, Detect e Diff: matriz principal × serviço, tabela filtrável, plano de correção com prévia ao vivo dos caminhos restantes, eventos do CloudTrail por passo e o que mudou entre dois snapshots.
Todo grafo tem uma tabela equivalente por trás, a navegação funciona pelo teclado e as paletas são seguras para daltonismo. Como o snapshot vem de uma conta que pode ter nomes e políticas hostis, a UI usa CSP estrita e nunca usa innerHTML, e --redact troca todo identificador por um pseudônimo antes de compartilhar um relatório.
Hands-on
Os binários de release (Linux, macOS e Windows, amd64 e arm64) já trazem a UI embutida e são assinados com cosign, com atestado de proveniência do GitHub. Também há uma imagem distroless:
# escolha a plataforma em https://github.com/brennocm/escalint/releases
curl -sSLO https://github.com/brennocm/escalint/releases/download/v0.1.0/escalint_0.1.0_linux_amd64.tar.gz
gh attestation verify escalint_0.1.0_linux_amd64.tar.gz --repo brennocm/escalint
tar xzf escalint_0.1.0_linux_amd64.tar.gz escalint
docker run --rm -v "$PWD:/work" ghcr.io/brennocm/escalint:v0.1.0 scan /work/snapshot.json
O fluxo básico são três comandos. O collect só faz chamadas de leitura (List* e Get*), e o repositório publica a política IAM mínima para ele:
escalint collect -o snapshot.json # snapshot só leitura com suas credenciais AWS
escalint serve snapshot.json # abre a UI em 127.0.0.1 com token de uso único
escalint scan snapshot.json # achados no terminal; exit 1 se houver high ou critical
Sem credenciais à mão, dá para usar as contas de exemplo do repositório. A saída real (resumida) do scan na conta vulnerável:
escalint scan examples/vulnerable-account/snapshot.json
1 account(s), 66 node(s), 114 edge(s), 15 path(s) to Tier 0; 19 critical, 28 high, 57 medium, 2 info
arn:aws:iam::111111111111:role/pe002-attacker (role, tier 1)
path p_a9433adab2c37415 [CRITICAL, allowed, 1 step(s)]
1. arn:aws:iam::111111111111:role/pe002-attacker --[modify_permissions EL-PE-002]--> effective admin (111111111111)
CRITICAL EL-PE-002 Put an inline policy on its own role
arn:aws:iam::111111111111:role/pe002-attacker can act as effective admin of account 111111111111 using iam:PutRolePolicy
policy: inline:arn:aws:iam::111111111111:role/pe002-attacker/p statement #0 (snapshot line 766)
ATT&CK: T1098.003 Account Manipulation: Additional Cloud Roles
fix: Remove iam:PutRolePolicy from the role's policies, or scope its Resource to roles other than itself
and add a permissions boundary that denies it.
arn:aws:iam::111111111111:role/tr002-ci-deploy (role, tier 3)
CRITICAL EL-TR-002 GitHub Actions OIDC trust without a sub condition
any GitHub Actions workflow, in any repository, can assume arn:aws:iam::111111111111:role/tr002-ci-deploy:
the trust has no condition on token.actions.githubusercontent.com:sub
policy: trust:arn:aws:iam::111111111111:role/tr002-ci-deploy statement #0 (snapshot line 1170)
ATT&CK: T1199 Trusted Relationship; T1078.004 Valid Accounts: Cloud Accounts
Cada achado aponta a política e a linha do snapshot que o causam, as técnicas ATT&CK e uma correção concreta. Os códigos de saída permitem usar o escalint como gate: 0 sem achados acima de --fail-on (padrão high), 1 com achados e 2 erro de uso.
Em CI
O caso de uso que mais me interessa é impedir que um caminho novo apareça sem ninguém perceber. O
escalint diff compara dois snapshots e falha apenas com arestas, caminhos e achados
adicionados. O repositório traz uma GitHub Action que, em um job diário, coleta com uma role só
leitura via OIDC, compara com o snapshot de ontem e envia o resultado em SARIF para o GitHub Code
Scanning:
escalint diff baseline.json snapshot.json --fail-on high
escalint scan snapshot.json --format sarif --output escalint.sarif
Onde o escalint se encaixa
O escalint não substitui nenhuma das ferramentas citadas. O Prowler continua sendo o lugar para
postura geral da conta; o escalint deliberadamente não olha bucket público nem security group aberto.
O Pacu e o CloudFox continuam sendo o lugar para exploração e validação ofensiva; o escalint não
assume roles, não cria credenciais e não altera nada na conta.
O espaço que ele ocupa é o do PMapper: análise defensiva, estática e explicável de acesso efetivo em IAM,
agora com SCPs e RCPs, catálogo atualizado e mapeado ao pathfinding.cloud, saída pronta para CI e uma
interface pensada para que o defensor enxergue o grafo que o atacante já enxerga. A ideia do grafo
self-hosted, em que nenhum dado sai da conta, veio do
aws-iam-graph, de marcelogsantos; o
escalint é uma reimplementação do zero.
Limitações
A v1 não modela session policies e session tags, políticas baseadas em recurso além da trust policy
(bucket policies do S3, key policies do KMS, Lambda, Secrets Manager), usuários root nem atribuições do
IAM Identity Center. Condições de requisição não são conhecidas estaticamente, então os passos afetados
ficam como conditional. A cobertura é de 33 dos 92 caminhos do pathfinding.cloud; a maioria dos
restantes passa uma role para outro serviço de computação e segue o mesmo molde das regras existentes.
O inventário de recursos é regional, o plano de correção é guloso (pequeno, mas não garantidamente
mínimo) e um snapshot é uma fotografia: depois de mudanças, é preciso coletar de novo.
E um cuidado importante: o snapshot e os relatórios são, literalmente, um mapa de quem consegue virar
administrador. Devem ser tratados como dado sensível e compartilhados só com --redact.
Considerações finais
Escalada de privilégio em IAM raramente é uma política obviamente errada. É uma soma de permissões
razoáveis, espalhadas por principais e contas diferentes, que ninguém revisou juntas. O atacante tem
ferramentas para encontrar e percorrer essa soma; o defensor, na maioria das vezes, tem uma lista.
O escalint tenta equilibrar isso: avaliar as políticas como a AWS avalia, desenhar o grafo, explicar
cada aresta com o statement que a permite e dizer onde cortar. Não é garantia de que uma conta está
segura, mas é uma forma de olhar para o IAM do mesmo jeito que quem vai atacá-lo.