0x.1 Contexto
Em 02:38 (UTC+8) do dia 15 de outubro de 2021, nosso sistema interno de monitoramento detectou transações suspeitas de flashloan:

Após investigação, descobrimos que se tratava de um ataque de manipulação de preços direcionado ao Indexed Finance. Especificamente, o atacante lançou o ataque explorando uma fórmula falha (utilizada para calcular o preço) deste projeto, obtendo um lucro de $16 milhões.
Já existem algumas discussões nas redes sociais, enquanto o projeto publicou um post-mortem oficial (Indexed Attack Post-Mortem). No entanto, as análises existentes não oferecem uma compreensão completa deste incidente de segurança. Portanto, neste blog, nosso objetivo é fornecer uma análise abrangente, incluindo o mecanismo do projeto, a vulnerabilidade, o ataque e o lucro.
0x1.1 Endereços de Contratos Relevantes
-
MarketCapSqrtController: 0x120c6956d292b800a835cb935c9dd326bdb4e011
-
DEFI5: 0xfa6de2697d59e88ed7fc4dfe5a33dac43565ea41
-
CC10: 0x17ac188e09a7890a1844e5e65471fe8b0ccfadf3
0x1.2 Transações do Ataque
-
TX-I do Ataque: 0x44aad3b853866468161735496a5d9cc961ce5aa872924c5d78673076b1cd95aa
-
TX-II do Ataque: 0xbde4521c5ac08d0033019993b0e7e1d29b1457e80e7743d318a3c27649ca4417
0x2. Mecanismo do Indexed Finance
Para melhor compreender a vulnerabilidade/ataque, utilizamos o DEFI5 (ou seja, o pool hackeado pelo atacante) para demonstrar o mecanismo do Indexed Finance.
0x2.1 Vinculação de Token
O DEFI5 foi projetado para fornecer serviço de negociação para os 5 principais tokens de projetos DeFi do Ethereum. Especificamente, o Indexed Finance atualiza o ranking dos tokens com base em sua capitalização de mercado através do MarketCapSqrtController. Como a classificação dos 5 principais tokens pode mudar com o tempo, o número de tokens utilizados pelo pool DEFI5 pode ser maior que 5, conforme mostrado no seguinte código:

A Figura 1 mostra que, para vincular um novo token, o DEFI5 deve acionar a função _bind invocada pela função reindexTokens, que só pode ser invocada pela função reindexPools do MarketCapSqrtController:

A Figura 2 demonstra que o MarketCapSqrtController primeiro busca informações do token (incluindo o TotalSupply e o preço) nos mercados, e então calcula o ranking com base em sua capitalização de mercado. Ao invocar a função reindexPool, os endereços dos principais tokens serão passados como argumentos para invocar a função reindexTokens. Note que o novo token adicionado será vinculado ao DEFI5 sem substituir os tokens originais do DEFI5.
0x2.2 O Que Vem a Seguir?
Após a vinculação do token, o DEFI5 deve definir uma variável chamada ready (que indica o status do token) como true para habilitar a negociação:

De acordo com a lógica do código, além da inicialização do contrato, o ready só pode ser definido na função gulp.
Conforme mostrado na Figura 3, isso ocorre quando o saldo do token no DEFI5 é maior ou igual a _minimumBalances.
Enquanto isso, o peso inicial do token (ou seja, denorm) será calculado com base na seguinte fórmula:

0x3. Análise da Vulnerabilidade
O código vulnerável pertence à função updateMinimumBalance do MarketCapSqrtController.

Conforme mostrado na Figura 4, updateMinimumBalance pode alterar o minimumBalance de um token cujo ready é false para 1/100 do poolValue. O cálculo do poolValue é a chave da vulnerabilidade.

O cálculo da Figura 5 apenas implementa a seguinte fórmula:

No entanto, existem dois problemas potenciais nessa fórmula:
- utilizar a liquidez de um token para estimar o valor de todo o pool;
- os pesos do pool (
_totalWeight) e do token (token.denorm) não são afetados pela mudança de liquidez. Na prática, eles são influenciados pela Capitalização de Mercado dos mercados externos. Além disso, sua variação é limitada pelo período de tempo, ou seja, aumento ou diminuição de 1% por hora.
Em resumo, o atacante é capaz de manipular o poolValue utilizando o flashloan para causar instantaneamente uma grande mudança na liquidez de um token, enquanto os pesos do pool e do token permanecem inalterados.
Ao fazer isso, o minimumBalance do token (cujo ready é false) pode ser manipulado para lançar o ataque de manipulação de preços.
0x4. Análise do Ataque
O ataque consiste nas seguintes 9 etapas:
Etapa 1: invocar a função reindexPool para vincular SUSHI. Antes da invocação, há 6 tokens no pool DEFI5, incluindo UNI, AAVE, COMP, SNX, CRV e MKR. À medida que a Capitalização de Mercado do SUSHI alcança o Top 5, o SUSHI será adicionado ao pool.

