fondo
El 22 de agosto, Balancer anunció oficialmente que había recibido informes de vulnerabilidad graves que afectaban a varios grupos de V2 Boost. Solo el 1,4% de TVL se vio afectado. Se suspendieron varios grupos y se notificó a los usuarios que retiraran LP de liquidez lo antes posible. [1] [2]
El 27 de agosto, el sistema SlowMist MistEye descubrió una transacción de ataque que se sospechaba que explotaba la vulnerabilidad Balancer. [3]
Dado que el grupo no se puede suspender y algunos fondos aún se ven afectados por el ataque, los funcionarios de Balancer recuerdan una vez más a los usuarios que recuperen el LP del grupo afectado. [4] Posteriormente, Balancer publicó oficialmente los detalles de la vulnerabilidad revelada en agosto en Medium [5], y el equipo de seguridad de SlowMist la revisó. Los detalles son los siguientes:
introducir
Los funcionarios de Balancer simplemente señalaron en su divulgación que el problema esta vez es que el redondeo a la baja del grupo lineal y el suministro virtual del grupo componible causaron que bptSupply fuera 0. Primero, echemos un vistazo breve al contenido del protocolo Balancer relacionado con esta vulnerabilidad.
Bóveda del equilibrador V2
El protocolo Balancer V2 [6] es un protocolo de creador de mercado automatizado (AMM) descentralizado basado en Ethereum que representa un componente flexible para la liquidez programable. Su componente principal es el contrato Vault, que mantiene registros de todos los grupos y gestiona la contabilidad y transferencia de tokens, incluido incluso el empaquetado y desembalaje de ETH nativo. En otras palabras, Vault se implementa para separar la contabilidad y la gestión de tokens de la lógica del grupo.
Hay cuatro interfaces en Vault, a saber, joinPool, exitPool, swap y batchSwap (unirse, salir e intercambiar son llamadas separadas y no hay combinación en una sola llamada). Una de las características destacadas es BatchSwap, que puede realizar múltiples intercambios atómicos entre múltiples grupos, conectando la salida de un intercambio de grupo con la entrada de otro grupo (GiveIn y GiveOut). El sistema también introduce Lightning Exchange [7], que es similar a un préstamo rápido interno.
Piscinas Lineales Piscinas Lineales
Para mejorar la eficiencia del capital de LP y el problema de los altos gastos generales de deformación y deformación, Balancer lanzó el grupo lineal como solución en V2, introduciendo así el token BPT (ERC20 Balancer Pool Token).
El pool lineal [8] contiene token principal (activo subyacente), token envuelto (token envuelto) y token BPT, e intercambia activos y sus contrapartes rentables envueltas a través de un tipo de cambio conocido. Cuanto mayor sea la proporción de tokens envueltos, mayor será el rendimiento y la eficiencia del capital del grupo. Durante el proceso de deformación, generalmente se utilizan factores de escala para garantizar que se calculen diferentes tokens con la misma precisión.
Piscinas componibles Piscinas componibles
Todos los grupos de Balancer son grupos componibles que contienen otros tokens y el grupo en sí tiene sus propios tokens. Entre ellos, la moneda BPT se refiere al token del grupo equilibrado ERC20, que es la base de todos los grupos. Los usuarios pueden combinar libremente tokens BPT en otros grupos para canjearlos. El canje siempre implica un fondo común y dos tokens: GiveIn y GiveOut. In significa enviar tokens constituyentes y recibir BPT, mientras que Out significa enviar BPT y recibir tokens constituyentes. Si el propio BPT fuera un token componente, podría intercambiarse como otros tokens. Dicha implementación constituye una ruta de intercambio por lotes simple entre los activos subyacentes y los tokens en el grupo externo. Los usuarios pueden usar BPT para intercambiar por los activos subyacentes del grupo lineal, que también es la base de Balancer Boosted Pool [9].
A través de la combinación anterior, se forma el grupo combinable de Balancer. Un grupo estable componible de bb-a-USD consta de tres grupos lineales y envía liquidez inactiva a un protocolo externo (Aave). Por ejemplo, bb-a-DAI es un grupo lineal que contiene DAI y waDAI (aDAI envuelto). Cuando el usuario necesita realizar un intercambio por lotes (como cambiar USDT a DAI), el ejemplo de ruta de intercambio es el siguiente:
1. En el grupo lineal de USDT, intercambie USDT por bb-a-USDT (ingrese al grupo lineal de USDT);
2. En bb-a-USD, bb-a-USDT se intercambia por bb-a-DAI (intercambio entre BPT lineal);
3. En el grupo lineal DAI, bb-a-DAI se intercambia por DAI (salga del grupo lineal DAI).
Después de comprender brevemente los conocimientos previos anteriores, ingresamos al enlace de análisis de vulnerabilidad.
analizar
El 27 de agosto, el equipo de seguridad de SlowMist recibió la identificación del sistema MistEye de que una supuesta vulnerabilidad de Balancer había sido explotada en estado salvaje. La transacción [3] es la siguiente:
El atacante primero prestó 300.000 USDC a través de un préstamo rápido de AAVE. Luego se llama a la operación BatchSwap de Vault para realizar el cálculo de intercambio de tokens BPT a través del pool estable combinable bb-a-USD. Finalmente, se intercambian 94,508 USDC por 59,964 bb-a-USDC, 68,201 bb-a-DAI y 74,280. bb-a-USDT. Finalmente, los tokens BPT obtenidos saldrán del pool a través del exitPool del contrato Vault a cambio de activos subyacentes, reembolsarán el préstamo flash y saldrán del mercado con una ganancia de aproximadamente 108.843,7 dólares estadounidenses.
Se puede ver que la clave de este ataque está en BatchSwap, y ¿qué sucedió exactamente en BatchSwap? Miremos más de cerca.
Durante todo el proceso de intercambio por lotes, el atacante primero canjeó USDC en el grupo bb-a-USDC y luego intercambió tokens BPT, intercambiando bb-a-USDC por bb-a-DAI, bb-a-USDT y USDC. Finalmente, el token principal subyacente USDC se intercambia por bb-a-USDT. Es decir, bb-a-USDC, como token BPT clave, sirve como token componente de GiveOut y GiveIn.
En el primer paso, el atacante intercambia tokens BPT por tokens principales de USDC en el grupo lineal bb-a-USDC con un factor de escala fijo, y la cantidad aumentada se registra en bptBalance en el grupo. Pero después del segundo intercambio onSwap, descubrimos que el valor de cantidad de USDC intercambiado durante el mismo proceso de intercambio era 0. ¿Por qué es esto?
Profundizando en la función onSwap, encontramos que en este proceso primero se procesará la precisión para nominalizar y se calculará el factor de escala del token correspondiente. Cuando se llama a continuación a la función downscaleDown, cantidadOut se redondea hacia abajo. Si el valor entre cantidadOut y scalingFactors[indexOut] es muy diferente, el valor de reducción de escala calculado será cero.

