Bem-vindo de volta ao Destaque de Vulnerabilidade do Sherlock, onde destacamos uma vulnerabilidade impactante descoberta durante uma auditoria do Sherlock. Esta semana, examinamos um cálculo incorreto do nível de garantia encontrado no concurso de @plaza_finance por @0xadrii, @KupiaSecurity, @f, @farman1094_ e @0xnovaman33.
Resumo da vulnerabilidade: Em getRedeemAmount(...), o contrato calcula o nível de garantia para resgates de BOND usando o estado pós-negociação: Código atual (vulnerável) nível de garantia = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISÃO) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); Como o preço de resgate usa esse colateralLevel estimado para decidir se deve pagar o preço máximo (US$ 100), um invasor pode comprar BOND a taxas de criação mistas (às vezes com desconto) até que o nível de garantia (atual) caia ≤ 1,2 e, em seguida, resgatar todos os BOND no limite de US$ 100, calculando com um depositAmount criado que empurra o nível de garantia estimado de volta acima de 1,2. Isso permite que o invasor extraia ETH (spread "livre de risco") até que o nível de garantia atual do pool atinja o limite.
Etapas de ataque: 1) Fase de configuração Parâmetros de exemplo de pool: - poolReserve = 120 ETH, bondSupply = 3000, levSupply = 200, preço ETH = $ 3075 Regras de preços: Criar (comprar) BOND: - Se colateralLevel ≤ 1.2: creationRate = tvl * 0.8 / bondSupply - Caso contrário: creationRate = $ 100 Resgatar (vender) BOND: - Se a garantia for estimadaNível ≤ 1,2: redeemRate = tvl * 0,8 / bondSupply - Else: redeemRate = $ 100
2) Fase A – Compre BOND a taxas mistas/com desconto - Compre BOND enquanto monitora a garantia (atual) Level = tvl / (bondSupply * 100). - A primeira compra ocorre enquanto o nível de garantia > 1,2 → cunhagem perto de US $ 100 por BOND. - As compras subsequentes empurram o nível de garantia atual abaixo de 1,2 → hortelã com desconto (por exemplo, ~ $ 94,07 pol.), acumulando um grande saldo de BOND de forma barata.
3) Fase B – Resgatar todos os TÍTULOS ao preço máximo - Chame resgatar(BOND, depositAmount = attackerBondBalance, ...) - O contrato calcula o nível de garantia estimado usando o estado pós-resgate (bondSupply - depositAmount) e (tvl - depositAmount * 100) e (no exemplo) obtém um valor > 1,2 - Como esse valor estimado excede o limite, redeemRate é definido como o máximo de US$ 100, permitindo que o invasor saque todo o BOND acumulado em US$ 100
4) Realização de lucro - A diferença entre compras com desconto e vendas de US$ 100 gera lucro líquido de ETH. - Nos números do PoC: gastar 60 ETH em duas compras retorna ~ 61,89 ETH no resgate → lucro de ~ 1,89 ETH. - O invasor pode iterar até que o nível atual de garantia do pool caia para ~ 1,2, extraindo aproximadamente: USD extraível ≈ ethPrice × poolReserve − 120 × bondSupply
Qual é o impacto? Extração direta de fundos / vazamento de valor: o invasor cunha com taxas de desconto e resgata a US$ 100, desviando ETH do pool. Não associado até o limite: pode continuar até que o nível de garantia atual do pool diminua para ≈ 1,2. Inconsistência sistêmica de preços: cria arbitragem pela qual usuários honestos pagam por meio de reservas/resultados de pool piores.
A causa raiz: Uso do estado pós-negociação para resgates de preços O código calcula collateralLevel como se o resgate já tivesse acontecido: usos (tvl - depositAmount*100) e (bondSupply - depositAmount) nível de garantia = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISÃO) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); 1. Isso permite que um invasor escolha depositAmount de forma que o nível estimado ultrapasse o limite (> 1.2), desbloqueando o redeemRate de US$ 100, mesmo quando o estado atual do pool não o justificar. 2. Limite de restrição em uma métrica manipulada A decisão do limite de US$ 100 depende desse nível de garantia estimado manipulável, e não do nível atual (pré-negociação), permitindo o jogo de preços.
A mitigação: Calcule o preço de resgate do estado atual do pool, não dos saldos estimados pós-resgate. A correção sugerida pelo projeto está correta: - colateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) - / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); + colateralLevel = (tvl * PRECISION) / (bondSupply * BOND_TARGET_PRICE);
Endurecimento adicional (recomendado): 1. Monotonicidade do preço: Certifique-se de que o preço de resgate não aumente à medida que o depositAmount aumenta (sem "vender mais, obter um preço unitário melhor"). 2. Precificação baseada em invariantes: derive a criação/resgate de um único invariante para que a simetria de compra/venda evite a arbitragem unilateral. 3. Verificações de derrapagem: exija taxas mínimas/máximas fornecidas pelo usuário para criar e resgatar. 4. Cap & throttle: Limites de resgate por tx e por bloco para limitar o dano se os limites forem atingidos. 5. Consistência entre caminhos: alinhe os caminhos de criação e resgate para usar a mesma definição de nível de garantia (somente estado atual).
Estamos orgulhosos de ter ajudado a garantir @plaza_finance por meio dessa descoberta. Quando absolutamente precisa ser seguro, Sherlock é a escolha certa.
3,4K