Notional Finance був взламаний на $1,73 млн через вразливість переповнення цілого числа

iconMetaEra
Поділитися
AI summary iconКороткий зміст
На 4 вересня 2026 року Notional Finance став жертвою витіку на $1,73 млн, коли нападники використали переповнення цілого числа в контракті Escrow. Уразливість у функції ExchangeRate._convertToETH дозволила створювати позиції fCash без належного забезпечення. Цей інцидент підкреслює важливість сильного співвідношення ризику до прибутку в стратегіях DeFi. Трейдери, які використовують технічний аналіз для криптовалют, повинні залишатися уважними, оскільки такі вразливості можуть швидко змінити ринкову динаміку.

Автор: Дев'ять дев'ять

Редагування: 77

Контекст

4 вересня 2026 року відома децентралізована платформа позичання Notional Finance зазнала атаки, втративши приблизно 1,73 мільйона доларів США. Ось детальний аналіз події від команди безпеки SlowMist:

Попередні знання

У Notional Finance V1 fCash можна розуміти як грошове боргове зобов’язання з датою погашення. Одержувач грошей отримує платежі у день погашення, а платник грошей здійснює платіж у день погашення. Протокол фіксує обидва типи позицій у Portfolio, а потім оцінює здоров’я рахунку на основі перевірки вільного забезпечення, щоб визначити, чи можна створювати позицію.

ERC1155Trade safeTransferFrom виконує функцію обліку активів. Коли тип переданого активу належить отримувачу готівки, контракт викликає Portfolios.mintfCashPair, одночасно фіксуючи борг для платника та відповідний кредит для отримувача. Цей виклик виглядає як передача ERC1155, але насправді є парним випуском fCash.

Після запису нового положення в Portfolio необхідно провести розрахунок і перевірку вільного забезпечення. Угода RiskFramework спочатку підсумовує fCash зобов’язання платника у вигляді знакового int256, а потім перетворює залишки кожного активу на ETH для підсумовування. Від’ємні значення вказують на суму готівки, яку потрібно сплатити, а додатні — на готівку або права, якими володіє рахунок. Цей розрахунок визначає, чи можна дозволити угоду.

Після закінчення терміну позиції Portfolio викликає функцію portfolioSettleCash з Escrow, щоб перетворити термінований fCash на готівковий залишок. Потім обліковий запис виводить відповідні активи DAI або USDC через функцію виведення з Escrow.

Основна причина

Основна вразливість цієї атаки полягає в ExchangeRate._convertToETH, який використовується в контракті Escrow, де один рядок коду безпосередньо перетворює абсолютне значення цілого числа зі знаком на uint128.

Зловмисник спочатку створив два борги з однаковим платником на суми 1 та uint128.max. Сума цих двох боргів дорівнює 2^128. Оскільки платіж платника в розрахунках ризику відображається як від’ємне число, ключовим параметром, переданим у convertBalancesToETH Escrow, є -2^128.

Але версія Solidity, використовувана в цьому контракті, — 0.6.x, тому її абсолютне значення 2^128 після приведення до uint128 переповнюється і стає 0, і ця заборгованість не потрапляє до результату, вираженого в ETH.

Нарешті, у функції mintfCashPair перевіряється лише те, що вільний залог платника більший або дорівнює нулю, тому після переповнення результату до 0 ця перевірка пройдена, і рахунок, який не має достатньо активів для покриття зобов’язань, все ще може відкрити позицію.

Аналіз кроків атаки

1. У передньому транзакції (0xe1589a19…d60a) атакуючий спочатку створив кілька допоміжних контрактів та викликав функцію setApprovalForAll контракту ERC1155Trade, щоб надати допоміжним контрактам дозвіл; крім того, він заздалегідь запитав терміни закінчення двох CashMarket та розрахував значення трьох параметрів AssetId, необхідних для подальших операцій.

2. Потім атакуючий викликає функцію safeTransferFrom контракту ERC1155Trade для створення пари fCash у розмірі 1, де cashGroupId відповідного облігаційного активу дорівнює 2, а час дозрівання — 1788480000 (4 вересня 2026 року, 8:00). Під час цього викликається внутрішня функція _upsertAsset для оновлення заборгованості та очікуваних доходів для адреси from (контракт атакуючого) та адреси to (приймаючий контракт 1).

Після завершення чеканки пари Portfolios одразу перевіряє вільний колатерал атакуючого контракту, і через те, що кількість першої чеканки занадто мала, при поточному курсі та точності, перетворена на ETH, вона округлюється до нуля, тому перша перевірка проходить успішно.

3. Потім зловмисник знову викликав функцію safeTransferFrom контракту ERC1155Trade для створення пари fCash з сумою uint128.max, де cashGroupId відповідного облігаційного активу дорівнював 2, але час дозрівання був 1796256000 (3 грудня 2026 року, 8:00).

Тут є деталь: атакуючий виготовив облігаційні активи з різними датами погашення для двох різних адрес. Це пов’язано з тим, що під час оновлення заборгованості для адреси from, якщо облігаційні активи були однаковими, вони просто додавалися, що призводило до переповнення під час обчислення додавання та відмови (контракт використовував бібліотеку SafeMath).

4. Після оновлення заборгованості та очікуваних доходів для адреси from (контракт атаки) та адреси to (контракт отримання 2), функція mintfCashPair викликає функцію freeCollateral для розрахунку чистої позиції забезпечення адреси from і перевірки, чи вона більша або дорівнює нулю, щоб бути здоровим.

Спочатку у функції getRequirement контракту RiskFramework буде додано загальну суму заборгованостей від двох випусків з адреси from:

Його кінцевий результат — -2^128, після чого викликається функція convertBalancesToETH контракту Escrow для перетворення цього значення на ETH, а функція convertBalancesToETH знову викликає функцію _convertToETH бібліотеки ExchangeRate для обчислення:

У функції _convertToETH можна побачити, що спочатку balance береться за абсолютною величиною, а потім за допомогою uint128 він перетворюється з 256 бітів у 128 бітів. Сума боргів зверху дорівнює -2^128; після взяття абсолютної величини 2^128 під час примусового перетворення uint128() викликає перевищення ліміту та переповнення до 0. Це означає, що протокол помилково вважає, що вільна позиція залогу адреси from дорівнює 0 і є здоровим станом, що дозволяє пройти остаточну перевірку в функції mintfCashPair.

5. Після цього отриманий контракт 2 розділив свою позицію між двома додатковими контрактами, і обидва safeTransferFrom все ще направляються до функції mintfCashPair. Однак, оскільки платником зараз є контракт 2, отриманий на другому етапі мінту, який має величезну позицію очікуваного доходу з попереднього кроку, розрахунок ризику вважає його позитивним активом, і він проходить перевірку забезпечення. Тому обидва додаткові контракти отримали receiver fCash, який можна обміняти на готівку після терміну.

6. Потім зловмисник ініціював другу офіційну прибуткову угоду, у якій було розраховано відповідні стягнення для двох утримуваних позицій з попередньої угоди, збільшено готівковий баланс відповідних активів, а потім викликано функцію withdraw контракту Escrow для виведення активів і отримання прибутку.

Підсумок

Ключовим моментом цієї атаки є те, що атакуючий спочатку використав два окремі позиції, щоб сума боргу payer досягла 2^128, а потім змусив Escrow перетворити цей борг на ETH. Використовуючи переповнення під час перетворення типів, він обрізав результат до нуля, і перевірка ризиків пропустила це.

Команда безпеки SlowMist рекомендує проектним командам обов’язково виконувати перевірку діапазону перед перетворенням типів; результати, що перевищують type(uint128).max, слід відхиляти безпосередньо. Крім того, усі межі, пов’язані зі знаковими сумами, масштабуванням точності та номіналами активів, повинні проходити тестування на граничні значення, зокрема для вхідних даних: uint128.max, uint128.max + 1 та абсолютні значення від’ємних чисел. Для обробки перетворень можна скористатися бібліотекою SafeCast від OpenZeppelin.

Відмова від відповідальності: Інформація на цій сторінці може бути отримана від третіх осіб і не обов'язково відображає погляди або думки KuCoin. Цей контент надається лише для загального інформування, без будь-яких запевнень або гарантій, а також не може розглядатися як фінансова або інвестиційна порада. KuCoin не несе відповідальності за будь-які помилки або упущення, а також за будь-які результати, отримані в результаті використання цієї інформації. Інвестиції в цифрові активи можуть бути ризикованими. Будь ласка, ретельно оцініть ризики продукту та свою толерантність до ризику, виходячи з ваших власних фінансових обставин. Для отримання додаткової інформації, будь ласка, зверніться до наших Умов використання та Розкриття інформації про ризики.