Back to Blog

A Análise do Incidente de Segurança do Popsicle Finance

Code Auditing
August 4, 2021
4 min read

Em 4 de agosto de 2021, a Popsicle Finance sofreu uma enorme perda financeira (mais de $20M) devido a um ataque [1]. Após análise manual, confirmamos que se trata de um ataque de double-claiming (dupla reivindicação), ou seja, uma brecha em seu sistema de recompensas permite que o atacante reivindique recompensas repetidamente. A seguir, usaremos uma transação de ataque para ilustrar o processo do ataque e a causa raiz da vulnerabilidade.

Contexto

A Popsicle Finance é uma plataforma de otimização de rendimento que suporta múltiplos vaults para diferentes redes (por exemplo, Ethereum e BSC).

Especificamente, um usuário primeiro invoca a função deposit para fornecer liquidez e recebe o token LP da Popsicle (PLP, abreviado). Após isso, a Popsicle Finance gerenciará a liquidez (interagindo com plataformas como a Uniswap) em benefício do usuário para gerar lucros. O usuário pode invocar a função withdraw para recuperar a liquidez da Popsicle Finance, que calculará o valor com base nos tokens PLP. A recompensa de incentivo provém da liquidez, que se acumula ao longo do tempo. O usuário pode invocar a função collectFees para reivindicar as recompensas, que é o ponto central deste ataque.

Análise de Vulnerabilidade

Na função collectFees, token0Reward e token1Reward (recompensas do par de tokens LP correspondente) são calculados para o usuário. Toda a lógica de cálculo é direta. No entanto, a função utiliza um modificador chamado updateVault, que é usado para atualizar as recompensas de acordo.

Em resumo, o updateVault irá:

  1. primeiro invocar a função _earnFees para obter a taxa acumulada do pool;
  2. em seguida invocar a função _tokenPerShare para atualizar token0PerShareStored e token1PerShareStored, que representam as quantidades de token0 e token1 no pool para cada cota;
  3. por fim invocar as funções _fee0Earned e _fee1Earned para atualizar as recompensas do usuário (ou seja, token0Rewards e token1Rewards respectivamente).

As funções _fee0Earned e _fee1Earned compartilham a mesma lógica, ou seja, implementam a seguinte fórmula (usando token0 como exemplo):

user.token0Rewards += PLP.balanceOf(account) * (fee0PerShare - user.token0PerSharePaid) / 1e18

Observe que o cálculo é incremental, o que significa que mesmo que o usuário NÃO possua token PLP, a recompensa calculada permanece com o valor armazenado em token0Rewards.

Portanto, podemos concluir as seguintes duas observações:

  1. as recompensas do usuário são armazenadas em token0Rewards e token1Rewards, que não estão associadas a nenhum token PLP;
  2. a função collectFees depende apenas do estado de token0Rewards e token1Rewards, o que significa que as recompensas podem ser retiradas sem a posse do token PLP.

No cenário do mundo real, isso significa que um usuário deposita dinheiro em um banco e o banco lhe entrega um certificado de depósito. Infelizmente, esse certificado não possui mecanismos antifalsificação nem está associado ao usuário. Nesse caso, é possível fazer cópias e distribuí-las a outros para obter lucros do banco.

Fluxo do Ataque

De forma resumida, o atacante seguiu os seguintes passos para executar o ataque:

  1. criou três contratos. Um deles foi usado para lançar o ataque, enquanto os outros dois foram usados para invocar a função collectFees e buscar as recompensas;
  2. utilizou o Flash Loan, ou seja, tomou emprestado uma grande quantidade de liquidez da AAVE;
  3. executou o ciclo Deposit-Withdraw-CollectFees para realizar o ataque (há 8 ciclos no total, e grande quantidade de liquidez foi retirada de múltiplos vaults da Popsicle Finance);
  4. devolveu o Flash Loan à AAVE e lavou os lucros por meio do Tornado.Cash.

Especificamente, o ciclo Deposit-Withdraw-CollectFees consiste em várias etapas, que podem ser facilmente identificadas e claramente resumidas utilizando nossa ferramenta online [2]:

Análise de Lucro

No total, o atacante obteve $20M da Popsicle Finance, incluindo 2,56K WETH, 96,2 WBTC, 160K DAI, 5,39M USDC, 4,98M USDT e 10,5K UNI. Após a exploração, o atacante primeiro trocou todos os outros tokens por ETH por meio da Uniswap e WETH, e em seguida realizou a lavagem de dinheiro utilizando o Tornado.Cash.

Créditos

Yufeng Hu, Ziling Lin, Junjie Fei, Lei Wu, Yajin Zhou @BlockSec

(Em ordem alfabética pelo sobrenome)

https://www.blocksecteam.com

Medium: https://blocksecteam.medium.com/

Twitter: https://twitter.com/BlockSecTeam

Contato: [email protected]

Referências

[1] https://twitter.com/defiprime/status/1422708265423556611

[2] https://tx.blocksecteam.com/

Best Security Auditor for Web3

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

BlockSec Audit