Es decir, cuando usamos tokens BPT para intercambiar tokens principales, si cantidad es demasiado pequeña, el valor de retorno se redondeará a cero y este valor es menor que 1e12 calculado por scalingFactors. Sin embargo, la cantidad de bb-a-USDC proveniente de cantidadIn aún se agregará a la cantidad virtual de bptBalance, y esta operación aumentará el saldo en el grupo de bb-a-USDC, lo que puede considerarse como una adición unilateral de bb. -a-Liquidez del USDC.


Luego, utilizando las características del grupo estable combinable, a través de la conversión mutua entre tokens BPT, primero se intercambia bb-a-USDC por otros tokens BPT. Para seguir este proceso de intercambio, puede combinar las siguientes rutas de llamada del grupo estable: bb-a-DAI onSwap -> swapGivenIn -> onSwapGivenIn Primero, reemplace bb-a-USDC con bb-a-DAI y bb-a-. USDT en secuencia. A diferencia de los pools lineales en línea, los pools estables componibles requieren una actualización de la caché del tipo de cambio antes de las operaciones onSwap. Podemos ver en el código que en el grupo combinado, onSwap primero determinará si es necesario actualizar el tipo de cambio del token almacenado en caché.


Después del intercambio anterior, la cantidad de bb-a-USDC ha cambiado, y la cantidad total real después de la nominalización a través de _toNominal es totalBalance 994,010,000,000, y el suministro virtual de tokens BPT es 20,000,000,000. Se puede calcular que el tipo de cambio actualizado es casi 45 veces el tipo de cambio de caché original del grupo lineal anterior de 1.100.443.876.587.504.549, que es 49.700.500.000.000.000.000.

