Em 5 de março de 2025, um contrato de resolver terceirizado integrado ao protocolo Fusion V1 da 1inch sofreu um exploit coordenado que resultou em perdas totais superiores a $5 milhões. A causa raiz foi uma reconstrução insegura de calldata no fluxo de liquidação, onde um comprimento de interação controlado pelo atacante disparou um underflow de ponteiro durante a montagem do sufixo, permitindo que dados de liquidação forjados fossem injetados. O exploit foi ainda viabilizado por um limite de confiança mal posicionado: os contratos de resolver confiavam implicitamente em todo o calldata encaminhado pelo contrato de liquidação com base exclusivamente no msg.sender, fazendo com que dados controlados pelo atacante herdassem autoridade de nível de liquidação apesar de passar em todas as verificações de controle de acesso.
Este incidente se destaca como um dos exploits DeFi mais sofisticados de 2025, não por causa de uma primitiva financeira inovadora, mas pela exploração de suposições de layout de memória e ABI de baixo nível, borrando a linha entre vulnerabilidades de contratos inteligentes e técnicas clássicas de exploração binária.
Contexto
Protocolo Fusion V1 da 1inch
A 1inch é um agregador de exchange descentralizada (DEX) que busca liquidez em múltiplas DEXs para fornecer aos usuários uma execução de swap otimizada. Construído sobre a infraestrutura de ordens limitadas do agregador, o 1inch Fusion introduz um modelo de correspondência de ordens baseado em leilão holandês, permitindo que os usuários especifiquem parâmetros de execução flexíveis, como faixas de preço e janelas de tempo de swap.
No modelo Fusion, as ordens dos usuários não são preenchidas diretamente pelo próprio protocolo. Em vez disso, são executadas por atores especializados conhecidos como resolvers. Os resolvers podem ser vistos como participantes privilegiados e incluídos em lista branca: para se tornarem elegíveis, eles devem fazer staking de tokens para obter Unicorn Power suficiente, que serve como mecanismo econômico de controle de acesso.
Operacionalmente, os resolvers executam sua própria infraestrutura off-chain que interage com as APIs de backend da 1inch para descobrir ordens de usuários e determinar se e quando preenchê-las. Uma vez que um resolver decide executar uma ordem, ele envia uma transação on-chain por meio de uma conta dedicada ao contrato Settlement, que realiza o swap efetivamente. (Embora o Fusion envolva um leilão holandês competitivo entre resolvers, esse mecanismo não é essencial para entender o ataque e, portanto, é omitido aqui; por simplicidade, assumimos que o resolver pode satisfazer diretamente as condições de ordem do usuário.)
Ao executar um swap, um resolver pode obter liquidez de mercados externos para entregar uma execução melhor do que os requisitos mínimos do usuário, ou liquidar a negociação diretamente usando seus próprios ativos. Qualquer valor excedente gerado no processo é acumulado pelo resolver como lucro. O ataque discutido neste blog tem como alvo tal contrato de resolver. Especificamente, um contrato utilizado para formação de mercado on-chain que detinha ativos e havia concedido aprovações ao contrato Settlement, o que acabou resultando na perda desses ativos.
Processamento e Execução de Ordens
A execução de ordens no 1inch Fusion é coordenada pelo contrato Settlement e segue um modelo de liquidação recursiva orientado por dados de interação, em vez de um fluxo de execução linear.
O processo começa quando um resolver chama a função settleOrders(), passando calldata que codifica a primeira ordem junto com um payload de interação. Em vez de tentar liquidar todas as ordens de uma vez, o Settlement as processa de forma incremental por meio de invocações repetidas da função interna _settleOrder().
Para cada ordem, a função _settleOrder() reconstrói uma chamada ao Protocolo de Ordem Limitada (AggregationRouterV5.fillOrderTo()) na memória. Durante essa reconstrução, o Settlement anexa contexto adicional relacionado ao resolver aos dados de interação na forma de um sufixo dinâmico. Esse sufixo carrega metadados de execução, como a identidade do resolver e as taxas acumuladas, e é tratado como opaco pelo Protocolo de Ordem Limitada. Ele é encaminhado sem alterações e posteriormente decodificado pelo próprio Settlement durante o callback de interação.
Após a ordem ser preenchida, o controle retorna ao Settlement por meio da função fillOrderInteraction(). Com base nos dados de interação, o Settlement continua a liquidação invocando recursivamente a função _settleOrder() com o payload de interação restante ou transita para uma etapa de finalização. Na etapa final, o Settlement transfere o controle para o contrato de resolver invocando sua função resolveOrders(), passando o contexto de execução acumulado.
Esse design permite que múltiplas ordens sejam liquidadas sequencialmente em uma única transação, adiando a lógica específica do resolver até que todas as etapas de execução em nível de protocolo tenham sido concluídas.
Análise de Vulnerabilidade
A vulnerabilidade surge de uma lógica insegura de reconstrução de calldata na função interna _settleOrder(), especificamente durante o posicionamento do sufixo dinâmico anexado aos dados de interação.
Os contratos de resolver são protegidos por controle de acesso explícito: eles exigem que as chamadas originem do contrato Settlement e que o endereço do resolver fornecido durante a resolução corresponda ao próprio contrato de resolver. Essas verificações assumem que a identidade do resolver propagada pelo processo de liquidação é construída corretamente e permanece intacta. Essa suposição depende da corretude de como os dados relacionados ao resolver são codificados e anexados durante a reconstrução do calldata.
Concretamente, a função resolveOrders() do resolver aplica duas verificações:
require(msg.sender == _settlement, OnlySettlement());
require(this == resolver, NotTaker());
A primeira verificação confirma que a chamada se origina do contrato Settlement. A segunda confirma que o parâmetro resolver (decodificado do sufixo) corresponde ao próprio endereço do contrato de resolver. Em operação normal, ambos os campos são definidos corretamente por _settleOrder(). Durante o ataque, ambas as verificações ainda passam: o atacante opera dentro do fluxo de liquidação (não fora dele), portanto msg.sender é genuinamente o contrato Settlement; e o campo resolver é decodificado do sufixo forjado, onde o atacante escreveu o endereço do resolver vítima.
Durante a liquidação, _settleOrder() reconstrói o calldata na memória e anexa um sufixo dinâmico que carrega o contexto de execução específico do resolver. Esse sufixo inclui não apenas o endereço do resolver, mas também informações relacionadas à ordem que o resolver utiliza para interpretar a liquidação como legítima. O sufixo é escrito em uma localização de memória calculada como ptr + interactionOffset + interactionLength, onde interactionLength é lido diretamente do calldata como um valor de 32 bytes completo.
Como esse cálculo de offset é realizado sem verificação de limites, um atacante pode controlar interactionLength para induzir overflow aritmético e manipular onde o sufixo é escrito na memória. Ao fazer isso, o atacante pode substituir o sufixo pretendido por um forjado, fornecendo um endereço de resolver arbitrário junto com contexto de ordem controlado pelo atacante.
Como resultado, quando o fluxo de liquidação posteriormente decodifica o sufixo, ele pode interpretar a execução como originada de um resolver legítimo e envolvendo dados de ordem válidos, mesmo que nenhuma das suposições seja verdadeira. O atacante é efetivamente capaz de injetar sua própria versão do sufixo de ordem enquanto finge ser o resolver para trocar alguns wei por milhões.
Análise do Ataque
Tomando a transação 0x74bc como exemplo. O ataque começa com a criação de cinco ordens válidas que trocam uma quantidade insignificante de tokens (alguns wei) por uma quantidade de saída desproporcionalmente grande. Essas ordens são sintaticamente válidas e passam nas verificações em nível de protocolo.
O atacante então preenche o calldata da ordem com uma grande região de bytes nulos. Esse preenchimento serve como uma área de memória controlada que posteriormente será sobrescrita devido ao posicionamento incorreto do sufixo.
De forma crucial, o atacante especifica um valor inválido de interactionLength para a ordem final. O valor é escolhido como 0xffff…fe00, que corresponde a -512 quando interpretado como um inteiro com sinal. Como interactionLength é tratado como um valor sem sinal de 256 bits e usado diretamente na aritmética de ponteiros, isso causa um overflow aritmético ao calcular o offset de escrita do sufixo.
A estrutura de interação maliciosa abaixo é tratada como um sufixo:
Do Sufixo Forjado à Extração de Ativos
Quando o Settlement processa a ordem final, o overflow faz com que o sufixo legítimo seja escrito na região preenchida com nulos, sobrescrevendo zeros de forma inofensiva. O sufixo forjado colocado pelo atacante na posição de leitura real é então interpretado como contexto de liquidação legítimo.
O sufixo forjado define o flag de finalização, fazendo com que o Settlement transite do processamento recursivo de ordens para a etapa de finalização. O Settlement chama resolveOrders() no endereço decodificado do sufixo, que é o resolver vítima. Conforme descrito na Análise de Vulnerabilidade, ambas as verificações de controle de acesso passam: msg.sender é o contrato Settlement (a chamada flui pelo caminho legítimo de liquidação), e resolver corresponde ao endereço da vítima (escrito no sufixo forjado pelo atacante).
O resolver vítima então processa o array tokensAndAmounts, que inclui os endereços de ativos e os valores escolhidos pelo atacante. Como o resolver havia anteriormente concedido aprovações de token ao contrato Settlement para operação normal, a transferência é executada. O resolver envia seus ativos mantidos para cumprir ordens que exigiam apenas alguns wei de entrada, resultando em perdas superiores a $5 milhões.
Resumo
Este incidente teve como alvo um contrato de resolver terceirizado integrado ao protocolo Fusion V1 descontinuado da 1inch, resultando em perdas substanciais, o que destaca várias lições importantes.
- Suposições Inseguras sobre Dados: Sistemas que dependem de dados de entrada construídos dinamicamente não devem assumir comprimentos, offsets ou integridade estrutural quando qualquer parte é influenciada pelo usuário.
- Cadeias de Confiança Implícitas: Verificações de segurança podem ser comprometidas quando contratos dependem de componentes upstream para preservar contexto crítico, em vez de validá-lo em cada limite.
- Superfície de Ataque Legada: Suportar componentes desatualizados aumenta a superfície de ataque e torna os protocolos mais vulneráveis a técnicas novas de exploração.
- Padrões de Exploração Entre Domínios: Vulnerabilidades comuns em software tradicional podem reaparecer on-chain quando lógica de memória de baixo nível ou de layout de dados está envolvida.
Referências
-
https://blog.decurity.io/yul-calldata-corruption-1inch-postmortem-a7ea7a53bfd9
-
https://paragraph.com/@cookies-research/1inch-fusion-cost-efficient-mev-resistant-swaps
-
https://blog-zh.1inch.com/fusion-swap-resolving-onchain-component/#steps-5-6
Sobre a BlockSec
A BlockSec é uma provedora completa de segurança em blockchain e conformidade em criptomoedas. Desenvolvemos produtos e serviços que ajudam clientes a realizar auditorias de código (incluindo contratos inteligentes, blockchain e carteiras), interceptar ataques em tempo real, analisar incidentes, rastrear fundos ilícitos e cumprir obrigações de AML/CFT, ao longo de todo o ciclo de vida de protocolos e plataformas.
A BlockSec publicou múltiplos artigos de segurança em blockchain em conferências de prestígio, reportou vários ataques zero-day em aplicações DeFi, bloqueou múltiplos hacks para resgatar mais de 20 milhões de dólares e protegeu bilhões em criptomoedas.
-
Site oficial: https://blocksec.com/
-
Conta oficial no Twitter: https://twitter.com/BlockSecTeam



