Back to Blog

№6: Инцидент с Hundred Finance: катализатор волны эксплойтов в уязвимых форкнутых протоколах

Code Auditing
February 16, 2024
6 min read

16 апреля 2023 года Hundred Finance, форк Compound V2, подвергся атаке, приведшей к потере около 7,4 млн долларов США. Атака была связана с двумя основными проблемами:

  • Проблема потери точности (ошибка неправильного округления);
  • Пустые рынки, что позволило хакеру манипулировать exchangeRate (обменным курсом).

Compound V2, наиболее часто форкаемый протокол кредитования согласно DeFiLlama, насчитывает более 100 форков. Его контракты, проверенные OpenZeppelin и испытанные в реальных условиях, считаются безопасными. Тем не менее, атака на Hundred Finance показала, как потеря точности, особенно при низкой ликвидности, может критически повлиять на безопасность протокола DeFi, спровоцировав волну аналогичных эксплойтов в таких известных форках, как Midas и Radiant.

Для более глубокого понимания мы рекомендуем ознакомиться с полным анализом. Ниже представлено краткое введение в данный инцидент, который мы выделили как один из десяти главных инцидентов безопасности 2023 года.

Предыстория

Обзор Hundred Finance

Hundred Finance — это форк Compound v2, который работает в нескольких основных сетях, используя оракулы Chainlink. В отличие от традиционных практик кредитования, протоколы кредитования DeFi, такие как Compound и Aave, не допускают избыточного обеспечения. Проще говоря, если вы вносите Токен А на сумму 100 долларов, вы можете заимствовать активы только на сумму менее 100 долларов. Согласно коэффициентам контроля рисков большинства протоколов, этот коэффициент обычно варьируется от 50% до 80%.

Для протоколов кредитования распространенными методами атаки являются манипулирование ценами и повторный вход (reentrancy). Примечательно, что ранее Hundred Finance уже сталкивался с атакой типа reentrancy в марте 2022 года, но данный инцидент создал новый вектор атаки.

hToken

Протоколы кредитования, созданные на основе Compound и Aave, создают соответствующий расчетный токен для каждого базового токена (обеспечения). Для Hundred Finance:

  • USDC соответствует hUSDC
  • WBTC соответствует hWBTC

Обменный курс между базовым токеном и hToken называется exchangeRate.

  • При внесении активов пользователи должны вызвать метод mint() токена hToken.
  • При выводе активов пользователь должен вызвать метод redeem() токена hToken.

exchangeRate

Ниже приведена формула для расчета exchangeRate: image

Где:

  • getCash(): Количество базовых токенов на балансе данного контракта hToken. Это ключевой параметр, которым можно манипулировать. Пожалуйста, запомните это.
  • totalBorrows(): Количество базовых токенов, выданных рынком в текущий момент, и сумма, на которую начисляются проценты поставщикам рынка.
  • totalReserves(): Резервы — это бухгалтерская запись в каждом контракте hToken, представляющая часть исторических процентов, отложенных как денежные средства, которые могут быть выведены или переданы через управление протоколом.
  • totalSupply(): Количество токенов, находящихся в обращении на данном рынке hToken.

Примечание: exchangeRate между hToken и базовым активом (например, dai против hDai или eth против hEth) начинается с 0,020 и увеличивается со скоростью, равной рыночной процентной ставке compounding.

Ликвидация

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

  1. Алиса вносит BTC на 100 долларов и занимает ETH на 70 долларов.
  2. Если цена ETH растет или цена BTC падает, активы Алисы могут достичь порога ликвидации.
  3. Боб может использовать определенное количество ETH, чтобы ликвидировать BTC Алисы, вернув долг Алисы к безопасному уровню, чтобы обезопасить средства протокола (протокол вознаграждает пользователей, инициирующих ликвидацию).

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

Уязвимость

Проблема потери точности

Хакер добивается того, что при выводе обеспечения через redeem() расчет суммы hToken, подлежащей вычету, дает результат 1,99999992 (очень близко к 2, но меньше 2). При преобразовании в целое число в truncate() использование округления в меньшую сторону приводит к итоговому результату 1.

exchangeRate

Расчет exchangeRate, как упоминалось ранее, включает getCash(), который относится к количеству базовых активов на балансе контракта hToken. Путем прямой отправки базовых токенов в контракт (без вызова mint, просто простым переводом), хакер может манипулировать exchangeRate. Однако важно отметить, что сама по себе проблема с exchangeRate не нарушает безопасность протокола; хакеры не могут получить от этого прибыль в изоляции. В контексте этой атаки этот метод в основном использовался для усиления выгоды хакера, позволяя ему быстро истощить пул. В противном случае атака превратилась бы из единого решительного удара в серию мелких уколов, требующих множества итераций для оказания существенного влияния.

Вкратце, потеря точности — это ключевая проблема данной атаки.

Процесс атаки

Вот транзакция атаки, и теперь мы воспользуемся Phalcon Explorer для ее анализа.

Транзакция: 0x6e9ebcdebbabda04fa9f2e3bc21ea8b2e4fb4bf4f4670cb8483e2f0b2604f451

  1. Займите 500 WBTC у Aave V3 через Flashloan (мгновенный кредит).

  2. Redeem (погасите) все ранее приобретенные hWBTC, сбросив totalSupply hWBTC до 0.

Цель до этого шага — подготовить резервные средства с помощью мгновенного кредита и сбросить hWBTC как новый рынок.

  1. Create второй контракт атаки (далее Контракт атаки 2) и переведите все WBTC (500,30063816 WBTC) на Контракт атаки 2.

  2. Mint() hWBTC, используя 4 WBTC, чтобы получить 200 hWBTC.

  3. Redeem() 199,99999998 hWBTC, оставив общий объем hWBTC на уровне 0,00000002 (2 wei hWBTC).

  4. Переведите все WBTC (500,30063816 WBTC) в hWBTC. Обратите внимание, что прямые переводы не увеличивают баланс hWBTC; это можно рассматривать как пожертвование WBTC в пул. Основная цель этого шага — манипулирование exchangeRate, о котором говорилось ранее. В этот момент totalSupply hWBTC остается равным 2 wei hWBTC, но теперь в пуле находится 500,30064194 WBTC, что делает exchangeRate в сотни раз выше первоначального.

  5. Займите 1021 Ether на рынке hETH.

  6. Redeem() 50030063815 WBTC. После расчета должно быть вычтено 1,9999992 hBTC, но из-за потери точности вычитается только 1 hBTC, что создает значительную потерю точности (почти 50%). На данный момент у хакера есть 500 WBTC + 1021 Ether, что дает успешную прибыль в 1021 Ether.

  7. Злоумышленник вызывает liquidate() для оставшегося hWBTC, сбрасывая его totalSupply до 0 в рамках подготовки к продолжению атак на другие рынки. Учитывая, что почти все WBTC внутри hWBTC были выведены, хакер справляется с этим, имея всего 0,000002 Ether.

  8. Продолжайте атаковать другие рынки, истощая весь протокол.

  9. Верните Flashloan в Aave.

Рекомендации по безопасности

Меры по смягчению последствий для протоколов кредитования

Эта проблема распространена, особенно в форках Compound и Aave. Проактивный подход заключается в том, чтобы при запуске новых рынков чеканить (mint) некоторое количество расчетных токенов, чтобы гарантировать, что totalSupply никогда не опустится до 0.

Меры по смягчению последствий потери точности

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

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

Best Security Auditor for Web3

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

BlockSec Audit