Compilación: Cadena de bloques en lenguaje sencillo
El 3 de noviembre de 2025, las piscinas de estabilidad compuestas (Composable Stable Pools) de Balancer V2 y varios proyectos bifurcados en múltiples cadenas sufrieron un ataque coordinado, lo que resultó en pérdidas totales de más de 125 millones de dólares. BlockSec emitió una alerta de inmediato y luego publicó un análisis preliminar.
Este es un ataque altamente complejo. Nuestra investigación muestra que la causa raíz es la pérdida de precisión en el cálculo de invariante, lo que llevó a la manipulación de precios y distorsionó el cálculo del precio de BPT (Token del Pool de Balancer). Esta manipulación de invariante permitió a los atacantes obtener beneficios a través de un intercambio masivo único de un pool estable específico. Aunque algunos investigadores han proporcionado análisis perspicaces, ciertas interpretaciones son engañosas, y la causa raíz y el proceso del ataque aún no se han esclarecido completamente. Este blog tiene como objetivo realizar un análisis técnico completo y preciso de este evento.
Puntos clave (TL;DR)
Causa raíz: inconsistencia en el redondeo y pérdida de precisión
La operación de ampliación (upscaling) utiliza redondeo unidireccional (redondeo hacia abajo), mientras que la operación de reducción (downscaling) utiliza redondeo bidireccional (redondeo hacia arriba y hacia abajo).
Esta inconsistencia causa una pérdida de precisión, y cuando se aprovecha a través de un camino de intercambio cuidadosamente diseñado, viola el principio estándar de que “el redondeo siempre debe favorecer al protocolo”.
Ejecución de ataque
El atacante diseñó cuidadosamente los parámetros, incluyendo el número de iteraciones y los valores de entrada, para maximizar el impacto de la pérdida de precisión.
Los atacantes utilizan un método de dos fases para evadir la detección: primero realizan un ataque central en una transacción individual, sin obtener ganancias de inmediato, y luego en otra transacción extraen activos para lograr beneficios.
Impacto operativo y ampliación
Debido a ciertas restricciones, el protocolo no puede ser pausado. Esta incapacidad para detener las operaciones ha agravado el impacto del ataque y ha llevado a una gran cantidad de ataques posteriores o imitativos.
En la siguiente sección, primero proporcionaremos información clave sobre Balancer V2, y luego analizaremos en profundidad los problemas encontrados y los ataques relacionados.
0x1 Antecedentes
1. Grupo de piscinas estables de Balancer V2
El componente afectado en este ataque es el grupo de estabilidad compuesto del protocolo Balancer V2. Estos grupos están diseñados para activos que se espera mantengan un anclaje cercano a 1:1 (o que se negocien a una tasa conocida) y permiten intercambios grandes con un impacto mínimo en el precio, lo que mejora significativamente la eficiencia del capital entre activos similares o relacionados. Cada grupo tiene su propio Token de Grupo de Balancer (BPT), que representa la participación de los proveedores de liquidez en el grupo, así como los activos subyacentes correspondientes.
Este fondo utiliza Stable Math (modelo StableSwap basado en Curve), donde la invariante D representa el valor total virtual del fondo.
El precio de BPT se puede aproximar a:
Como se puede ver en la fórmula anterior, si D se puede reducir en los libros (incluso sin ninguna pérdida real de fondos), el precio de BPT parecerá más barato.
2. batchSwap() y onSwap()
Balancer V2 ofrece la función batchSwap(), que admite intercambios de múltiples saltos (multi-hop swaps) dentro del Vault. Según los parámetros pasados a esta función, hay dos tipos de intercambio:
GIVEN_IN (“entrada dada”): El llamador especifica la cantidad exacta de Token de entrada, el bloque calcula la cantidad correspondiente de salida.
GIVEN_OUT (“salida dada”): El llamador especifica la cantidad de salida esperada, el bloque calcula la cantidad de entrada necesaria.
Normalmente, batchSwap() consiste en intercambios entre múltiples Token ejecutados a través de la función onSwap(). A continuación se describe la ruta de ejecución cuando una SwapRequest se asigna como tipo de intercambio GIVEN_OUT (tenga en cuenta que ComposableStablePool hereda de BaseGeneralPool):
! [imagen] ()
A continuación se muestra el cálculo de amount_in en el tipo de intercambio GIVEN_OUT, que involucra la invariante D.
// enGivenOut token x por y - ecuación polinómica para resolver
// ax = cantidad en para calcular
// bx = saldo de token en
// x = bx + ax (finalBalanceIn)
// D = invariante
// A = coeficiente de amplificación
// n = número de tokens
// S = suma de saldos finales pero x
// P = producto de saldos finales pero x
D D^(n+1)
x^2 + ( S - ---------- - D) * x - ------------- = 0
(A * n^n) A * n^2n * P
3. Escalado y redondeo
Para normalizar el cálculo entre diferentes saldos de Token, Balancer realiza las siguientes dos operaciones:
Escalado (Upscaling): Antes de realizar cálculos, amplía el saldo y la cantidad a una precisión interna unificada.
! [imagen] ()
Reducción (Downscaling): Convertir el resultado de nuevo a su precisión nativa y aplicar redondeo dirigido (por ejemplo, el monto de entrada generalmente se redondea hacia arriba para asegurar que el fondo no se cobre insuficientemente, mientras que el monto de salida a menudo se redondea hacia abajo).
! [imagen] ()
Evidentemente, ampliar y reducir son operaciones emparejadas en teoría—que son multiplicación y división, respectivamente. Sin embargo, hay inconsistencias en la implementación de estas dos operaciones. Específicamente, la operación de reducción tiene dos variantes o direcciones: divUp y divDown. En comparación, la operación de ampliación solo tiene una dirección, que es mulDown.
La razón de esta inconsistencia aún no está clara. Según los comentarios en la función _upscale(), los desarrolladores creen que el impacto del redondeo unidireccional es mínimo.
// La ampliación (Upscale) no siempre redondeará en la misma dirección: por ejemplo, en un intercambio,
// El saldo del Token de entrada debe redondearse hacia arriba, mientras que el saldo del Token de salida debe redondearse hacia abajo. Este es el único lugar donde redondeamos todas las cantidades en la misma dirección,
// Se espera que el impacto de este redondeo sea mínimo.
// (y a menos que _scalingFactor() sea sobrescrito, no hay error de redondeo).
0x2 Análisis de vulnerabilidades
El problema fundamental radica en la operación de redondeo hacia abajo que se realiza al ejecutar la operación de escalado (upscaling) en la función BaseGeneralPool._swapGivenOut(). En particular, _swapGivenOut() redondea incorrectamente swapRequest.amount hacia abajo a través de la función _upscale(). Este valor redondeado se utiliza posteriormente como amountOut al calcular amountIn a través de _onSwapGivenOut(). Este comportamiento contradice la práctica estándar de que “el redondeo debe aplicarse de manera que favorezca al protocolo”.
! [imagen] ()
Por lo tanto, para el grupo dado (wstETH/rETH/cbETH), el amountIn calculado subestima la entrada real necesaria. Esto permite a los usuarios intercambiar una menor cantidad de un activo subyacente (por ejemplo, wstETH) por otro activo (por ejemplo, cbETH), lo que causa una reducción en el invariante D debido a la disminución de la liquidez efectiva. Por lo tanto, el precio correspondiente del BPT (wstETH/rETH/cbETH) se vuelve artificialmente bajo (deflacionado), ya que $\text{BPT price} = \frac{D}{\text{totalSupply}}$.
Análisis de ataque 0x3
El atacante ejecutó
Ataque de dos fases, que puede ser para minimizar el riesgo de ser detectado:
En la primera fase, el núcleo se utiliza para ejecutar en una sola transacción, sin obtener ganancias inmediatas.
En la segunda fase, el atacante logra ganancias retirando activos en otra transacción.
La primera fase se puede dividir aún más en dos etapas: cálculo de parámetros e intercambio por lotes. A continuación, utilizamos un ejemplo de una transacción de ataque (TX) en Arbitrum para ilustrar estas etapas.
etapa de cálculo de parámetros
En esta etapa, el atacante combinará el cálculo fuera de la cadena con la simulación en la cadena, ajustando con precisión los parámetros de cada salto en la siguiente etapa (intercambio por lotes) según el estado actual del fondo de estabilidad combinado (incluyendo el factor de escala, el coeficiente de ampliación, la tasa de BPT, las tarifas de intercambio y otros parámetros). Curiosamente, el atacante también desplegó un contrato auxiliar para ayudar con estos cálculos, lo que podría ser para reducir el riesgo de ser “adelantado”.
Primero, el atacante recopila la información básica del grupo objetivo, incluyendo el factor de escalado de cada Token, los parámetros de amplificación, la tasa de BPT y el porcentaje de tarifas de intercambio. Luego, calculan un valor clave trickAmt, que es la cantidad de Token objetivo manipulada utilizada para provocar la pérdida de precisión.
Sea el factor de escala del Token objetivo (scaling factor) sF, calculado de la siguiente manera:
Para determinar los parámetros utilizados en el siguiente paso (intercambio por lotes) de la fase 2, el atacante utiliza el siguiente calldata para realizar una llamada simulada posterior a la función 0x524c9e20 del contrato auxiliar:
uint256[] balances; // Saldo de tokens del pool (excluyendo BPT)
uint256[] scalingFactors; // Factor de escala para cada token del grupo
uint tokenIn; // Índice del Token de entrada simulado en este salto
uint tokenOut; // Este salto simula el índice del token de salida
uint256 amountOut; // Cantidad de Token de salida esperada
uint256 amp; // Parámetro de ampliación del bloque
uint256 fee; // porcentaje de tarifa de intercambio de bloque
Los datos devueltos son:
uint256[] balances; // Saldo de tokens del pool después del intercambio (excluyendo BPT)
En concreto, el saldo inicial y el número de iteraciones se calculan fuera de la cadena y se pasan como parámetros al contrato del atacante (los informes son 100,000,000,000 y 25, respectivamente). En cada iteración se realizan tres intercambios:
Intercambio 1: Aumentar la cantidad del Token objetivo a trickAmt + 1, suponiendo que la dirección del intercambio sea 0 → 1.
Intercambio 2: Continuar usando trickAmt para cambiar por el Token objetivo, esto activará el redondeo hacia abajo en la llamada _upscale().
320,000.
Tenga en cuenta que, debido a que el cálculo de StableMath utiliza el método de Newton-Raphson, este paso puede fallar ocasionalmente. Para mitigar esta situación, el atacante implementó dos intentos de reintento, cada uno utilizando el 9/10 del valor original como valor de respaldo.
El contrato auxiliar del atacante se deriva de la biblioteca StableMath de Balancer V2, lo que se puede demostrar por el hecho de que incluye un mensaje de error personalizado al estilo “BAL”.
$d$ Etapa de intercambio por lotes
Luego, la operación batchSwap###( se puede descomponer en tres pasos:
Paso 1: El atacante intercambia BPT )wstETH/rETH/cbETH( por activos subyacentes, ajustando con precisión el saldo de un Token (cbETH) al borde del límite de redondeo (amount = 9). Esto crea condiciones para la pérdida de precisión en el siguiente paso.
Paso 2: Luego, el atacante utiliza una cantidad cuidadosamente diseñada (= 8) para intercambiar entre un activo subyacente diferente (wstETH) y cbETH. Debido a que al escalar la cantidad de tokens se redondea hacia abajo, el Δx calculado se vuelve ligeramente menor (de 8.918 a 8), lo que lleva a que Δy sea subestimado, haciendo que la invariante (D, del modelo StableSwap de Curve) se reduzca. Debido a que $\text{precio de BPT} = \frac{D}{\text{suministroTotal}}$, el precio de BPT es artificialmente reducido.
! [imagen] )(
3. Paso 3: El atacante convierte de nuevo los activos subyacentes a BPT, restaurando el equilibrio, al mismo tiempo que obtiene ganancias de la reducción del precio de BPT.
0x4: Ataques y pérdidas
Hemos resumido estos ataques y sus respectivas pérdidas en la tabla a continuación, con una pérdida total que supera los 125 millones de dólares.
! [imagen] )(
0x5 Conclusión
Este evento involucró una serie de transacciones de ataque dirigidas al protocolo Balancer V2 y sus proyectos bifurcados, lo que resultó en pérdidas financieras significativas. Después del ataque inicial, se observaron numerosas transacciones de seguimiento y imitación en múltiples cadenas. Este incidente destaca varias lecciones clave sobre el diseño y la seguridad de los protocolos DeFi:
Comportamiento de redondeo y pérdida de precisión: La redondeo unidireccional (redondeo hacia abajo) utilizado en la operación de ampliación (upscaling) es diferente del redondeo bidireccional (redondeo hacia arriba y hacia abajo) utilizado en la operación de reducción (downscaling). Para prevenir vulnerabilidades similares, el protocolo debe utilizar una aritmética de mayor precisión e implementar verificaciones de validación robustas. Se debe adherir al principio estándar de que “el redondeo siempre debe favorecer al protocolo”.
Evolución de la explotación de vulnerabilidades: Los atacantes llevaron a cabo una compleja explotación de vulnerabilidades en dos fases, con el objetivo de eludir la detección. En la primera fase, los atacantes ejecutaron la explotación principal en una única transacción, sin obtener beneficios inmediatos. En la segunda fase, los atacantes lograron obtener ganancias extrayendo activos en otra transacción. Esto vuelve a resaltar la continua “carrera armamentista” entre los investigadores de seguridad y los atacantes.
Conciencia operativa y respuesta a amenazas: Este incidente subraya la importancia de las alertas oportunas sobre el estado de inicialización y operación, así como de los mecanismos proactivos de detección y prevención de amenazas para mitigar las posibles pérdidas causadas por ataques continuos o simulados.
Mientras mantienen la continuidad operativa y del negocio, los participantes de la industria pueden utilizar BlockSec Phalcon como la última línea de defensa para proteger sus activos. El equipo de expertos de BlockSec está siempre listo para realizar una evaluación de seguridad completa para su proyecto.
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
La magia negra de la manipulación de precios: Análisis de la vulnerabilidad de cálculo de invariante de Balancer V2
Compilación: Cadena de bloques en lenguaje sencillo
El 3 de noviembre de 2025, las piscinas de estabilidad compuestas (Composable Stable Pools) de Balancer V2 y varios proyectos bifurcados en múltiples cadenas sufrieron un ataque coordinado, lo que resultó en pérdidas totales de más de 125 millones de dólares. BlockSec emitió una alerta de inmediato y luego publicó un análisis preliminar.
Este es un ataque altamente complejo. Nuestra investigación muestra que la causa raíz es la pérdida de precisión en el cálculo de invariante, lo que llevó a la manipulación de precios y distorsionó el cálculo del precio de BPT (Token del Pool de Balancer). Esta manipulación de invariante permitió a los atacantes obtener beneficios a través de un intercambio masivo único de un pool estable específico. Aunque algunos investigadores han proporcionado análisis perspicaces, ciertas interpretaciones son engañosas, y la causa raíz y el proceso del ataque aún no se han esclarecido completamente. Este blog tiene como objetivo realizar un análisis técnico completo y preciso de este evento.
Puntos clave (TL;DR)
En la siguiente sección, primero proporcionaremos información clave sobre Balancer V2, y luego analizaremos en profundidad los problemas encontrados y los ataques relacionados.
0x1 Antecedentes
1. Grupo de piscinas estables de Balancer V2
El componente afectado en este ataque es el grupo de estabilidad compuesto del protocolo Balancer V2. Estos grupos están diseñados para activos que se espera mantengan un anclaje cercano a 1:1 (o que se negocien a una tasa conocida) y permiten intercambios grandes con un impacto mínimo en el precio, lo que mejora significativamente la eficiencia del capital entre activos similares o relacionados. Cada grupo tiene su propio Token de Grupo de Balancer (BPT), que representa la participación de los proveedores de liquidez en el grupo, así como los activos subyacentes correspondientes.
Este fondo utiliza Stable Math (modelo StableSwap basado en Curve), donde la invariante D representa el valor total virtual del fondo.
El precio de BPT se puede aproximar a:
2. batchSwap() y onSwap()
Balancer V2 ofrece la función batchSwap(), que admite intercambios de múltiples saltos (multi-hop swaps) dentro del Vault. Según los parámetros pasados a esta función, hay dos tipos de intercambio:
Normalmente, batchSwap() consiste en intercambios entre múltiples Token ejecutados a través de la función onSwap(). A continuación se describe la ruta de ejecución cuando una SwapRequest se asigna como tipo de intercambio GIVEN_OUT (tenga en cuenta que ComposableStablePool hereda de BaseGeneralPool):
! [imagen] ()
A continuación se muestra el cálculo de amount_in en el tipo de intercambio GIVEN_OUT, que involucra la invariante D.
// enGivenOut token x por y - ecuación polinómica para resolver // ax = cantidad en para calcular // bx = saldo de token en // x = bx + ax (finalBalanceIn)
// D = invariante // A = coeficiente de amplificación // n = número de tokens // S = suma de saldos finales pero x // P = producto de saldos finales pero x
x^2 + ( S - ---------- - D) * x - ------------- = 0
(A * n^n) A * n^2n * P
3. Escalado y redondeo
Para normalizar el cálculo entre diferentes saldos de Token, Balancer realiza las siguientes dos operaciones:
! [imagen] ()
! [imagen] ()
Evidentemente, ampliar y reducir son operaciones emparejadas en teoría—que son multiplicación y división, respectivamente. Sin embargo, hay inconsistencias en la implementación de estas dos operaciones. Específicamente, la operación de reducción tiene dos variantes o direcciones: divUp y divDown. En comparación, la operación de ampliación solo tiene una dirección, que es mulDown.
La razón de esta inconsistencia aún no está clara. Según los comentarios en la función _upscale(), los desarrolladores creen que el impacto del redondeo unidireccional es mínimo.
0x2 Análisis de vulnerabilidades
El problema fundamental radica en la operación de redondeo hacia abajo que se realiza al ejecutar la operación de escalado (upscaling) en la función BaseGeneralPool._swapGivenOut(). En particular, _swapGivenOut() redondea incorrectamente swapRequest.amount hacia abajo a través de la función _upscale(). Este valor redondeado se utiliza posteriormente como amountOut al calcular amountIn a través de _onSwapGivenOut(). Este comportamiento contradice la práctica estándar de que “el redondeo debe aplicarse de manera que favorezca al protocolo”.
! [imagen] ()
Por lo tanto, para el grupo dado (wstETH/rETH/cbETH), el amountIn calculado subestima la entrada real necesaria. Esto permite a los usuarios intercambiar una menor cantidad de un activo subyacente (por ejemplo, wstETH) por otro activo (por ejemplo, cbETH), lo que causa una reducción en el invariante D debido a la disminución de la liquidez efectiva. Por lo tanto, el precio correspondiente del BPT (wstETH/rETH/cbETH) se vuelve artificialmente bajo (deflacionado), ya que $\text{BPT price} = \frac{D}{\text{totalSupply}}$.
Análisis de ataque 0x3
El atacante ejecutó
Ataque de dos fases, que puede ser para minimizar el riesgo de ser detectado:
La primera fase se puede dividir aún más en dos etapas: cálculo de parámetros e intercambio por lotes. A continuación, utilizamos un ejemplo de una transacción de ataque (TX) en Arbitrum para ilustrar estas etapas.
etapa de cálculo de parámetros
En esta etapa, el atacante combinará el cálculo fuera de la cadena con la simulación en la cadena, ajustando con precisión los parámetros de cada salto en la siguiente etapa (intercambio por lotes) según el estado actual del fondo de estabilidad combinado (incluyendo el factor de escala, el coeficiente de ampliación, la tasa de BPT, las tarifas de intercambio y otros parámetros). Curiosamente, el atacante también desplegó un contrato auxiliar para ayudar con estos cálculos, lo que podría ser para reducir el riesgo de ser “adelantado”.
Primero, el atacante recopila la información básica del grupo objetivo, incluyendo el factor de escalado de cada Token, los parámetros de amplificación, la tasa de BPT y el porcentaje de tarifas de intercambio. Luego, calculan un valor clave trickAmt, que es la cantidad de Token objetivo manipulada utilizada para provocar la pérdida de precisión.
Sea el factor de escala del Token objetivo (scaling factor) sF, calculado de la siguiente manera:
uint256[] balances; // Saldo de tokens del pool (excluyendo BPT) uint256[] scalingFactors; // Factor de escala para cada token del grupo uint tokenIn; // Índice del Token de entrada simulado en este salto uint tokenOut; // Este salto simula el índice del token de salida uint256 amountOut; // Cantidad de Token de salida esperada uint256 amp; // Parámetro de ampliación del bloque uint256 fee; // porcentaje de tarifa de intercambio de bloque
Los datos devueltos son:
uint256[] balances; // Saldo de tokens del pool después del intercambio (excluyendo BPT)
En concreto, el saldo inicial y el número de iteraciones se calculan fuera de la cadena y se pasan como parámetros al contrato del atacante (los informes son 100,000,000,000 y 25, respectivamente). En cada iteración se realizan tres intercambios:
Tenga en cuenta que, debido a que el cálculo de StableMath utiliza el método de Newton-Raphson, este paso puede fallar ocasionalmente. Para mitigar esta situación, el atacante implementó dos intentos de reintento, cada uno utilizando el 9/10 del valor original como valor de respaldo.
El contrato auxiliar del atacante se deriva de la biblioteca StableMath de Balancer V2, lo que se puede demostrar por el hecho de que incluye un mensaje de error personalizado al estilo “BAL”.
$d$ Etapa de intercambio por lotes
Luego, la operación batchSwap###( se puede descomponer en tres pasos:
! [imagen] )( 3. Paso 3: El atacante convierte de nuevo los activos subyacentes a BPT, restaurando el equilibrio, al mismo tiempo que obtiene ganancias de la reducción del precio de BPT.
0x4: Ataques y pérdidas
Hemos resumido estos ataques y sus respectivas pérdidas en la tabla a continuación, con una pérdida total que supera los 125 millones de dólares.
! [imagen] )(
0x5 Conclusión
Este evento involucró una serie de transacciones de ataque dirigidas al protocolo Balancer V2 y sus proyectos bifurcados, lo que resultó en pérdidas financieras significativas. Después del ataque inicial, se observaron numerosas transacciones de seguimiento y imitación en múltiples cadenas. Este incidente destaca varias lecciones clave sobre el diseño y la seguridad de los protocolos DeFi:
Mientras mantienen la continuidad operativa y del negocio, los participantes de la industria pueden utilizar BlockSec Phalcon como la última línea de defensa para proteger sus activos. El equipo de expertos de BlockSec está siempre listo para realizar una evaluación de seguridad completa para su proyecto.
Enlace del artículo:
Fuente: