Bienvenido de nuevo al Enfoque de vulnerabilidades de Sherlock, donde destacamos una vulnerabilidad impactante descubierta durante una auditoría de Sherlock. Esta semana, examinamos un cálculo incorrecto del nivel de garantía encontrado en el concurso de @plaza_finance por @0xadrii, @KupiaSecurity, @f, @farman1094_ y @0xnovaman33.
Resumen de la vulnerabilidad: En getRedeemAmount(...), el contrato calcula el nivel de garantía para los reembolsos de BONOS utilizando el estado posterior a la negociación: Código actual (vulnerable) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISIÓN) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); Debido a que el precio de reembolso utiliza este collateralLevel estimado para decidir si paga el precio máximo ($ 100), un atacante puede comprar BOND a tasas de creación mixtas (a veces con descuento) hasta que el nivel de garantía (actual) caiga ≤ 1.2 y luego canjear todos los BOND al límite de $ 100, calculando con un depositAmount diseñado que empuja el nivel de garantía estimado por encima de 1.2. Esto permite al atacante extraer ETH (spread "libre de riesgo") hasta que el nivel de garantía actual del pool alcance el umbral.
Pasos de ataque: 1) Fase de configuración Parámetros de ejemplo de grupo: - poolReserve = 120 ETH, bondSupply = 3000, levSupply = 200, precio de ETH = $3075 Reglas de precios: Crear (comprar) BOND: - Si colateralLevel ≤ 1.2: creationRate = tvl * 0.8 / bondSupply - De lo contrario: creationRate = $100 Canjear (vender) BONO: - Si se estima colateralNivel ≤ 1.2: redeemRate = tvl * 0.8 / bondSupply - De lo contrario: redeemRate = $100
2) Fase A: compre BONOS a tasas mixtas / con descuento - Compre BONOS mientras monitorea el nivel de garantía (actual) = tvl / (bondSupply * 100). - La primera compra ocurre mientras colateralLevel > 1.2 → acuña cerca de $ 100 por BOND. - Las compras posteriores empujan el nivel de garantía actual por debajo de 1.2 → acuñación a una tasa de creación con descuento (por ejemplo, ~ $ 94.07 pulgadas), acumulando un gran saldo de BONOS a bajo precio.
3) Fase B – Canjear todos los BONOS al precio máximo - Rescatar a la carta(BOND, depositAmount = attackerBondBalance, ...) - El contrato calcula el nivel de garantía estimado utilizando el estado posterior al reembolso (bondSupply - depositAmount) y (tvl - depositAmount * 100) y (en el ejemplo) obtiene un valor > 1.2 - Debido a que ese valor estimado supera el umbral, redeemRate se establece en un máximo de $100, lo que permite al atacante retirar todo el BOND acumulado a $100
4) Realización de ganancias - La diferencia entre las compras con descuento y las ventas de 100 dólares produce un beneficio neto de ETH. - En los números de PoC: gastar 60 ETH en dos compras devuelve ~61.89 ETH en el canje → ~1.89 ETH de ganancia. - El atacante puede iterar hasta que el nivel de garantía actual del grupo caiga a ~1.2, extrayendo aproximadamente: USD extraíble ≈ ethPrice × poolReserve − 120 × bondSupply
¿Cuál es el impacto? Extracción directa de fondos / fuga de valor: el atacante acuña a tasas de descuento y canjea a $ 100, desviando ETH del grupo. Sin límite hasta el umbral: puede continuar hasta que el nivel de garantía actual del grupo se reduzca a ≈ 1,2. Inconsistencia sistémica de precios: crea arbitraje que los usuarios honestos pagan a través de peores reservas/resultados del grupo.
La causa raíz: Uso del estado posterior a la negociación para los reembolsos de precios El código calcula collateralLevel como si el reembolso ya hubiera ocurrido: utiliza (tvl - depositAmount*100) y (bondSupply - depositAmount) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISIÓN) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); 1. Esto permite a un atacante elegir depositAmount de modo que el nivel estimado cruce el umbral (> 1.2), desbloqueando el redeemRate de $ 100, incluso cuando el estado actual del grupo no lo justificaría. 2. Umbral de acceso en una métrica manipulada La decisión del límite de 100 dólares depende de este nivel de garantía estimado manipulable en lugar del nivel actual (previo a la negociación), lo que permite jugar con los precios.
La mitigación: Calcule los precios de reembolso a partir del estado actual del grupo, no de los saldos estimados posteriores al reembolso. La solución sugerida por el proyecto es correcta: - collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISIÓN) - / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); + collateralLevel = (tvl * PRECISIÓN) / (bondSupply * BOND_TARGET_PRICE);
Endurecimiento adicional (recomendado): 1. Monotonicidad del precio: asegúrese de que el precio de canje no aumente a medida que aumenta depositAmount (no "venda más, obtenga un mejor precio unitario"). 2. Precios basados en invariantes: Derive crear/canjear de un solo invariante para que la simetría de compra/venta evite el arbitraje unilateral. 3. Comprobaciones de deslizamiento: Requiere tasas mínimas/máximas proporcionadas por el usuario tanto para crear como para canjear. 4. Límite y aceleración: límites de canje por tx y por bloque para limitar el daño si se acercan a los umbrales. 5. Coherencia entre rutas: alinee las rutas de creación y canje para usar la misma definición de nivel de garantía (solo estado actual).
Estamos orgullosos de haber ayudado a asegurar @plaza_finance a través de este descubrimiento. Cuando es absolutamente necesario que sea seguro, Sherlock es la elección correcta.
3.26K