Back to Blog

№3: Инцидент в KyberSwap: мастерское использование ошибок округления посредством крайне тонких вычислений

Code Auditing
10 февраля 2024 г.
6 min read

23 ноября 2023 года произошла серия атак на KyberSwap. Эти атаки привели к совокупному ущербу на сумму более 48 миллионов долларов. Фундаментальная проблема возникла из-за некорректного направления округления в процессе реинвестирования KyberSwap. Это впоследствии привело к неправильным расчетам тиков и, в конечном итоге, к двойному учету ликвидности.

Мы выпустили подробный отчет "Еще одна трагедия потери точности: углубленный анализ инцидента с KyberSwap", в котором подробно рассматриваются детали этого события. Для более глубокого понимания мы рекомендуем ознакомиться с полным анализом. Ниже представлено краткое введение в данный инцидент, который мы включили в десятку главных инцидентов безопасности 2023 года.

Фон

KyberSwap — это децентрализованная платформа автоматизированного маркет-мейкера (CLAMM). Чтобы удовлетворить рыночный спрос на концентрированную ликвидность, KyberSwap Elastic была запущена на базе Uniswap V3 с рядом улучшений, включая кривую реинвестирования для обеспечения автоматического реинвестирования доходности от предоставления ликвидности.

1. Тик и цена квадратного корня

Тик в CLAMM, подобных Uniswap V3, используется для дискретного обозначения цены, что позволяет поставщикам ликвидности (LP) предоставлять ее в фиксированном диапазоне, а не во всем диапазоне (отсюда термин "концентрированная").

Ликвидность может быть размещена в диапазоне между любыми двумя тиками (которые не обязательно должны быть соседними), т.е. парой индексов тиков (нижний тик и верхний тик). В частности, цена каждого тика (с целочисленным индексом i) определяется следующим образом:

На практике используется цена квадратного корня (обозначается как sqrtP или sqrtPrice):

Также возможно вычислить текущий тик на основе текущей цены квадратного корня:

Очевидно, что хотя для данного тика рассчитывается только одна цена квадратного корня, несколько цен квадратного корня могут указывать на один и тот же тик. Для получения более подробного объяснения, пожалуйста, обратитесь к документации Uniswap V3 и KyberSwap.

2. Кривая реинвестирования

CLAMM на базе Uniswap V3 страдает от низкой эффективности использования пула для комиссий LP и значительных затрат газа, необходимых для реинвестирования. Поэтому KyberSwap приняла кривую реинвестирования для решения этой проблемы.

Ключ к кривой реинвестирования заключается в том, что комиссии, собранные в каждом обмене, накапливаются в качестве дополнительной ликвидности в пуле как реинвестируемая ликвидность в бесконечном диапазоне. Реинвестируемые токены выпускаются для LP, и накопленная ликвидность распределяется между ними соответствующим образом. Кроме того, реинвестируемая ликвидность также участвует в процессе обмена и расчета цены.

Соответствующий код для расчетов, представленных выше, показан в функции computeSwapStep в следующем фрагменте кода соответствующего пула.

Следует отметить, что из-за ликвидности реинвестирования liquidity (ликвидность) в этой функции является суммой двух компонентов: baseL для базовой ликвидности и reinvestL для накопленной ликвидности для реинвестирования.

3. Обмен в KyberSwap

Реализацию функции swap пула KyberSwap, рассмотренную ранее, можно представить в виде следующей диаграммы:

Ключевая логика, относящаяся к расчету тиков, находится внутри цикла обмена, что выделено синим прямоугольником. В частности, основная логика включает функцию computeSwapStep и функцию _updateLiquidityAndCrossTick. Первая рассчитывает ключевые состояния, такие как объемы ввода и вывода для данного обмена и nextSqrtP, в то время как вторая обрабатывает случаи, когда происходит пересечение тика.

Традиционно, когда цена растет, мы называем это сдвигом тика вправо/вверх; в противном случае мы говорим, что тик движется влево/вниз.

Чтобы лучше понять уязвимость, которая будет обсуждаться позже, необходимо изучить соответствующую логику кода функции computeSwapStep, как показано на следующем рисунке:

В частности, функция calcReachAmount вычисляет входные токены, необходимые для целевой цены targetSqrtP (строки 50-57). Если usedAmount больше, чем specifiedAmount, тик не пересекается, и nextSqrtP рассчитывается из deltaL (т.е. дельта-ликвидности, строки 59-62). deltaL определяется с помощью функции estimateIncrementalLiquidity, а конечная цена nextSqrtP рассчитывается с помощью функции calcFinalPrice (строки 70-79). Если требуется меньше входных данных, nextSqrtP устанавливается на цену следующего тика, но этот случай не используется в атаке.

Приведенные выше шаги ясно показывают, что если тик не пересекается, nextSqrtP, возвращаемый функцией computeSwapStep, не должен быть больше, чем sqrtP следующего тика. Однако из-за зависимости цены от ликвидности (базовой ликвидности и дельта-ликвидности) и потери точности, злоумышленники могут манипулировать nextSqrtP, чтобы он стал больше, хотя тик не пересечен.

