Fluxos de tokens, NFTs e contratos inteligentes · Aula 6 de 6
Analisando exploits e hacks
10 min de leitura
Quando um protocolo ou uma exchange é hackeada, analistas recebem as mesmas perguntas em poucas horas: o que aconteceu, quanto foi levado, onde está agora e quem fez isso. As respostas vêm da leitura detalhada de um pequeno número de transações, seguida por um longo trabalho de rastreamento. Esta aula reúne as habilidades de decodificação do módulo (tokens, proxies, swaps, empréstimos relâmpago e bridges) e as aplica a casos públicos.
O ciclo de vida de um exploit nos dados
A maioria dos incidentes, independentemente da causa raiz, passa pelas mesmas fases, e cada fase deixa evidências diferentes:
| Fase | O que o invasor faz | Evidências a coletar |
|---|---|---|
| Financiamento | Obtém fundos para pagar o gas de novos endereços | Primeira transferência recebida por cada endereço do invasor e sua origem |
| Preparação | Implanta contratos de ataque, faz testes e, às vezes, começa por outras chains | Criações de contratos, tentativas malsucedidas e seus motivos de reversão, bytecode |
| Execução | Executa uma ou mais transações de ataque | Traces, logs, alterações de saldo por endereço e token |
| Consolidação | Converte tokens congeláveis ou ilíquidos em ETH ou outros ativos neutros e reúne os fundos | Swaps em DEX com grande impacto sobre o preço, transferências entre endereços do invasor |
| Dispersão | Divide os fundos, usa bridges e mixers, faz swaps para Bitcoin e converte os ativos em dinheiro | Padrões de dispersão, depósitos em bridges, depósitos em mixers, endereços de depósito em exchanges |
As fases se sobrepõem e, no caso de comprometimento de chaves, a preparação acontece off-chain. Ainda assim, organizar as evidências dessa forma evita que a investigação salte direto para a transação mais visível e deixe de identificar o financiamento, que pode ajudar a identificar o invasor.
Analisando a transação de ataque
Comece pelo trace, não pelos logs. Os logs registram o que os contratos decidiram anunciar; o trace registra cada chamada, seu valor e se foi bem-sucedida. Uma ordem prática de trabalho:
- Liste os participantes: a conta de propriedade externa que enviou a transação, o contrato de ataque que ela chamou, os contratos da vítima e qualquer endereço que termine com lucro.
- Calcule as alterações de saldo por endereço e por token para toda a transação (a consulta de netting da aula Swaps em DEX e fluxos de liquidez funciona aqui). O ganho líquido do invasor e a perda líquida da vítima contextualizam todo o restante.
- Percorra a árvore de chamadas desde o topo, identificando os empréstimos relâmpago (consulte a aula Empréstimos, bridges e contabilidade de protocolos) para não contabilizar o principal emprestado como perda.
- Encontre a etapa em que o estado do protocolo se tornou inconsistente. Essa é a causa raiz e, geralmente, corresponde a uma única chamada de função.
Um resumo das alterações de saldo de uma transação de ataque fictícia mostra o resultado da etapa 2:
{
"tx_hash": "0x7c0d…e91a",
"block_number": 21000417,
"balance_changes": [
{"address": "0xa77a…c0de", "role": "contrato de ataque", "token": "DAI", "delta": "+2400000.00"},
{"address": "0x5af3…71b2", "role": "pool da vítima", "token": "DAI", "delta": "-2401000.00"},
{"address": "0x87f1…4e2a", "role": "credor do empréstimo relâmpago", "token": "DAI", "delta": "+1000.00"},
{"address": "0x3e9d…02f7", "role": "EOA remetente", "token": "ETH", "delta": "-0.041"}
]
}
O empréstimo relâmpago de 2.000.000 DAI do credor não aparece, porque saiu e voltou na mesma transação; apenas o prêmio de 1.000 DAI (0,05%) permanece. A vítima perdeu 2.401.000 DAI, o contrato de ataque ficou com 2.400.000 e o remetente pagou 0,041 ETH em gas.
O exploit da Euler Finance, em 13 de março de 2023, ilustra o método. Análises publicadas por pesquisadores de segurança sobre uma das transações de ataque a reduzem às seguintes etapas:
Contrato de ataque, uma de várias transações de ataque (pool de DAI)
1. Empréstimo relâmpago de 30.000.000 DAI da Aave
2. Depositar 20.000.000 DAI na Euler -> tokens de garantia (eDAI)
3. mint(): alavancagem própria -> dívida de 200.000.000 dDAI, 195.600.000 eDAI
4. Reembolsar 10.000.000 DAI -> 10.000.000 dDAI queimados
5. Executar mint() novamente -> mais 200.000.000 dDAI e 195.600.000 eDAI
6. donateToReserves(100.000.000 eDAI) -> sem verificação de solvência: a conta agora está insolvente
7. Um segundo contrato de ataque liquida o primeiro com desconto de 20%
8. Retirar 38.900.000 DAI, reembolsar o empréstimo relâmpago mais o prêmio e ficar com o restante
A etapa 6 é a causa raiz: uma função que reduzia a garantia sem verificar a saúde da conta. As etapas 1 e 8 anulam o capital emprestado. Repetido em vários pools, o ataque levou cerca de US$ 197 milhões em DAI, WETH, WBTC, stETH e USDC. O invasor converteu a maior parte em ETH e DAI, enviou 100 ETH para um mixer, depois negociou com o protocolo por meio de mensagens on-chain e devolveu todos os fundos recuperáveis até 3 de abril de 2023. Vale a pena decodificar as mensagens on-chain: elas são texto nos dados de entrada de transferências simples e fazem parte do registro.
Financiamento, preparação e cópias do ataque
O financiamento de um endereço do invasor costuma ser a pista mais útil para atribuição. As origens mais comuns são saques de mixers (os pools do Tornado Cash usavam denominações fixas, como 0,1, 1, 10 e 100 ETH), bridges de outra chain, serviços de exchange instantânea e, ocasionalmente, um saque de uma exchange, que pode ser investigado por meio de processos legais (consulte a aula Rastreando valor por meio de exchanges e serviços). Verifique também:
- O implantador de cada contrato de ataque e outros contratos que ele implantou, incluindo versões de teste em outras chains.
- Similaridade de bytecode com contratos de ataque anteriores, que pode conectar incidentes.
- Tentativas malsucedidas antes da tentativa bem-sucedida: os motivos de reversão mostram o que o invasor estava testando e quando começou.
Nem todo endereço que obtém lucro pertence ao invasor original. Bots de MEV copiam transações pendentes lucrativas, e alguns grupos white hat antecipam ataques para resgatar fundos. No caso da Euler, um endereço que alegou que seu bot havia copiado por acidente o primeiro ataque acabou transferindo o DAI ao invasor mesmo assim, porque o contrato copiado tinha o endereço do invasor codificado diretamente. No incidente da bridge Nomad, em agosto de 2022, centenas de endereços simplesmente repetiram a transação do primeiro invasor, substituindo o endereço dele pelo próprio. Identifique esses participantes separadamente, indicando o grau de confiança, e nunca divulgue a identidade de uma pessoa privada com base apenas em vínculos desse tipo (consulte a aula Atribuição e rotulagem).
Quando o bug não está no código
Alguns dos maiores roubos não envolveram nenhuma falha em contratos inteligentes. No roubo da bridge Ronin, em março de 2022, os invasores controlavam cinco das nove chaves de validadores exigidas pela bridge, o suficiente para assinar saques de cerca de 173.600 ETH e 25,5 milhões de USDC; o roubo passou despercebido por seis dias. As autoridades dos EUA atribuíram o roubo ao Grupo Lazarus da Coreia do Norte em abril de 2022. On-chain, os saques estavam validamente assinados; apenas o contexto (tamanho, destinatário e momento incomuns) os denunciava.
O roubo da Bybit, em 21 de fevereiro de 2025, de cerca de US$ 1,5 bilhão, foi o maior roubo de criptoativos registrado quando aconteceu e serve de lição sobre como analisar o que os signatários realmente aprovaram. A carteira fria da exchange era um proxy multisig Safe. Os investigadores descobriram que a interface da carteira havia sido comprometida por meio de uma máquina de desenvolvedor do provedor da carteira, fazendo com que os signatários vissem uma transferência rotineira enquanto suas carteiras de hardware assinavam outra coisa. Decodificada, a transação assinada fica assim:
SafeProxy.execTransaction(...) -- seletor 0x6a761202, três assinaturas válidas
to = attacker contract
data = transfer(...) -- nome de função aparentemente inofensivo
operation = 1 -- DELEGATECALL, não CALL
DELEGATECALL attacker contract.transfer(...) -- executa com o armazenamento do proxy
SSTORE slot 0 := malicious implementation -- ponteiro do singleton Safe substituído
Depois: sweepETH(...) e sweepERC20(...) chamados no proxy, sem necessidade de assinaturas
Como um proxy Safe mantém seu ponteiro de implementação no slot de armazenamento 0 (consulte a aula Decodificando interações com contratos inteligentes), um delegatecall que grava no slot 0 toma o controle de toda a carteira. As transações de drenagem movimentaram então cerca de 401.346 ETH, 90.375 stETH, 15.000 cmETH e 8.000 mETH. Em 26 de fevereiro de 2025, o FBI atribuiu o roubo a agentes norte-coreanos que chama de TraderTraitor, publicou uma lista de endereços e informou que os fundos estavam sendo convertidos em Bitcoin e distribuídos por milhares de endereços em várias chains. As principais conclusões para analistas: decodificar todos os campos operation em transações multisig, emitir alertas para qualquer delegatecall a um contrato desconhecido e monitorar os slots de implementação de proxies de alto valor.
Rastreamento após o incidente
As primeiras horas determinam quanto pode ser congelado ou sinalizado. Uma sequência típica:
- Crie o conjunto inicial: todos os endereços controlados pelo invasor encontrados nas transações de ataque e nos vínculos de financiamento e implantação.
- Monitore os ativos congeláveis. Alguns emissores de stablecoins podem congelar saldos, por isso os invasores fazem swaps desses ativos primeiro; informe os endereços rapidamente.
- Compartilhe endereços e hashes de transações com exchanges, bridges e outros serviços que possam receber os fundos, pelos canais de denúncia estabelecidos.
- Rastreie a consolidação e a dispersão para a frente, salto a salto, entre chains (consulte as aulas Rastreando fundos para a frente e para trás, Rastreamento entre chains e Mixers, CoinJoin e ofuscação em grafos).
-- Primeiros dois saltos a partir dos endereços do conjunto inicial de um incidente, começando pelos maiores fluxos
WITH seeds AS (
SELECT address FROM incident_addresses WHERE incident_id = 'INC-0142'
), hop1 AS (
SELECT t.to_address, SUM(t.amount_usd) AS usd, MIN(t.block_time) AS first_seen
FROM value_transfers t JOIN seeds s ON t.from_address = s.address
WHERE t.to_address NOT IN (SELECT address FROM seeds)
GROUP BY t.to_address
)
SELECT h.to_address AS hop1, t.to_address AS hop2, SUM(t.amount_usd) AS usd
FROM hop1 h
JOIN value_transfers t
ON t.from_address = h.to_address AND t.block_time >= h.first_seen
GROUP BY h.to_address, t.to_address
ORDER BY usd DESC;
A consulta pressupõe uma tabela value_transfers que já combina transferências nativas dos traces com transferências de tokens, cada uma avaliada no momento da transferência. A primeira CTE encontra os endereços que receberam fundos do conjunto inicial (excluindo movimentações entre endereços desse conjunto); a etapa final acompanha cada um deles por mais um salto, contando apenas transferências posteriores ao primeiro recebimento. Fluxos grandes no segundo salto que chegam a serviços conhecidos são aqueles para os quais podem existir registros off-chain.
Elaborando o relatório de um incidente
Classifique a causa raiz, pois ela determina quais evidências são relevantes:
| Classe da causa raiz | Exemplo público | Assinatura típica nos dados |
|---|---|---|
| Falha de lógica | Euler, 2023 | Empréstimo relâmpago mais uma sequência incomum de chamadas que deixa o estado impossível |
| Inicialização ou controle de acesso | Nomad, 2022 | Muitas chamadas são bem-sucedidas com os mesmos dados de chamada |
| Verificação de assinatura | Wormhole, 2022 | Tokens cunhados sem depósito correspondente |
| Comprometimento de chave | Ronin, 2022 | Saques validamente assinados, sem bug no código |
| Interface ou cadeia de suprimentos | Bybit, 2025 | Transação validamente assinada com um delegatecall inesperado |
| Governança | Beanstalk, 2022 | Poder de voto emprestado, proposta executada na mesma transação |
Calcule as perdas de valor com base nos preços do bloco de cada transação de ataque, liste todas as transações de ataque com a altura do bloco e mantenha os valores recuperados separados das perdas. As aulas Redigindo o relatório analítico e Proteção de dados, ética e divulgação responsável abordam como apresentar e compartilhar essas conclusões.
Foco no exame: as cinco fases de um exploit e as evidências deixadas por cada uma; análise da transação de ataque a partir do trace, cálculo das alterações de saldo e abatimento do principal do empréstimo relâmpago; etapas do ataque à Euler e sua causa raiz (donateToReserves sem verificação de saúde); fontes de financiamento, vínculos com o implantador e o bytecode, tentativas malsucedidas e cópias do ataque como pistas para atribuição; comprometimentos de chaves e de interfaces (Ronin, Bybit) e o delegatecall Safe que reescreveu o slot 0; prioridades das primeiras horas (conjunto inicial, ativos congeláveis, notificação de serviços); consultas de saltos sobre transferências nativas e de tokens combinadas; classes de causas raiz e avaliação de perdas com base nos preços do bloco.