Posteriormente, bb-a-USDC se intercambia por USDC en un pool lineal. Sin embargo, este intercambio es el mismo que el segundo intercambio, lo que nuevamente hace que cantidadOut se redondee a la baja a 0 y la ruta de intercambio es la misma que antes.
El siguiente intercambio es revertir USDC a bb-a-USDC, y la ruta de intercambio es onSwap -> onSwapGivenIn -> _swapGivenMainIn. Durante este proceso, descubrimos que al calcular la cantidad que debe canjearse, el cálculo del suministro virtual se basa en la diferencia entre el suministro total del token BPT canjeado y la cantidad restante en el grupo, que es 0.

Esto se debe a que bptSupply es 0 y la función _toNominal se llama directamente al calcular BPT Out, y la llamada de esta ruta hace que la relación de cambio de USDC a bb-a-USDC se acerque a 1:1.


Resumir
BatchSwap conecta la salida del intercambio de un grupo con la entrada de otro grupo (tokenIn y tokenOut) a través de múltiples intercambios atómicos entre múltiples grupos e intercambia USDC por tokens BPT. En este intercambio por lotes, no se produce ninguna transferencia de token real, pero el monto final del intercambio se confirma registrando el monto transferido hacia adentro y hacia afuera. Y debido a que el conjunto lineal se intercambia a través del token del activo subyacente, el método de intercambio consiste en calcular la Tasa a través de una oferta virtual y un algoritmo fijo. Por lo tanto, existen dos agujeros de seguridad en BatchSwap:
El primero es el problema de redondeo a la baja de los grupos lineales. El atacante agrega unilateralmente tokens principales al grupo mediante el redondeo para aumentar la proporción de tokens almacenados en caché, manipulando así el tipo de cambio de tokens en el grupo componible correspondiente;
En segundo lugar, debido a las características de suministro virtual del grupo combinable, el suministro virtual se calcula restando el saldo en el grupo del token BPT. Si GiveIn es un token BPT en el momento del canje, esta parte se deducirá del token BPT posterior. suministro, el atacante solo necesita intercambiar BPT como GiveIn, manipular su suministro a 0 primero y luego realizar un intercambio inverso, es decir, BPT se usa como GiveOut. En este momento, dado que el suministro es 0, el algoritmo lo hará. estar cerca de 1: la proporción de 1 es menor que la proporción de canje del grupo lineal para el canje real, lo que hace que la cantidad de tokens BPT de GiveOut se manipule indirectamente.
Podemos encontrar que la vulnerabilidad uno aumenta el tipo de cambio para el intercambio, mientras que la vulnerabilidad dos reduce el tipo de cambio a la inversa durante el intercambio inverso. El atacante aprovechó el doble beneficio para obtener ganancias y marcharse.
Enlaces de referencia:
[1]https://twitter.com/Balancer/status/1694014645378724280
[2]https://forum.balancer.fi/t/vulnerability-found-in-some-pools/5102?u=endymionjkb
[3] https://etherscan.io/tx/0x7020e0ccafff2c86db3df5a2af0cccb4e931fe948f69bf20ea517b0cc99c1f15
[4]https://twitter.com/Balancer/status/1695777503699435751
[5] https://medium.com/balancer-protocol/rate-manipulation-in-balancer-boosted-pools-technical-postmortem-53db4b642492
[6]https://docs.balancer.fi/concepts/overview/basics.html
[7]https://docs.balancer.fi/reference/swaps/flash-swaps.html#flash-swaps
[8]https://docs.balancer.fi/concepts/pools/linear.html
[9]https://docs.balancer.fi/concepts/pools/boosted.html
