Резюме управління
Команда ZenPool зв’язалася з Sayfer Security, щоб провести повний аудит безпеки для всіх їхніх контрактів.
Перед оцінкою цих послуг ми провели стартову нараду з технічною командою ZenPool і отримали огляд системи та цілі цієї оцінки.
Наступний аудит зайняв 20 людино-днів, усі контракти в ланцюжку перевірялися рядок за рядком принаймні 2 аудиторами на контракт. Через часові обмеження з боку клієнта ми перевірили компоненти поза ланцюгом і вжили найкращих зусиль, щоб переконатися, що вони не містять критичних уразливостей.
Ми знайшли загалом 9 знахідок, 3 з яких були класифіковані як уразливості «високого» ризику та могли бути використані зловмисниками, які могли повністю випорожнити кошти пулу.
Ми задокументували наш процес і наші пропозиції щодо усунення кожної вразливості. Команда ZenPool реалізувала виправлення, які були задокументовані в кожному розділі вразливості.
Огляд системи
ZenPool — це токен із відкритим вихідним кодом, який не є кастодіальним, і ринковий протокол кредитування.
Користувачі можуть внести свої криптоактиви, щоб заробити відсотки, або позичити інші токени, щоб сплатити відсотки на ринку ZenPool. ZenPool має власний токен під назвою ZEN.
ZEN — це вільно плаваюча валюта, забезпечена казначейським запасом стабільної монети BUSD. Токени ZEN можна карбувати та записувати лише за допомогою протоколу, протокол робить це лише у відповідь на ціну. Кожен ZEN підтримується принаймні одним BUSD.
Якщо ціна ZEN падає нижче 1 BUSD, протокол купує та спалює ZEN, повертаючи ціну до 1 BUSD.
ZenPool також підтримує облігації, які є ще одним способом збільшити його скарбницю. Протокол продає облігації в обмін на різні активи, а в обмін покупець отримує ZEN за значно зниженою ціною. Це збільшує скарбницю та дозволяє ZeenPool надавати неймовірні прибутки своїм клієнтам.
Нарешті, частина всіх комісій за продукти ZenPool буде використовуватися як додаткова підтримка, потенційно забезпечуючи токен ZenPool нескінченну смугу для винагороди за ставки.
Уразливості за ризиком
Високий
- Транзакція DoS
- пряма втрата коштів
- постійне заморожування коштів
Medium
- Атаки на тонкі клієнти
- Частково DoS
- Газова атака
низький
- Кращі практики
Інформаційний
- Не завдає шкоди системі, і ми не маємо достатньо даних або знань, щоб довести, що це коли-небудь зашкодить, але важливо поділитися нашим
Обсяг і контракти
У рамках визначення обсягу проекту та для розуміння цілей і потреб наших клієнтів ми визначили контракти, які ми повинні протестувати під час цього аудиту. Разом ми знайшли найкращий баланс, який підходить для цього конкретного проекту.
Область дії за визначенням є м’якою, тобто ми можемо протестувати в локальному середовищі інші контракти, які можуть взаємодіяти з контрактом, який визначено як область аудиту. Це дає нам можливість знаходити проблеми безпеки, які інакше можна було б проігнорувати.
Список контрактів і їх комміт Github:
Зафіксувати хеш
Зафіксувати хеш | Контракт |
5d253cb3c544a17399e7d2f4c381a1065bcfa0a0 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/createGovernor.sol |
3887cd307163d130477d4805e74ade11f84d7a4d | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenPoolUserManager.sol |
a50062dfc63a0e82ec43de98865420193270236a | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenPoolManager.sol |
c4d0db2f9fbfc3146a1a1a797e55c3f3d7a19899 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenTickets.sol |
844132ae5210cb27101bf4a415d9c16e201e1afc | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenPoolFunds.sol |
a70146c5d276f933a79c567d2e96512302a6a940 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenGovernorAlpha.sol |
d476137af592fe71cae2578a62e2f8f92b335d8c | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenLendingPool.sol |
8c5d858ad7fbfe856cd23160fd8810997f3fdf70 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenGovernorAlpha.sol |
Замовити аудит у Сайфер
Результати аудиту смарт-контрактів
Користувачі можуть знімати кошти з інших балансів, доки пул не спорожніє
Контракт | contracts/ZenPoolFunds.sol |
Risk | Високий |
Виправлено | Так |
Знайдено | Ручне тестування |
Опис
Команда withdrawFunds
функція підтверджує, що користувач може зняти лише максимальну суму на своєму балансі, перевірену за його адресою та пізніше порівняну в пулі токенів.
Пізніше знята сума вираховується з поточної максимальної позики користувача за поточною ціною. Якщо загальна сума позичених коштів користувачем перевищує нову максимальну позику, метод не вдається, оскільки користувач більше не має достатньої застави для підтримки своєї позиції. Однак ця вимога перевіряється, лише якщо користувач ще не використовує надмірне кредитне плече:
Зловмисник може використати цю функцію для використання withdrawFunds
функція для зняття суми, що перевищує максимальну суму позики.
Він міг це зробити, тому що getMaxBorrow
перевірятиметься лише в тому випадку, якщо користувач позичить менше максимально дозволеної суми, у різних сценаріях зловмисник може зловживати потоком, щоб пропустити цей конкретний require
, дозволяючи йому дзвонити withdrawFund
кілька разів, поки басейн не спустошиться.
Пом'якшення
Змінити require
викликаний перед рядком 145, тому він не залежить від оператора if. Таким чином require
завжди буде виконано.
Небезпечне самознищення в контракті проксі
Контракт | контракти/ZenTickets.sol contracts/ZenPoolManager.sol |
Risk | Високий |
Виправлено | Так |
Знайдено | Ручне тестування |
Опис
При осн ZenPoolManager
розгортається, він також розгортає кілька контрактів.
Одним з них є ZenTickets
договір, який переважно забезпечений, за винятком destoryContract
функція, яка не має механізму ACL, як інші функції:
Використовуючи цю функцію, неавтентифікований зловмисник може викликати destoryContract
і виберіть, куди буде переведено ETH контракту. Це легко використовувати з точки зору зловмисника.
Пом'якшення
Використовувати onlyOwner
модифікатор, який використовується для скидання функцій.
Іншим більш конкретним рішенням було б використовувати спеціальний механізм ACL, як-от ACL ролей openzeppelin, який дозволить лише певним ролям отримувати доступ до destoryContract
Функції
Пули користувачів зазнають атак MEV
Контракт | contracts/ZenPoolUserManager.sol |
Risk | Високий |
Виправлено | Ні – ризиковано |
Знайдено | Ручне тестування |
Опис
Головний ZenPoolUserManager
обробляє власні пули користувачів, які були створені в системі. Користувач може викликати різні дії в цих пулах, якщо він має на це відповідні дозволи, користувач також може надати доступ через механізм ролей на основі контрактів контролю доступу OpenZeppelin
Команда ZenPoolUserManager
має відповідний механізм ACL, але в той же час він має 2 функції, які піддаються передній/задній роботі або будь-яким атакам MEV.
Незважаючи на те, що сама бізнес-логіка правильна, і здається, що вона не може завдати великої шкоди, оскільки має відповідний список керування доступом, користувач із такою ж роллю може використовувати її за допомогою атак MEV.
Пом'якшення
Використовувати onlyOwner
модифікатор, який використовується для скидання функцій.
Іншим більш конкретним рішенням було б використовувати спеціальний механізм ACL, як-от ACL ролей openzeppelin, який дозволить лише певним ролям отримувати доступ до destoryContract
функції.
Відсутність пом'якшення опікуна
Контракт | контракти/ZenGovernorAlpha.sol |
Risk | Medium |
Виправлено | Виправлено |
Знайдено | Аналіз застарілого коду та ручне тестування |
Опис
Під час нашого аудиту ми виконали аналіз вихідного коду та порівняли поточну кодову базу клієнта з кодовою базою
проекти з відкритим кодом, які клієнт використовував для розробки коду. Якщо ми виявили, що у коді з відкритим вихідним кодом були внесені зміни, ми глибше вивчили їх, щоб переконатися, що вони зроблені мудро.
Більшість клієнтів вважають сторонні проекти з відкритим кодом безпечними, оскільки вони надходять від великих постачальників (наприклад, пул Uniswap). Це припущення здебільшого вірне. Однак серйозні помилки безпеки виникають, коли клієнти вносять незначні зміни в код і припускають, що ці зміни не впливають на загальну безпеку продукту.
Під час нашого аналізу оригінального коду ми виявили, що ZenPool використовує код із протоколу STRIKE для створення маркера управління, оригінальний код із репозиторію STRIKE:
Тоді як код у ZenGovernorAlpha:
Наступне твердження було видалено msg.sender == guardian
, це означає, що опікун не може скасувати замовлення, якщо досягнуто порогу. Усунення влади охоронців може спричинити дуже небезпечні ситуації. Наприклад, якщо було виконано скомпрометовану пропозицію, яка перераховує всі гроші за контрактом зловмиснику (наприклад, за допомогою злому закритого ключа), механізм порогового захисту є зайвим і не може допомогти, оскільки немає опікуна, який міг би зупинити пропозицію від того, що відбувається.
Пом'якшення
додавати msg.sender == guardian
до require
.
Відсутність Reenetrancy Guard
Контракт | contracts/ZenLendingPool.sol |
Risk | Medium |
Виправлено | Виправлено |
Знайдено | Ковзання та ручне тестування |
Опис
Наступна функція запозичення не містить ні захисту від повторного входу, ні шаблону перевірок-ефектів-взаємодій, наразі це не можна використовувати, оскільки пул підтримує лише токени ERC20, але якщо в майбутньому буде додано новий ERC-777, може статися експлойт повторного входу та спричинити повне використання контрактних коштів.
Крім того, код не дотримується шаблону перевірки-ефекти-взаємодії. У цьому конкретному випадку, require
перевіряється після того, як деяка бізнес-логіка вже реалізована, це може спричинити помилки повторного входу, якщо побічні ефекти будуть додані до require
.
Пом'якшення
Додайте захист від повторного входу для повторного входу тієї ж функції, а також використовуйте шаблон перевірки-ефекти-взаємодії для складного повторного входу між функціями.
Заборонити перекази нульової суми
Контракт | contracts/ZenPoolFunds.sol |
Risk | низький |
Виправлено | Так |
Знайдено | Ручне тестування |
Опис
Команда ZenPoolFunds.sol
Контракт дозволяє переказувати нульові суми між користувачами. Хоча сама по собі це не є вразливістю безпеки, це зразок коду, який може розширити вектор атаки зловмисника.
Вразливість існує тому, що transfer
на ZenPool випускають події, роблячи нульову кількість transfer
зловмисник може випустити кілька подій, які в деяких сценаріях можуть ініціювати бізнес-логіку поза ланцюгом, навіть не маючи маркерів у пулі.
Пом'якшення
Потрібне глибше розуміння бізнесу. Якщо можливо, видаліть цю функцію.
Якщо є випадки використання, коли має сенс використовувати перекази нульової суми, запровадьте інший рівень перевірок, щоб обмежити використання користувачами, які входять до цієї групи.
Недостатнє ведення журналу для привілейованих функцій
Контракт | контракти/ZenGovernorAlpha.sol |
Risk | інформація |
Виправлено | Так |
Знайдено | Ручне тестування |
Опис
Наступні пільгові onlyOwner
функція не випромінює події.
Події є звичайною практикою для привілейованих функцій, щоб оголосити громадськості про внесені зміни. Без подій зміни важче виявити, і користувачів дивує поведінка контракту.
Власник може дзвонити chainId
шляхом виконання setChainId()
і змінюючи chaindId
параметр без будь-якої події, випущеної в процесі.
Пом'якшення
Додайте код, який видає події.
Надлишкова перевірка стану
Контракт | contracts/createGovernor.sol |
Risk | інформація |
Виправлено | Так |
Знайдено | Ручне тестування |
Опис
При створенні нового губернатора маркера _timelockDelay
параметр перевіряється двічі. onely in all перевіряє, коли функція запускається, а потім як частину іншої require
, це зайве і його слід видалити.
Дубльований код потребує більше газу для роботи та може призвести до майбутніх помилок, коли бізнес-логіка зміниться.
Пом'якшення
Видаліть друге зайве require
.
Непослідовність імен
Контракт | – Кілька – |
Risk | інформація |
Виправлено | Ризикують |
Знайдено | Ковзання та ручне тестування |
Опис
Існують численні випадки використання різних стилів великої літери (малий регістр верблюда, регістр змії тощо).
Пом'якшення
Перегляньте код на предмет стилю коду.
Додайте посібник із стилізації коду та застосуйте його за допомогою завдання CI.