Аналіз вразливостей компілятора Solidity та стратегії реагування
Компилятор є одним з основних компонентів сучасних комп'ютерних систем, його роль полягає в перетворенні вихідного коду на високорівневих мовах програмування на інструкційний код, який може виконувати комп'ютер. Більшість розробників і фахівців з безпеки зазвичай зосереджуються на безпеці кодів додатків, але часто ігнорують безпеку самого компілятора. Насправді, компілятор, як комп'ютерна програма, також може мати вразливості безпеки, які в певних ситуаціях можуть призвести до серйозних ризиків безпеки.
Наприклад, під час компіляції та виконання коду JavaScript браузер може через вразливість JavaScript-движка дозволити зловмисникам використовувати цю вразливість для віддаленого виконання коду, що в результаті призводить до контролю над браузером жертви або навіть операційною системою. Інше дослідження також показало, що помилки компілятора C++ можуть призвести до серйозних наслідків, таких як віддалене виконання коду.
Компілятор Solidity також має вразливості безпеки. Згідно з попередженнями про безпеку команди розробників Solidity, у кількох версіях компілятора Solidity існують вразливості безпеки. Функція компілятора Solidity полягає в перетворенні коду смарт-контракту на інструкційний код віртуальної машини Ethereum (EVM), який в кінцевому підсумку буде завантажено в Ethereum і виконано EVM.
Необхідно розмежувати вразливості компілятора Solidity та вразливості самого EVM. Вразливості EVM - це проблеми безпеки, які виникають під час виконання інструкцій віртуальної машини, що можуть вплинути на всю мережу Ethereum. Вразливості компілятора Solidity - це проблеми, що виникають під час перетворення Solidity в код EVM, які не впливають безпосередньо на мережу Ethereum, але можуть призвести до того, що згенерований код EVM не відповідатиме очікуванням розробника.
Одна з небезпек вразливості компілятора Solidity полягає в тому, що вона може призвести до несумісності згенерованого коду EVM з очікуваннями розробника смарт-контракту. Оскільки смарт-контракти зазвичай пов'язані з криптовалютними активами користувачів, будь-яка помилка, викликана компілятором, може призвести до втрати активів користувачів, що матиме серйозні наслідки.
Розробники та аудитори зазвичай зосереджуються на логіці контракту та поширених проблемах безпеки, тоді як вразливості компілятора важко виявити через аудит коду. Потрібно проводити спільний аналіз з урахуванням конкретної версії компілятора та моделей коду, щоб визначити, чи підлягає смарт-контракт впливу вразливостей компілятора.
Ось кілька реальних прикладів вразливостей компілятора Solidity:
SOL-2016-9 HighOrderByteCleanStorage
Вразливість існує в ранніх версіях компілятора Solidity (>=0.1.6 <0.4.4). Розгляньте наступний код:
солідність
контракт C {
uint32 a = 0x12345678;
uint32 b = 0;
функція f() публічна {
a = a + 1;
}
функція run() публічний перегляд повертає (uint32) {
повернути b;
}
}
Теоретично змінна b не була змінена, функція run() повинна повертати 0. Але в коді, згенерованому скомпільованим кодом уразливого компілятора, run() повертає 1. Це пов'язано з тим, що EVM використовує елементи стеку розміром 32 байти, тоді як Solidity підтримує менші типи даних, такі як uint32. Під час обробки компілятору потрібно очистити старші біти (clean up), але під час переповнення при додаванні це не було правильно оброблено, що призвело до того, що старший біт 1 був записаний у змінну b.
SOL-2022-4 InlineAssemblyMemoryПобічні ефекти
Ця уразливість існує в компіляторах версій від 0.8.13 до 0.8.15. Розгляньте наступний код:
солідність
контракт C {
функція f() публічна чиста повертає (uint) {
збірка {
mstore(0, 0x42)
}
uint x;
збірка {
x := mload(0)
}
повернути x;
}
}
Компилятор, щоб оптимізувати, видаляє на перший погляд непотрібні операції запису в пам'ять, але помилково також оптимізує доступ до пам'яті через блоки assembly. Це призводить до того, що функція f() повертає 0, а не правильне 0x42.
Цей вразливість впливає на компілятори версій від 0.5.8 до 0.8.16. Розгляньте наступний код:
солідність
контракт C {
функція f(string[1] calldata a) зовнішня чиста повертає (string пам'яті) {
повернути abi.decode(abi.encode(a), (строка[1]))[0];
}
}
У нормальних умовах ця функція повинна повертати введений рядок. Але в уразливій версії вона повертає порожній рядок. Це сталося через те, що компілятор неправильно очистив деякі дані під час кодування масиву calldata в форматі ABI, що призвело до невідповідності даних після кодування та декодування.
Щодо вразливостей компілятора Solidity, команда з безпеки блокчейну Cobo пропонує такі рекомендації:
Для розробників:
Використовуйте новішу версію компілятора Solidity, відомих проблем із безпекою зазвичай менше.
Поліпшити тестові випадки модулів. Більшість помилок на рівні компілятора призводять до того, що результати виконання коду не відповідають очікуванням, підвищення покриття тестами може запобігти таким проблемам.
Уникайте використання внутрішнього асемблера, складного кодування та декодування ABI тощо, не використовуйте нові функції та експериментальні можливості бездумно. Більшість вразливостей компіляторів пов'язані з цими складними операціями.
Для безпеки персоналу:
Під час аудиту не ігноруйте ризики безпеки, які можуть бути пов'язані з компілятором. Відповідний пункт перевірки: SWC-102: Застаріла версія компілятора.
У процесі розробки, слідкуйте за командою розробників, щоб оновити версію компілятора, можна впровадити автоматичну перевірку версії в CI/CD.
Не варто надмірно турбуватися про вразливості компілятора. Більшість вразливостей спрацьовують лише за певних кодових шаблонів, необхідно оцінити безпеку залежно від конкретного проекту.
Кілька корисних ресурсів:
Офіційне попередження безпеки Solidity:
Офіційний список помилок Solidity:
Список помилок компілятора для всіх версій:
Попереджувальний знак у правому верхньому куті сторінки контракту Etherscan може вказувати на наявність вразливостей безпеки у поточній версії компілятора
Отже, розробники та фахівці з безпеки повинні звертати увагу на потенційні ризики безпеки, які можуть виникнути через вразливості компілятора Solidity, вжити відповідних заходів для зниження ризиків і забезпечення безпеки смарт-контрактів.
Переглянути оригінал
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Аналіз вразливостей компілятора Solidity та практичні стратегії захисту смартконтрактів
Аналіз вразливостей компілятора Solidity та стратегії реагування
Компилятор є одним з основних компонентів сучасних комп'ютерних систем, його роль полягає в перетворенні вихідного коду на високорівневих мовах програмування на інструкційний код, який може виконувати комп'ютер. Більшість розробників і фахівців з безпеки зазвичай зосереджуються на безпеці кодів додатків, але часто ігнорують безпеку самого компілятора. Насправді, компілятор, як комп'ютерна програма, також може мати вразливості безпеки, які в певних ситуаціях можуть призвести до серйозних ризиків безпеки.
Наприклад, під час компіляції та виконання коду JavaScript браузер може через вразливість JavaScript-движка дозволити зловмисникам використовувати цю вразливість для віддаленого виконання коду, що в результаті призводить до контролю над браузером жертви або навіть операційною системою. Інше дослідження також показало, що помилки компілятора C++ можуть призвести до серйозних наслідків, таких як віддалене виконання коду.
Компілятор Solidity також має вразливості безпеки. Згідно з попередженнями про безпеку команди розробників Solidity, у кількох версіях компілятора Solidity існують вразливості безпеки. Функція компілятора Solidity полягає в перетворенні коду смарт-контракту на інструкційний код віртуальної машини Ethereum (EVM), який в кінцевому підсумку буде завантажено в Ethereum і виконано EVM.
Необхідно розмежувати вразливості компілятора Solidity та вразливості самого EVM. Вразливості EVM - це проблеми безпеки, які виникають під час виконання інструкцій віртуальної машини, що можуть вплинути на всю мережу Ethereum. Вразливості компілятора Solidity - це проблеми, що виникають під час перетворення Solidity в код EVM, які не впливають безпосередньо на мережу Ethereum, але можуть призвести до того, що згенерований код EVM не відповідатиме очікуванням розробника.
Одна з небезпек вразливості компілятора Solidity полягає в тому, що вона може призвести до несумісності згенерованого коду EVM з очікуваннями розробника смарт-контракту. Оскільки смарт-контракти зазвичай пов'язані з криптовалютними активами користувачів, будь-яка помилка, викликана компілятором, може призвести до втрати активів користувачів, що матиме серйозні наслідки.
Розробники та аудитори зазвичай зосереджуються на логіці контракту та поширених проблемах безпеки, тоді як вразливості компілятора важко виявити через аудит коду. Потрібно проводити спільний аналіз з урахуванням конкретної версії компілятора та моделей коду, щоб визначити, чи підлягає смарт-контракт впливу вразливостей компілятора.
Ось кілька реальних прикладів вразливостей компілятора Solidity:
Вразливість існує в ранніх версіях компілятора Solidity (>=0.1.6 <0.4.4). Розгляньте наступний код:
солідність контракт C { uint32 a = 0x12345678; uint32 b = 0; функція f() публічна { a = a + 1; } функція run() публічний перегляд повертає (uint32) { повернути b; } }
Теоретично змінна b не була змінена, функція run() повинна повертати 0. Але в коді, згенерованому скомпільованим кодом уразливого компілятора, run() повертає 1. Це пов'язано з тим, що EVM використовує елементи стеку розміром 32 байти, тоді як Solidity підтримує менші типи даних, такі як uint32. Під час обробки компілятору потрібно очистити старші біти (clean up), але під час переповнення при додаванні це не було правильно оброблено, що призвело до того, що старший біт 1 був записаний у змінну b.
Ця уразливість існує в компіляторах версій від 0.8.13 до 0.8.15. Розгляньте наступний код:
солідність контракт C { функція f() публічна чиста повертає (uint) { збірка { mstore(0, 0x42) } uint x; збірка { x := mload(0) } повернути x; } }
Компилятор, щоб оптимізувати, видаляє на перший погляд непотрібні операції запису в пам'ять, але помилково також оптимізує доступ до пам'яті через блоки assembly. Це призводить до того, що функція f() повертає 0, а не правильне 0x42.
Цей вразливість впливає на компілятори версій від 0.5.8 до 0.8.16. Розгляньте наступний код:
солідність контракт C { функція f(string[1] calldata a) зовнішня чиста повертає (string пам'яті) { повернути abi.decode(abi.encode(a), (строка[1]))[0]; } }
У нормальних умовах ця функція повинна повертати введений рядок. Але в уразливій версії вона повертає порожній рядок. Це сталося через те, що компілятор неправильно очистив деякі дані під час кодування масиву calldata в форматі ABI, що призвело до невідповідності даних після кодування та декодування.
Щодо вразливостей компілятора Solidity, команда з безпеки блокчейну Cobo пропонує такі рекомендації:
Для розробників:
Використовуйте новішу версію компілятора Solidity, відомих проблем із безпекою зазвичай менше.
Поліпшити тестові випадки модулів. Більшість помилок на рівні компілятора призводять до того, що результати виконання коду не відповідають очікуванням, підвищення покриття тестами може запобігти таким проблемам.
Уникайте використання внутрішнього асемблера, складного кодування та декодування ABI тощо, не використовуйте нові функції та експериментальні можливості бездумно. Більшість вразливостей компіляторів пов'язані з цими складними операціями.
Для безпеки персоналу:
Під час аудиту не ігноруйте ризики безпеки, які можуть бути пов'язані з компілятором. Відповідний пункт перевірки: SWC-102: Застаріла версія компілятора.
У процесі розробки, слідкуйте за командою розробників, щоб оновити версію компілятора, можна впровадити автоматичну перевірку версії в CI/CD.
Не варто надмірно турбуватися про вразливості компілятора. Більшість вразливостей спрацьовують лише за певних кодових шаблонів, необхідно оцінити безпеку залежно від конкретного проекту.
Кілька корисних ресурсів:
Отже, розробники та фахівці з безпеки повинні звертати увагу на потенційні ризики безпеки, які можуть виникнути через вразливості компілятора Solidity, вжити відповідних заходів для зниження ризиків і забезпечення безпеки смарт-контрактів.