Etapa 2: emprestar todos os 6 tokens (UNI, AAVE, COMP, SNX, CRV e MKR) suportados pelo IndexPool através do SushiSwap.

Etapa 3: trocar UNI invocando a função swapExactAmountIn múltiplas vezes com os tokens emprestados (usando COMP como exemplo).
Aviso 1: aqui as múltiplas vezes são devido à restrição do
MAX_IN_RATIO, podendo-se trocar no máximo metade do saldo do token de uma vez.Aviso 2: nesta etapa, o
poolValueserá grandemente subestimado, pois o saldo de UNI (como ofirstToken) no pool diminui muito.

Etapa 4: modificar o minimumBalance do SUSHI invocando a função updateMinimumBalance.
Note que o
minimumBalanceé menor que o valor normal devido aopoolValueanormal calculado na etapa 3.

Etapa 5: preparar tokens LP invocando a função joinswapExternAmountIn para fornecer liquidez. Veremos que esses tokens LP são usados para recomprar mais SUSHI.
Note que: a função
joinswapExternAmountInprecisa ser invocada múltiplas vezes devido ao impacto doMAX_IN_RATIO.

Etapa 6: manipular o peso do SUSHI no pool DEFI5 primeiro emprestando uma enorme quantidade de SUSHI e transferindo-os para o pool, e então invocando a função gulp para definir o ready do SUSHI como true. Ao fazer isso, o peso inicial do SUSHI (denorm) se torna um valor elevado.

Etapa 7: trocar tokens LP de volta para tokens subjacentes (UNI, AAVE, COMP, SNX, CRV, MKR e SUSHI) invocando a função exitPool.
Note que a função
exitPoolNÃO considera o peso de cada token; como resultado, os tokens subjacentes serão devolvidos em proporções iguais.
Etapa 8: trocar tokens LP invocando a função joinswapExternAmountIn com SUSHI para fornecer liquidez. Mais tokens LP podem ser obtidos devido ao peso anormal do SUSHI naquele momento.


Note que a função joinswapPoolAmountIn irá cunhar o token LP com base no peso do token subjacente recebido (SUSHI neste caso).
Etapa 9: drenar o pool invocando a função exitPool com os tokens LP obtidos na etapa 8.
0x5. Análise do Lucro
Nossa investigação mostra que todo o dinheiro (incluindo as taxas de transação) utilizado pelo atacante provém do Tornado Cash.
No total, há duas transações de ataque:
-
Na primeira transação, o atacante obteve: 6.226,8 AAVE, 15 ETH, 192.358,6 UNI, 5.459,5 COMP, 721.611,3 CRV, 16.680,6 SNX, 406,5 MKR.
-
Na segunda transação, o atacante obteve: 109,6 MKR, 17.844 UMA, 1.002,4 COMP, 34.602,5 UNI, 131.645,4 BAT, 28.754,1 SNX, 1.273,6 AAVE, 124.194,2 CRV, 33.215,4 LINK, 5,24 YFI.
Além disso, o atacante também teve várias tentativas fracassadas.
No momento da redação deste artigo, o lucro obtido pelo atacante equivale a $16 milhões de dólares americanos e ainda não foi transferido.
Sobre a BlockSec
A BlockSec é uma empresa pioneira em segurança blockchain, fundada em 2021 por um grupo de especialistas em segurança de renome mundial. A empresa está comprometida em aprimorar a segurança e a usabilidade do emergente mundo Web3, visando facilitar sua adoção em massa. Para isso, a BlockSec oferece serviços de auditoria de segurança de contratos inteligentes e redes EVM, a plataforma Phalcon para desenvolvimento seguro e bloqueio proativo de ameaças, a plataforma MetaSleuth para rastreamento e investigação de fundos, e a extensão MetaSuites para construtores web3 navegarem com eficiência no mundo cripto.
Até o momento, a empresa atendeu mais de 300 clientes renomados, como MetaMask, Uniswap Foundation, Compound, Forta e PancakeSwap, e recebeu dezenas de milhões de dólares americanos em duas rodadas de financiamento de investidores de destaque, incluindo Matrix Partners, Vitalbridge Capital e Fenbushi Capital.
Site oficial: https://blocksec.com/
Conta oficial no Twitter: https://twitter.com/BlockSecTeam



