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 неточно округляется в большую сторону.
Анализ атаки
Злоумышленники инициировали несколько транзакций атаки, при этом каждая транзакция опустошала несколько пулов. Для простоты следующее обсуждение основано на первой атаке в рамках этой транзакции атаки.
Основная логика атаки состоит из следующих шести шагов:
-
Заимствование 2000 WETH через флеш-кредит от AAVE.
-
Обмен 6.850 WETH на 6.371 frxETH в целевом пуле 0xfd7b. Этот шаг используется для перевода текущего тика и
currentSqrtPв положение, где в настоящее время ликвидность отсутствует.
currentSqrtP, по-видимому, был выбран злоумышленником случайным образом, и обмен останавливается именно на этой цене.- Базовая ликвидность (
baseL) после этого шага равна нулю, но ликвидность реинвестирования (reinvestL) не равна нулю.
- Добавление ликвидности в пул, а затем удаление части ликвидности. Этот шаг используется для контроля диапазона и общей ликвидности до желаемого значения.
- Диапазон тиков выбирается на основе
currentSqrtP, хотя соответствующая логика расчета требует дальнейшего изучения.
- Обмен 387.170 WETH на 0.06 frxETH в пуле. Этот шаг используется для манипулирования текущим тиком так, чтобы
nextTick==currentTick. В частности, обмен на этапе 4 хитро вводит пул в заблуждение, заставляя его поверить, что тик 111,310 не пересечен. Однако в действительностиcurrentSqrtPдействительно больше, чемsqrtPтика 111,310.
- Обмен 0.06 frxETH на 396.244 WETH в пуле. Обратите внимание, что направление обмена противоположно предыдущему шагу. На этом этапе ликвидность учитывается дважды, чтобы сделать обмен прибыльным и, следовательно, опустошить пул.
- Погашение флеш-кредита и получение 6.364 WETH и 1.117 frxETH.
Для углубленного изучения, пожалуйста, обратитесь к нашему комплексному анализу, который включает больше деталей с расчетами и рисунками.
Резюме
Фундаментальная проблема в этом инциденте заключается в неправильном округлении во время процесса реинвестирования KyberSwap, что привело к неточным расчетам тиков и, в конечном итоге, к двойному учету ликвидности. Этот инцидент подчеркивает сложный и неуловимый характер проблем потери точности в протоколах DeFi, создавая серьезный вызов для всего сообщества.
Эта атака 2023 года выделяется своей сложностью, отличаясь исключительно тонкими расчетами и служа ярким примером многих инцидентов безопасности, связанных с точностью, которые серьезно испытали сообщество. Более того, после длительных переговоров с властями злоумышленник опубликовал вызывающее сообщение для общественности, заявив о требовании полного контроля над протоколом.
Читайте другие статьи из этой серии:
- Вступление: Десять главных "потрясающих" инцидентов безопасности в 2023 году
- #1: Сбор MEV-ботов путем использования уязвимостей в Flashbots Relay
- #2: Инцидент с Euler Finance: Крупнейший взлом 2023 года
- #4: Инцидент с Curve: Ошибка компилятора создает ошибочный байт-код из безобидного исходного кода
- #5: Platypus Finance: Выживание после трех атак по счастливой случайности
- #6: Инцидент с Hundred Finance: Катализатор волны эксплойтов, связанных с точностью, в уязвимых форкнутых протоколах
- #7: Инцидент с ParaSpace: Гонка со временем, чтобы предотвратить самую критическую атаку в отрасли
- #8: Инцидент с SushiSwap: Неуклюжая попытка спасения привела к серии подражательных атак
- #9: MEV-бот 0xd61492: От хищника до добычи в гениальном эксплойте
- #10: Инцидент с ThirdWeb: Несовместимость между доверенными модулями раскрывает уязвимость





