Back to Blog

#3: Incidente KyberSwap: Exploração Magistral de Erros de Arredondamento com Cálculos Extremamente Sutis

Code Auditing
10 de fevereiro de 2024
7 min read

Em 23 de novembro de 2023, ocorreu uma série de ataques direcionados ao KyberSwap. Esses ataques resultaram em uma perda total de mais de $48 milhões. O problema fundamental originou-se de uma direção de arredondamento incorreta durante o processo de reinvestimento do KyberSwap. Isso subsequentemente levou a cálculos de tick impróprios e, em última análise, à contagem dupla de liquidez.

Publicamos um relatório extenso, "Yet Another Tragedy of Precision Loss: An In-Depth Analysis of the KyberSwap Incident", que aprofunda os detalhes do evento. Para uma compreensão mais aprofundada, recomendamos a consulta à análise completa. A seguir, fornecemos uma introdução sucinta a este incidente, destacando-o como um dos dez principais incidentes de segurança de 2023.

Contexto

KyberSwap é uma plataforma descentralizada de formador de mercado automatizado (CLAMM). Para atender à demanda de mercado de liquidez concentrada, o KyberSwap Elastic foi lançado com base no Uniswap V3, com diversas melhorias, incluindo a curva de reinvestimento para permitir o auto-compounding dos rendimentos de provisão de liquidez.

1. Tick e Preço de Raiz Quadrada

O Tick em CLAMMs similares ao Uniswap V3 é utilizado para marcar o preço de forma discreta, de modo que os LPs possam fornecer liquidez dentro de um intervalo fixo em vez de todo o intervalo (daí o termo "concentrada").

A liquidez pode ser colocada em um intervalo entre quaisquer dois ticks (que não precisam ser adjacentes), ou seja, um par de índices de tick (um tick inferior e um tick superior). Especificamente, o preço de cada tick (em um índice inteiro i) é definido da seguinte forma:

Na prática, o preço de raiz quadrada (denotado como sqrtP ou sqrtPrice) é utilizado:

Também é possível calcular o tick atual com base no preço de raiz quadrada atual:

Obviamente, enquanto apenas um único preço de raiz quadrada é calculado para um dado tick, múltiplos preços de raiz quadrada podem apontar para o mesmo tick. Para uma explicação mais detalhada, consulte a documentação do Uniswap V3 e do KyberSwap.

2. Curva de Reinvestimento

O CLAMM baseado no Uniswap V3 sofre com a baixa utilização do pool pelas taxas de LP e com as significativas taxas de gas necessárias para o reinvestimento. Portanto, o KyberSwap adotou a curva de reinvestimento para resolver o problema.

A chave da curva de reinvestimento é que as taxas coletadas em cada swap são acumuladas como liquidez adicional no pool como a liquidez de reinvestimento dentro de um intervalo infinito. Os tokens de reinvestimento são emitidos para os LPs e a liquidez de reinvestimento acumulada é alocada aos LPs de acordo. Além disso, a liquidez de reinvestimento também participa do processo de swap e cálculo de preço.

O código correspondente aos cálculos introduzidos acima é exibido na função computeSwapStep no seguinte trecho de código do pool correspondente.

Deve-se observar que, devido à liquidez de reinvestimento, a liquidity nesta função é a soma de dois componentes: baseL para a liquidez base e reinvestL para a liquidez acumulada para o reinvestimento.

3. Swap no KyberSwap

A implementação da função swap do pool do KyberSwap discutida anteriormente pode ser abstraída no diagrama abaixo:

A lógica crucial relacionada ao cálculo do tick reside dentro do loop while de swap, destacado pelo retângulo azul. Especificamente, a lógica principal envolve a função computeSwapStep e a função _updateLiquidityAndCrossTick. A primeira calcula estados-chave, como quantidades de entrada e saída para o swap dado e nextSqrtP, enquanto a segunda lida com casos em que um cross-tick ocorre.

Tradicionalmente, quando o preço aumenta, nos referimos a isso como deslocar o tick para a direita/para cima; caso contrário, dizemos que o tick se move para a esquerda/para baixo.

Para melhor compreender a vulnerabilidade que será discutida adiante, é essencial explorarmos a lógica de código relevante da função computeSwapStep, conforme ilustrado na figura a seguir:

Especificamente, a função calcReachAmount calcula os tokens de entrada necessários para o preço alvo targetSqrtP (linhas 50-57). Se o usedAmount for maior que specifiedAmount, o tick não é cruzado e nextSqrtP é calculado a partir de deltaL (ou seja, o delta de liquidez, linhas 59-62). O deltaL é determinado usando a função estimateIncrementalLiquidity, e o preço final nextSqrtP é calculado com a função calcFinalPrice (linhas 70-79). Se menos entrada for necessária, nextSqrtP é definido como o preço do próximo tick, mas esse caso não é usado no ataque.

Os passos descritos acima deixam claro que, se o tick não for cruzado, o nextSqrtP retornado por computeSwapStep não deve ser maior que o sqrtP do próximo tick. No entanto, devido à dependência do preço em relação à liquidez (liquidez base e delta de liquidez) e à perda de precisão, os atacantes conseguem manipular o nextSqrtP para ser maior enquanto o tick não é cruzado.

Análise de Vulnerabilidade

A causa raiz reside no cálculo de tick defeituoso causado pela direção de arredondamento incorreta dentro do cálculo do delta de liquidez (ou seja, a função estimateIncrementalLiquidity) do contrato SwapMath (que é invocado pela função computeSwapStep). Isso, por sua vez, afeta impropriamente o cálculo do tick posteriormente.

Curiosamente, ao examinar o comentário na linha 188 (destacado pelo retângulo azul), descobrimos que deltaL deve ser arredondado para cima a fim de arredondar para baixo o nextSqrtP. No entanto, deltaL é erroneamente arredondado para baixo devido ao uso da função mulDivFloor na linha 189. Consequentemente, nextSqrtP é incorretamente arredondado para cima.

Análise do Ataque

Os atacantes iniciaram múltiplas transações de ataque, com cada transação drenando múltiplos pools. Por simplicidade, a discussão a seguir é baseada no primeiro ataque dentro da transação de ataque.

A lógica central do ataque consiste nas seguintes seis etapas:

  1. Empréstimo de 2.000 WETH via flash loan do AAVE.

  2. Troca de 6,850 WETH por 6,371 frxETH no pool vítima 0xfd7b. Esta etapa é usada para empurrar o tick atual e o currentSqrtP para uma posição onde atualmente não há liquidez presente.

  • currentSqrtP parece ser escolhido aleatoriamente pelo atacante, e o swap para exatamente nesse preço.
  • A liquidez base (baseL) é zero após esta etapa, mas a liquidez de reinvestimento (reinvestL) é diferente de zero.
  1. Adição de liquidez ao pool e em seguida remoção de parte da liquidez. Esta etapa é usada para controlar o intervalo e a liquidez total para um valor desejado.
  • O intervalo de tick é escolhido com base no currentSqrtP.
  • A liquidez desejada para o ataque pode ser derivada do intervalo de tick, embora a lógica de cálculo correspondente requeira exploração adicional.
  1. Troca de 387,170 WETH por 0,06 frxETH no pool. Esta etapa é usada para manipular o tick atual de modo que nextTick == currentTick. Especificamente, o swap na etapa 4 engana astuciosamente o pool para acreditar que o tick 111.310 não é cruzado. No entanto, na realidade, o currentSqrtP é de fato maior que o sqrtP do tick 111.310.
  1. Troca de 0,06 frxETH por 396,244 WETH no pool. Observe que a direção do swap é oposta em comparação com a etapa anterior. Nesta etapa, a liquidez é contada duplamente para tornar o swap lucrativo e, consequentemente, drenar o pool.
  1. Repagamento do flash loan e coleta de 6,364 WETH e 1,117 frxETH.

Para um exame aprofundado, consulte nossa análise abrangente, que inclui mais detalhes com cálculos e figuras.

Resumo

O problema fundamental neste incidente decorre do arredondamento incorreto durante o processo de reinvestimento do KyberSwap, levando a cálculos de tick imprecisos e, em última análise, à contagem dupla de liquidez. Este incidente destaca a natureza intrincada e evasiva dos problemas de perda de precisão dentro dos protocolos DeFi, representando um sério desafio para toda a comunidade.

Este ataque de 2023 se destaca por sua complexidade, com cálculos excepcionalmente refinados, servindo como um exemplo primordial dos muitos incidentes de segurança relacionados à precisão que testaram significativamente a comunidade. Além disso, após extensas negociações com as autoridades, o atacante emitiu uma mensagem de tom provocador ao público, afirmando uma exigência de controle total sobre o protocolo.

Leia outros artigos desta série:

Sign up for the latest updates
Perdas de ~5,57 milhões USD: exploração de falhas no X100 Freezer e no NEM | Boletim semanal da BlockSec
Security Insights

Perdas de ~5,57 milhões USD: exploração de falhas no X100 Freezer e no NEM | Boletim semanal da BlockSec

Este relatório semanal aborda dois incidentes de segurança na Ethereum e na BNB Chain no período de 19/10/2026–25/10/2026. Ele examina uma tomada de controle da governança do X100 Freezer financiada por um empréstimo-relâmpago e uma falha na liquidação de impostos que afetou um pool de liquidez de um token NEM na BNB Chain. As perdas estimadas no escopo do relatório totalizam aproximadamente 5,57 milhões USD: cerca de 4,75 milhões USD da transação representativa do X100 e cerca de 820 mil USD do incidente no pool do NEM. O total exclui as extrações subsequentes do X100 relatadas separadamente.

Perdas de ~3,87 milhões USD: ataques à Autheo Network e à UNCX | Resumo semanal da BlockSec
Security Insights

Perdas de ~3,87 milhões USD: ataques à Autheo Network e à UNCX | Resumo semanal da BlockSec

Este relatório semanal aborda 3 incidentes de segurança no período de 12/10/2026–18/10/2026, com perdas totais estimadas em aproximadamente 3,87 milhões USD. A Autheo Network sofreu um comprometimento de chave privada com perdas de cerca de 1,76 milhões USD, a Qubic Wallet perdeu cerca de 1,30 milhões USD devido à geração de seeds fracas, e os contratos Smart Token Vesting V2 da UNCX Network sofreram um erro contábil de cerca de 807 mil USD nas redes Ethereum, BNB Chain, Base e Robinhood Chain. A análise em destaque da UNCX explica como um cálculo incorreto do total de cotas misturou parcelas de vesting criadas com diferentes proporções entre tokens e cotas, permitindo saques acima do devido de pools compartilhados.

~US$ 542 mil perdidos: ataque ao MakerDaoBot | Boletim semanal da BlockSec
Security Insights

~US$ 542 mil perdidos: ataque ao MakerDaoBot | Boletim semanal da BlockSec

O relatório cobre 1 incidente no Ethereum (05–11/10/2026), com perda estimada de US$ 542 mil. Um adaptador malicioso obteve acesso aos saldos MakerDAO via função desprotegida do MakerDaoBot, liquidou 4 leilões ETH-A e converteu 200 ETH-A em WETH, depois enviados como ETH ao atacante.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit