18 июля наша система DeFiRanger зафиксировала несколько подозрительных транзакций. После ручного анализа мы подтвердили, что эти транзакции являются атаками на Array Finance. Далее мы воспользуемся одной из транзакций атаки, чтобы проиллюстрировать процесс нападения и первопричину уязвимости.
Транзакция атаки
Транзакция атаки, используемая в этой статье: 0xa17bbc7c9ab17aa88fdb5de83b41de982845e9c9c072efff6709dd29febf0daa
Ход атаки

Как показано на Рисунке 1, мы обнаружили, что злоумышленник получил прибыль в размере 186,62 WETH (в данной статье мы не будем проводить явного различия между WETH и ETH) после получения флэш-кредита от AAVE.

Подробный процесс атаки показан на Рисунке 2.
- Сначала злоумышленник вызвал функцию
buyв Array Finance. Он получил 430 токенов ARRAY, выпущенных (minted) Array Finance, используя 45,91 WETH. - Затем злоумышленник пять раз вызвал функцию
joinPoolконтракта с закрытым исходным кодом (Array Collater - 0xa800cda5). Он внес 676 410,58 DAI + 679 080,46 USDC + 901,82 WETH + 20 WBTC + 20 renBTC и получил 726,38 токенов aBPT, выпущенных Array Collater. - Злоумышленник вызвал функцию
sell, чтобы сжечь 430 токенов ARRAY, и получил 77,17 токенов aBPT. - Наконец, злоумышленник вызвал функцию
exitPoolв Array Collater. Он сжег 804,55 токенов aBPT, полученных на предыдущих двух шагах, и получил 748 271,55 DAI + 751 225,08 USDC + 997,62 WETH + 22,63 WBTC + 22,74 renBTC.
Из Рисунка 2 видно, что злоумышленник получает прибыль на шаге 5 (Рисунок 2: Вызов функции sell). Это связано с тем, что полученные 77,17 токенов aBPT более ценны, чем 49,9142 WETH, внесенные на шаге 3 (Рисунок 2: Вызов функции buy). Далее мы проанализируем код, чтобы понять, почему стала возможной эта атака.
Уязвимость в коде
Ниже приведен код функции sell из Array Finance. В этой функции Array Finance использует баланс токенов ARRAY, принадлежащих злоумышленнику, и вызывает внутреннюю функцию _sell для расчета количества токенов aBPT, которые можно получить при продаже токенов ARRAY.

Ниже представлена реализация функции _sell. Она вызывает calculateLPtokensGivenArrayTokens, чтобы получить количество токенов aBPT, которые можно получить при определенном количестве токенов ARRAY. Затем эта функция сжигает токены ARRAY и возвращает токены aBPT.

Ниже показана реализация функции calculateLPtokensGivenArrayTokens.

Обратите внимание, что есть четыре аргумента, которые могут повлиять на расчет amountLPToken. Проанализировав saleTargetAmount, мы пришли к выводу, что формула выглядит следующим образом:
arraySmartPool.totalSupply() * (1 - (1 - amount / ARRAY.totalSupply()) ^ (1000000 / reseveRatio))
arraySmartPool — это адрес смарт-контракта Array Collater (0xa800cda5f3416a6fb64ef93d84d6298a685d190d). Значение arraySmartPool.totalSupply() увеличивается, когда злоумышленник вносит средства, взятые в флэш-кредит, в Array Collater (показано в таблице ниже).
TxnIndex: 64 arraySmartPool.totalSupply(): 110162296218708026400
TxnIndex: 107 arraySmartPool.totalSupply(): 165243444328062039600
TxnIndex: 150 arraySmartPool.totalSupply(): 247865166492093059400
TxnIndex: 193 arraySmartPool.totalSupply(): 371797749738139589100
TxnIndex: 236 arraySmartPool.totalSupply(): 5576