Анализ уязвимости

Первопричина заключается в ошибочном расчете тика, вызванном неправильным направлением округления при расчете дельта-ликвидности (т.е. функции estimateIncrementalLiquidity) контракта SwapMath (который вызывается функцией computeSwapStep). Это, в свою очередь, неправильно влияет на последующий расчет тика.

Интересно, что при изучении комментария в строке 188 (выделено синим прямоугольником) мы обнаруживаем, что deltaL должна округляться в большую сторону для округления в меньшую сторону nextSqrtP. Однако deltaL по ошибке округляется в меньшую сторону из-за использования функции mulDivFloor в строке 189. Следовательно, nextSqrtP неточно округляется в большую сторону.

Анализ атаки

Злоумышленники инициировали несколько транзакций атаки, при этом каждая транзакция опустошала несколько пулов. Для простоты следующее обсуждение основано на первой атаке в рамках этой транзакции атаки.

Основная логика атаки состоит из следующих шести шагов:

  1. Заимствование 2000 WETH через флеш-кредит от AAVE.

  2. Обмен 6.850 WETH на 6.371 frxETH в целевом пуле 0xfd7b. Этот шаг используется для перевода текущего тика и currentSqrtP в положение, где в настоящее время ликвидность отсутствует.

  • currentSqrtP, по-видимому, был выбран злоумышленником случайным образом, и обмен останавливается именно на этой цене.
  • Базовая ликвидность (baseL) после этого шага равна нулю, но ликвидность реинвестирования (reinvestL) не равна нулю.
  1. Добавление ликвидности в пул, а затем удаление части ликвидности. Этот шаг используется для контроля диапазона и общей ликвидности до желаемого значения.
  • Диапазон тиков выбирается на основе currentSqrtP, хотя соответствующая логика расчета требует дальнейшего изучения.
  1. Обмен 387.170 WETH на 0.06 frxETH в пуле. Этот шаг используется для манипулирования текущим тиком так, чтобы nextTick == currentTick. В частности, обмен на этапе 4 хитро вводит пул в заблуждение, заставляя его поверить, что тик 111,310 не пересечен. Однако в действительности currentSqrtP действительно больше, чем sqrtP тика 111,310.
  1. Обмен 0.06 frxETH на 396.244 WETH в пуле. Обратите внимание, что направление обмена противоположно предыдущему шагу. На этом этапе ликвидность учитывается дважды, чтобы сделать обмен прибыльным и, следовательно, опустошить пул.
  1. Погашение флеш-кредита и получение 6.364 WETH и 1.117 frxETH.

Для углубленного изучения, пожалуйста, обратитесь к нашему комплексному анализу, который включает больше деталей с расчетами и рисунками.

Резюме

Фундаментальная проблема в этом инциденте заключается в неправильном округлении во время процесса реинвестирования KyberSwap, что привело к неточным расчетам тиков и, в конечном итоге, к двойному учету ликвидности. Этот инцидент подчеркивает сложный и неуловимый характер проблем потери точности в протоколах DeFi, создавая серьезный вызов для всего сообщества.

Эта атака 2023 года выделяется своей сложностью, отличаясь исключительно тонкими расчетами и служа ярким примером многих инцидентов безопасности, связанных с точностью, которые серьезно испытали сообщество. Более того, после длительных переговоров с властями злоумышленник опубликовал вызывающее сообщение для общественности, заявив о требовании полного контроля над протоколом.

Читайте другие статьи из этой серии:

Sign up for the latest updates
За пределами смарт-контракта: операционная безопасность доменов и DNS в Web3
Security Insights

За пределами смарт-контракта: операционная безопасность доменов и DNS в Web3

Аудит контрактов не проверяет сам контракт. Мы провели 800 SEAL-проверок DNS и регистраторов для 100 доменов из TVL Top 100 DefiLlama — лишь один домен прошёл все. Каких четырёх мер защиты не хватает большинству и почему это критично для пользователя.

Поверхности атаки Web3: обзор тестирования на проникновение

Поверхности атаки Web3: обзор тестирования на проникновение

Крипто-организации сохраняют все традиционные уязвимости и добавляют цепочку работы с деньгами. Статья описывает систему через четыре компонента: приложение, авторизацию и подпись, взаимодействие с блокчейном, инфраструктуру — с их зонами атак, а также пять специфичных для web3 областей: от эксплуатации и подписания до вывода средств и контрактов.

Потеряно ~23 млн $: эксплойты Cosmos EVM и Moonwell | BlockSec Weekly
Security Insights

Потеряно ~23 млн $: эксплойты Cosmos EVM и Moonwell | BlockSec Weekly

За период (22.08.2026–30.08.2026): 5 инцидентов, ~$22,7 млн убытков; Tectonic — $74–119,5 млн, откачены на Cronos. Взлом Cosmos EVM (~$5,7 млн) на TAC Chain, Moonwell, Ajna, Rain Card Exploit (Ed25519) на Solana.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit