3 ноября 2025 года комбинированные стабильные пулы (Composable Stable Pools) Balancer V2 и несколько форков на разных блоках стали жертвами скоординированной атаки, что привело к общим потерям более 125 миллионов долларов. BlockSec сразу же поднял тревогу и затем опубликовал предварительный анализ.
Это высоко сложная атака. Наше расследование показало, что коренной причиной является потеря точности в вычислении инвариантов, что привело к манипуляциям с ценами и искажению расчета цены BPT (Token Балансировочного Пула). Эта манипуляция с инвариантами позволила злоумышленникам извлечь выгоду из однократного пакетного обмена из определенного стабильного пула. Хотя некоторые исследователи предоставили проницательный анализ, существуют вводящие в заблуждение интерпретации, и коренные причины и процесс атаки еще не полностью раскрыты. Этот блог призван предоставить всесторонний и точный технический анализ данного события.
Ключевые моменты (TL;DR)
Коренная причина: несоответствие округления и потеря точности
Операция увеличения (upscaling) использует однонаправленное округление (округление вниз), в то время как операция уменьшения (downscaling) использует двунаправленное округление (округление вверх и вниз).
Эта несогласованность приводит к потере точности, когда она используется через тщательно спроектированные обменные пути, нарушая стандартный принцип “округление всегда должно быть в пользу протокола”.
Атакующее выполнение
Атакующий тщательно спроектировал параметры, включая количество итераций и входные значения, чтобы максимизировать влияние потери точности.
Нападающий использует двухступенчатый метод для обхода обнаружения: сначала выполняет основную атаку в одной транзакции, не получая немедленной прибыли, а затем в другой транзакции извлекает активы для получения прибыли.
Влияние на операцию и увеличение
Из-за некоторых ограничений протокол не может быть приостановлен. Эта невозможность остановить работу усугубила последствия атаки и привела к множеству последующих или подражающих атак.
В следующей части мы сначала предоставим ключевую информацию о Balancer V2, а затем углубимся в анализ обнаруженных проблем и связанных атак.
0x1 Фон
1. Комбинированный стабильный пул Balancer V2
Затронутым компонентом в данной атаке является комбинированный стабильный пул протокола Balancer V2. Эти пулы специально разработаны для активов, которые ожидается, будут поддерживать близкое к 1:1 привязку (или торговаться по известному курсу), и позволяют проводить крупные обмены с минимальным ценовым воздействием, значительно увеличивая капитальную эффективность между аналогичными или связанными активами. Каждый пул имеет свой собственный токен Balancer (BPT), представляющий долю поставщиков ликвидности в пуле и соответствующие базовые активы.
Этот пул использует Stable Math (модель StableSwap на основе Curve), где инвариант D представляет собой виртуальную общую стоимость пула.
Цена BPT может быть приблизительно равна:
Из приведенной выше формулы видно, что если D можно уменьшить на бумаге (даже без каких-либо фактических потерь средств), цена BPT будет казаться более низкой.
2. batchSwap() и onSwap()
Balancer V2 предоставляет функцию batchSwap(), которая поддерживает многошаговые обмены (multi-hop swaps) внутри Vault. В зависимости от параметров, передаваемых в эту функцию, существуют два типа обменов:
GIVEN_IN(“Дано входное”):Вызывающий указывает точное количество входных токенов, а пул рассчитывает соответствующее количество выходных токенов.
GIVEN_OUT(“Данный вывод”):Вызывающий указывает ожидаемое количество вывода, пул рассчитывает необходимое количество входа.
Обычно, batchSwap() состоит из нескольких обменов между токенами, выполняемых через функцию onSwap(). Ниже описан путь выполнения, когда SwapRequest назначается как тип обмена GIVEN_OUT (обратите внимание, что ComposableStablePool наследуется от BaseGeneralPool):
! [изображение] ()
Ниже показан расчет amount_in для типа обмена GIVEN_OUT, который включает инвариант D.
// в GivenOut токен x для y - полиномиальное уравнение для решения
// ax = сумма для расчета
// bx = баланс токена в
x = bx + ax (finalBalanceIn)
// D = инвариант
// A = коэффициент усиления
// n = количество токенов
// S = сумма конечных балансов, но x
// P = произведение конечных балансов, но x
D D^(n+1)
x^2 + ( S - ---------- - D) * x - ------------- = 0
(A * n^n) A * n^2n * P
3. Масштабирование и округление
Чтобы нормализовать расчеты между разными балансами токенов, Balancer выполняет следующие две операции:
Увеличение (Upscaling): Перед выполнением расчетов увеличьте баланс и сумму до единой внутренней точности.
! [изображение] ()
Снижение (Downscaling): Преобразование результата обратно к его исходной точности и применение направленного округления (например, вводимая сумма обычно округляется вверх, чтобы гарантировать, что пул не будет недостаточно взимать плату, в то время как выводимая сумма часто округляется вниз).
! [изображение] ()
Очевидно, что увеличение и уменьшение в теории являются парными операциями — соответственно, умножением и делением. Однако в реализации этих двух операций существуют несоответствия. В частности, операция уменьшения имеет два варианта или направления: divUp и divDown. В отличие от этого, операция увеличения имеет только одно направление, а именно mulDown.
Причина этой несогласованности пока неясна. Согласно комментариям в функции _upscale(), разработчики считают, что влияние одностороннего округления незначительно.
// Увеличение (Upscale) округление не обязательно всегда будет происходить в одном и том же направлении: например, в одном обмене,
// Баланс входного токена должен округляться вверх, а баланс выходного токена должен округляться вниз. Это единственное место, где мы округляем все суммы в одном направлении,
// Поскольку предполагается, что влияние такого округления минимально
// (и если только _scalingFactor() не будет переопределен, то нет ошибок округления).
0x2 Анализ уязвимостей
Основная проблема заключается в операции округления вниз, выполняемой во время масштабирования (upscaling) в функции BaseGeneralPool._swapGivenOut(). В частности, _swapGivenOut() ошибочно округляет значение swapRequest.amount вниз с помощью функции _upscale(). Это округленное значение затем используется в качестве amountOut при вычислении amountIn через _onSwapGivenOut(). Такое поведение противоречит стандартной практике “округление должно применяться в пользу протокола”.
! [изображение] ()
Таким образом, для данного пула (wstETH/rETH/cbETH) рассчитанное значение amountIn недооценивало фактически необходимый ввод. Это позволяет пользователям обменивать меньшую сумму одного базового актива (например, wstETH) на другой актив (например, cbETH), что приводит к снижению инварианта D из-за уменьшения эффективной ликвидности. Таким образом, соответствующая цена BPT (wstETH/rETH/cbETH) становится искусственно заниженной (deflated), потому что $\text{BPT price} = \frac{D}{\text{totalSupply}}$.
0x3 Анализ атак
Атакующий выполнил
Двухступенчатая атака, возможно, предназначена для минимизации риска обнаружения:
На первом этапе ядро используется для выполнения в одной транзакции, без немедленной прибыли.
На втором этапе злоумышленник реализует прибыль, извлекая активы в другой транзакции.
Первый этап можно дополнительно разделить на два этапа: расчет параметров и массовый обмен. Далее мы используем пример атаки транзакции (TX) на Arbitrum (для иллюстрации этих этапов.
Этап расчета параметров
На этом этапе злоумышленник сочетает оффлайн вычисления с онлайн симуляциями, точно настраивая параметры каждого прыжка на следующем этапе (массовый обмен) в зависимости от текущего состояния комбинированного стабильного пула (включая коэффициент масштабирования, коэффициент увеличения, курс BPT, комиссии за обмен и другие параметры). Интересно, что злоумышленник также развернул вспомогательный контракт для помощи в этих вычислениях, что может быть направлено на снижение риска “фронт-рана”.
Сначала злоумышленники собирают основную информацию о целевом пуле, включая масштабный коэффициент для каждого токена, параметры увеличения, курс BPT и процент обменной комиссии. Затем они вычисляют ключевое значение trickAmt, которое представляет собой манипулируемое количество целевого токена, используемое для вызова потери точности.
Обозначим коэффициент масштабирования целевого токена (scaling factor) как sF, рассчитываем следующим образом:
Чтобы определить параметры, используемые на следующем этапе (массовый обмен) шаг 2, злоумышленник использует следующий calldata для последующего симуляционного вызова функции 0x524c9e20 вспомогательного контракта:
uint256[] balances; // Баланс токенов пула (не включая BPT)
uint256[] scalingFactors; // Масштабный фактор для каждого токена пула
uint tokenIn; // Индекс входного токена для этого прыжка
uint tokenOut; // Индекс выходного токена для этого прыжка
uint256 amountOut; // Ожидаемое количество выходных токенов
uint256 amp; // Параметр увеличения пула
uint256 fee; // Процент обменной комиссии пула
Возвращаемые данные:
uint256[] балансы; // Остаток токенов пула после обмена (не включая BPT)
В частности, начальный баланс и количество итерационных циклов рассчитываются вне цепи и передаются в контракт злоумышленника (соответственно 100,000,000,000 и 25). На каждой итерации выполняются три обмена:
Обмен 1: Увеличьте количество целевого токена до trickAmt + 1, предположим, что направление обмена 0 → 1.
Обмен 2: Продолжайте использовать trickAmt для обмена на целевой токен, это вызовет округление вниз в вызове _upscale().
320,000.
Обратите внимание, что из-за использования метода Ньютона-Рафсона (Newton–Raphson) в расчетах StableMath этот шаг может иногда завершаться неудачей. Чтобы смягчить эту ситуацию, злоумышленник реализовал две попытки повторного запуска, каждая из которых использует 9/10 от исходного значения в качестве резервного.
Вспомогательный контракт атакующего происходит из библиотеки StableMath Balancer V2, что можно доказать его включением кастомного сообщения об ошибке в стиле “BAL”.
$d$ Этап пакетного обмена
Затем операция batchSwap###( может быть разбита на три шага:
Шаг 1: Атакующий обменивает BPT )wstETH/rETH/cbETH( на базовые активы, чтобы точно настроить баланс одного токена (cbETH) на границе округления (amount = 9). Это создает условия для потери точности на следующем этапе.
Шаг 2: Затем злоумышленник использует тщательно рассчитанное количество (= 8) для обмена между другим базовым активом (wstETH) и cbETH. Поскольку при масштабировании количества токенов происходит округление вниз, вычисленное Δx становится немного меньше (с 8.918 до 8), что приводит к недооценке Δy и, соответственно, уменьшению инварианта (D, из модели StableSwap от Curve). Поскольку $\text{BPT price} = \frac{D}{\text{totalSupply}}$, цена BPT искусственно занижается.
! [изображение] )(
3. Шаг 3: Атакующий обратно обменивает базовые активы на BPT, восстанавливая баланс, одновременно получая прибыль от заниженной цены BPT.
0x4: Атака и потеря
Мы обобщили эти атаки и соответствующие потери в таблице ниже, общий ущерб превышает 125 миллионов долларов.
! [изображение] )(
0x5 Заключение
Это событие связано с серией атакующих сделок против протокола Balancer V2 и его форков, что привело к значительным финансовым потерям. После первоначальной атаки на нескольких блокчейнах было зафиксировано множество последующих и имитирующих сделок. Этот инцидент подчеркивает несколько ключевых уроков по дизайну и безопасности протоколов DeFi:
Округление и потеря точности: Одностороннее округление (округление вниз), используемое в операции увеличения (upscaling), отличается от двустороннего округления (округление вверх и вниз), применяемого в операции уменьшения (downscaling). Чтобы предотвратить подобные уязвимости, протокол должен использовать арифметику с более высокой точностью и внедрять надежные проверки валидации. Необходимо придерживаться принципа “округление всегда должно быть в пользу протокола”.
Эволюция эксплуатации уязвимостей: Злоумышленник выполнил сложную двухфазную эксплуатацию уязвимости, направленную на избегание обнаружения. На первом этапе злоумышленник выполнил основную эксплуатацию в одной транзакции, не получая немедленной выгоды. На втором этапе злоумышленник реализовал прибыль в другой транзакции, извлекая активы. Это вновь подчеркивает продолжающуюся “гонку вооружений” между исследователями безопасности и злоумышленниками.
Операционная осведомленность и реагирование на угрозы: Этот инцидент подчеркивает важность своевременных оповещений о начальном и операционном состоянии, а также активных механизмов обнаружения и предотвращения угроз для снижения потенциальных потерь от продолжающихся или имитационных атак.
При обеспечении операционной и бизнес-непрерывности участники отрасли могут использовать BlockSec Phalcon в качестве последней линии защиты своих активов. Экспертная команда BlockSec всегда готова провести комплексную оценку безопасности вашего проекта.
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
Черная магия манипуляции ценами: Анализ уязвимости постоянных значений Balancer V2
Компиляция: Простая Блокчейн
!
3 ноября 2025 года комбинированные стабильные пулы (Composable Stable Pools) Balancer V2 и несколько форков на разных блоках стали жертвами скоординированной атаки, что привело к общим потерям более 125 миллионов долларов. BlockSec сразу же поднял тревогу и затем опубликовал предварительный анализ.
Это высоко сложная атака. Наше расследование показало, что коренной причиной является потеря точности в вычислении инвариантов, что привело к манипуляциям с ценами и искажению расчета цены BPT (Token Балансировочного Пула). Эта манипуляция с инвариантами позволила злоумышленникам извлечь выгоду из однократного пакетного обмена из определенного стабильного пула. Хотя некоторые исследователи предоставили проницательный анализ, существуют вводящие в заблуждение интерпретации, и коренные причины и процесс атаки еще не полностью раскрыты. Этот блог призван предоставить всесторонний и точный технический анализ данного события.
Ключевые моменты (TL;DR)
В следующей части мы сначала предоставим ключевую информацию о Balancer V2, а затем углубимся в анализ обнаруженных проблем и связанных атак.
0x1 Фон
1. Комбинированный стабильный пул Balancer V2
Затронутым компонентом в данной атаке является комбинированный стабильный пул протокола Balancer V2. Эти пулы специально разработаны для активов, которые ожидается, будут поддерживать близкое к 1:1 привязку (или торговаться по известному курсу), и позволяют проводить крупные обмены с минимальным ценовым воздействием, значительно увеличивая капитальную эффективность между аналогичными или связанными активами. Каждый пул имеет свой собственный токен Balancer (BPT), представляющий долю поставщиков ликвидности в пуле и соответствующие базовые активы.
Этот пул использует Stable Math (модель StableSwap на основе Curve), где инвариант D представляет собой виртуальную общую стоимость пула.
Цена BPT может быть приблизительно равна:
2. batchSwap() и onSwap()
Balancer V2 предоставляет функцию batchSwap(), которая поддерживает многошаговые обмены (multi-hop swaps) внутри Vault. В зависимости от параметров, передаваемых в эту функцию, существуют два типа обменов:
Обычно, batchSwap() состоит из нескольких обменов между токенами, выполняемых через функцию onSwap(). Ниже описан путь выполнения, когда SwapRequest назначается как тип обмена GIVEN_OUT (обратите внимание, что ComposableStablePool наследуется от BaseGeneralPool):
! [изображение] ()
Ниже показан расчет amount_in для типа обмена GIVEN_OUT, который включает инвариант D.
// в GivenOut токен x для y - полиномиальное уравнение для решения // ax = сумма для расчета // bx = баланс токена в x = bx + ax (finalBalanceIn)
// D = инвариант // A = коэффициент усиления // n = количество токенов // S = сумма конечных балансов, но x // P = произведение конечных балансов, но x
x^2 + ( S - ---------- - D) * x - ------------- = 0
(A * n^n) A * n^2n * P
3. Масштабирование и округление
Чтобы нормализовать расчеты между разными балансами токенов, Balancer выполняет следующие две операции:
! [изображение] ()
! [изображение] ()
Очевидно, что увеличение и уменьшение в теории являются парными операциями — соответственно, умножением и делением. Однако в реализации этих двух операций существуют несоответствия. В частности, операция уменьшения имеет два варианта или направления: divUp и divDown. В отличие от этого, операция увеличения имеет только одно направление, а именно mulDown.
Причина этой несогласованности пока неясна. Согласно комментариям в функции _upscale(), разработчики считают, что влияние одностороннего округления незначительно.
0x2 Анализ уязвимостей
Основная проблема заключается в операции округления вниз, выполняемой во время масштабирования (upscaling) в функции BaseGeneralPool._swapGivenOut(). В частности, _swapGivenOut() ошибочно округляет значение swapRequest.amount вниз с помощью функции _upscale(). Это округленное значение затем используется в качестве amountOut при вычислении amountIn через _onSwapGivenOut(). Такое поведение противоречит стандартной практике “округление должно применяться в пользу протокола”.
! [изображение] ()
Таким образом, для данного пула (wstETH/rETH/cbETH) рассчитанное значение amountIn недооценивало фактически необходимый ввод. Это позволяет пользователям обменивать меньшую сумму одного базового актива (например, wstETH) на другой актив (например, cbETH), что приводит к снижению инварианта D из-за уменьшения эффективной ликвидности. Таким образом, соответствующая цена BPT (wstETH/rETH/cbETH) становится искусственно заниженной (deflated), потому что $\text{BPT price} = \frac{D}{\text{totalSupply}}$.
0x3 Анализ атак
Атакующий выполнил
Двухступенчатая атака, возможно, предназначена для минимизации риска обнаружения:
Первый этап можно дополнительно разделить на два этапа: расчет параметров и массовый обмен. Далее мы используем пример атаки транзакции (TX) на Arbitrum (для иллюстрации этих этапов.
Этап расчета параметров
На этом этапе злоумышленник сочетает оффлайн вычисления с онлайн симуляциями, точно настраивая параметры каждого прыжка на следующем этапе (массовый обмен) в зависимости от текущего состояния комбинированного стабильного пула (включая коэффициент масштабирования, коэффициент увеличения, курс BPT, комиссии за обмен и другие параметры). Интересно, что злоумышленник также развернул вспомогательный контракт для помощи в этих вычислениях, что может быть направлено на снижение риска “фронт-рана”.
Сначала злоумышленники собирают основную информацию о целевом пуле, включая масштабный коэффициент для каждого токена, параметры увеличения, курс BPT и процент обменной комиссии. Затем они вычисляют ключевое значение trickAmt, которое представляет собой манипулируемое количество целевого токена, используемое для вызова потери точности.
Обозначим коэффициент масштабирования целевого токена (scaling factor) как sF, рассчитываем следующим образом:
uint256[] balances; // Баланс токенов пула (не включая BPT) uint256[] scalingFactors; // Масштабный фактор для каждого токена пула uint tokenIn; // Индекс входного токена для этого прыжка uint tokenOut; // Индекс выходного токена для этого прыжка uint256 amountOut; // Ожидаемое количество выходных токенов uint256 amp; // Параметр увеличения пула uint256 fee; // Процент обменной комиссии пула
Возвращаемые данные:
uint256[] балансы; // Остаток токенов пула после обмена (не включая BPT)
В частности, начальный баланс и количество итерационных циклов рассчитываются вне цепи и передаются в контракт злоумышленника (соответственно 100,000,000,000 и 25). На каждой итерации выполняются три обмена:
Обратите внимание, что из-за использования метода Ньютона-Рафсона (Newton–Raphson) в расчетах StableMath этот шаг может иногда завершаться неудачей. Чтобы смягчить эту ситуацию, злоумышленник реализовал две попытки повторного запуска, каждая из которых использует 9/10 от исходного значения в качестве резервного.
Вспомогательный контракт атакующего происходит из библиотеки StableMath Balancer V2, что можно доказать его включением кастомного сообщения об ошибке в стиле “BAL”.
$d$ Этап пакетного обмена
Затем операция batchSwap###( может быть разбита на три шага:
! [изображение] )( 3. Шаг 3: Атакующий обратно обменивает базовые активы на BPT, восстанавливая баланс, одновременно получая прибыль от заниженной цены BPT.
0x4: Атака и потеря
Мы обобщили эти атаки и соответствующие потери в таблице ниже, общий ущерб превышает 125 миллионов долларов.
! [изображение] )(
0x5 Заключение
Это событие связано с серией атакующих сделок против протокола Balancer V2 и его форков, что привело к значительным финансовым потерям. После первоначальной атаки на нескольких блокчейнах было зафиксировано множество последующих и имитирующих сделок. Этот инцидент подчеркивает несколько ключевых уроков по дизайну и безопасности протоколов DeFi:
При обеспечении операционной и бизнес-непрерывности участники отрасли могут использовать BlockSec Phalcon в качестве последней линии защиты своих активов. Экспертная команда BlockSec всегда готова провести комплексную оценку безопасности вашего проекта.
Ссылка на статью:
Источник: