Глибина чергової небезпечної уразливості Move мови
Раніше ми виявили серйозну уразливість Aptos Moveevm, і після глибокого дослідження ми виявили нову уразливість цілого числа. Процес активації цієї уразливості є ще більш цікавим, нижче ми детально проаналізуємо цю уразливість, а також представимо деякі базові знання про мову Move. Віримо, що завдяки поясненням у цій статті ви отримаєте глибше розуміння мови Move.
Як всім відомо, мова Move перевіряє кодові одиниці перед виконанням байт-коду. Процес перевірки складається з 4 етапів, і ця уразливість виникає на етапі reference_safety.
модуль reference_safety визначає функцію перенесення для перевірки посилальної безпеки суб'єкта процесу. Його перевірки включають підтвердження відсутності висячих посилань, безпечність доступу до змінних посилань, безпечність доступу до посилань на глобальне сховище тощо.
Функція входу, що посилається на перевірку безпеки, викликатиме analyze_function. У analyze_function функція перевіряє кожен базовий блок. Базовий блок - це послідовність коду, що не має інструкцій розгалуження, окрім входу та виходу.
Мова Move визначає основні блоки, обходячи байт-код, шукаючи всі інструкції розгалуження та послідовності інструкцій циклу. Типовий приклад основного блоку коду Move IR може містити 3 основні блоки, які визначаються інструкціями BrTrue, Branch та Ret.
Посилальна безпека в Move
Запозичуючи ідеї мови Rust, Move підтримує два типи посилань: незмінне посилання (&) та змінне посилання (&mut). Незмінне посилання використовується для читання даних зі структури, змінне посилання - для модифікації даних. Розумне використання типів посилань допомагає підтримувати безпеку та розпізнавати модулі читання.
Модуль безпеки посилань Move перевіряє байт-код інструкцій базових блоків функцій, щоб підтвердити, що всі операції посилання є законними. Процес перевірки в основному пов'язаний зі структурою AbstractState, яка містить граф позик і локальні змінні, щоб забезпечити безпеку посилань у функції.
Процес верифікації буде порівнювати стан до і після виконання базового блоку, об'єднуючи результати для оновлення стану блоку, водночас поширюючи післяумови цього блоку на наступні блоки. Цей процес подібний до ідеї Sea of Nodes у V8 turbofan.
Основний цикл виконує код блоку, а потім намагається об'єднати стан pre і post. Якщо стан змінюється і поточний блок має зворотний край, що вказує на себе ( вказує на наявність ) циклу, він перескакує назад на початок циклу і продовжує виконувати цей базовий блок до тих пір, поки стан запису не зрівняється зі станом перед або не буде перервано з помилкою.
Аналіз вразливостей
Вразливість виникає під час перевірки, чи змінився результат join. Функція join_ використовується для оновлення локальних змінних і графу відносин borrow. Коли довжина параметрів функції разом з довжиною локальних змінних перевищує 256, через те, що локальні змінні мають тип u8, під час обходу locals може статися переповнення.
Хоча Move має процес перевірки кількості локальних змінних, але в модулі перевірки меж лише перевіряються локальні змінні, без включення параметра length. Розробники, здається, усвідомлюють необхідність перевірки суми параметрів і локальних значень, але код насправді лише перевіряє кількість локальних змінних.
Від переповнення цілого числа до атаки типу DoS
Головний цикл буде сканувати блоки коду та викликати функцію execute_block, після чого об'єднає стан до і після виконання. Коли в коді є цикл, він буде повертатися на початок блоку коду і виконувати його знову.
Якщо ми створимо циклічний кодовий блок і використаємо переповнення для зміни стану блоку, щоб нова карта локальних змінних відрізнялася від попередньої, то при повторному виконанні execute_block аналіз послідовності інструкцій у базовому блоці звернеться до нової карти локальних змінних. У цей момент, якщо індекс, який потрібно отримати інструкцією, не існує в новій карті, це призведе до DoS.
У модулі reference safety коди операцій MoveLoc/CopyLoc/FreeRef можуть досягти цієї мети. Наприклад, функція copy_loc намагається отримати локальне значення через LocalIndex, якщо LocalIndex не існує, це призведе до паніки, внаслідок чого весь вузол впаде.
Демонстрація PoC
Ми можемо створити базовий блок, що містить інструкцію безумовного переходу, щоб кілька разів викликати функції execute_block та join. Встановивши відповідні параметри та кількість локальних змінних, можна після першого виконання зменшити довжину нової локальної карти до 8.
При другому виконанні спроба отримати доступ до неіснуючого offset призведе до panic, що викличе DoS.
Підсумок
Цей вразливість показує, що навіть у Move мові, яка пройшла сувору статичну перевірку, існують проблеми безпеки. Уразливість переповнення може обійти перевірки меж, підкреслюючи важливість аудиту коду.
Як лідери в дослідженні безпеки мови Move, ми рекомендуємо розробникам мови додати більше перевірок коду в середовище виконання Move, щоб запобігти виникненню непередбачених ситуацій. Наразі Move проводить основні перевірки безпеки на стадії верифікації, але на стадії виконання бракує достатнього зміцнення безпеки, що може призвести до більш серйозних проблем.
Ми також виявили ще одну вразливість мови Move, яку ми поділимося з вами пізніше.
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
18 лайків
Нагородити
18
5
Поділіться
Прокоментувати
0/400
ConsensusDissenter
· 08-05 10:34
Все ж таки небезпечно
Переглянути оригіналвідповісти на0
DefiPlaybook
· 08-05 10:31
Кроки верифікації потребують оптимізації
Переглянути оригіналвідповісти на0
MEVHunterWang
· 08-05 10:26
Хто розуміє уразливості, той заробляє великі гроші.
Аналіз нової вразливості переповнення цілого числа в мові Move: від безпеки посилань до атак DoS
Глибина чергової небезпечної уразливості Move мови
Раніше ми виявили серйозну уразливість Aptos Moveevm, і після глибокого дослідження ми виявили нову уразливість цілого числа. Процес активації цієї уразливості є ще більш цікавим, нижче ми детально проаналізуємо цю уразливість, а також представимо деякі базові знання про мову Move. Віримо, що завдяки поясненням у цій статті ви отримаєте глибше розуміння мови Move.
Як всім відомо, мова Move перевіряє кодові одиниці перед виконанням байт-коду. Процес перевірки складається з 4 етапів, і ця уразливість виникає на етапі reference_safety.
модуль reference_safety визначає функцію перенесення для перевірки посилальної безпеки суб'єкта процесу. Його перевірки включають підтвердження відсутності висячих посилань, безпечність доступу до змінних посилань, безпечність доступу до посилань на глобальне сховище тощо.
Функція входу, що посилається на перевірку безпеки, викликатиме analyze_function. У analyze_function функція перевіряє кожен базовий блок. Базовий блок - це послідовність коду, що не має інструкцій розгалуження, окрім входу та виходу.
Мова Move визначає основні блоки, обходячи байт-код, шукаючи всі інструкції розгалуження та послідовності інструкцій циклу. Типовий приклад основного блоку коду Move IR може містити 3 основні блоки, які визначаються інструкціями BrTrue, Branch та Ret.
Посилальна безпека в Move
Запозичуючи ідеї мови Rust, Move підтримує два типи посилань: незмінне посилання (&) та змінне посилання (&mut). Незмінне посилання використовується для читання даних зі структури, змінне посилання - для модифікації даних. Розумне використання типів посилань допомагає підтримувати безпеку та розпізнавати модулі читання.
Модуль безпеки посилань Move перевіряє байт-код інструкцій базових блоків функцій, щоб підтвердити, що всі операції посилання є законними. Процес перевірки в основному пов'язаний зі структурою AbstractState, яка містить граф позик і локальні змінні, щоб забезпечити безпеку посилань у функції.
Процес верифікації буде порівнювати стан до і після виконання базового блоку, об'єднуючи результати для оновлення стану блоку, водночас поширюючи післяумови цього блоку на наступні блоки. Цей процес подібний до ідеї Sea of Nodes у V8 turbofan.
Основний цикл виконує код блоку, а потім намагається об'єднати стан pre і post. Якщо стан змінюється і поточний блок має зворотний край, що вказує на себе ( вказує на наявність ) циклу, він перескакує назад на початок циклу і продовжує виконувати цей базовий блок до тих пір, поки стан запису не зрівняється зі станом перед або не буде перервано з помилкою.
Аналіз вразливостей
Вразливість виникає під час перевірки, чи змінився результат join. Функція join_ використовується для оновлення локальних змінних і графу відносин borrow. Коли довжина параметрів функції разом з довжиною локальних змінних перевищує 256, через те, що локальні змінні мають тип u8, під час обходу locals може статися переповнення.
Хоча Move має процес перевірки кількості локальних змінних, але в модулі перевірки меж лише перевіряються локальні змінні, без включення параметра length. Розробники, здається, усвідомлюють необхідність перевірки суми параметрів і локальних значень, але код насправді лише перевіряє кількість локальних змінних.
Від переповнення цілого числа до атаки типу DoS
Головний цикл буде сканувати блоки коду та викликати функцію execute_block, після чого об'єднає стан до і після виконання. Коли в коді є цикл, він буде повертатися на початок блоку коду і виконувати його знову.
Якщо ми створимо циклічний кодовий блок і використаємо переповнення для зміни стану блоку, щоб нова карта локальних змінних відрізнялася від попередньої, то при повторному виконанні execute_block аналіз послідовності інструкцій у базовому блоці звернеться до нової карти локальних змінних. У цей момент, якщо індекс, який потрібно отримати інструкцією, не існує в новій карті, це призведе до DoS.
У модулі reference safety коди операцій MoveLoc/CopyLoc/FreeRef можуть досягти цієї мети. Наприклад, функція copy_loc намагається отримати локальне значення через LocalIndex, якщо LocalIndex не існує, це призведе до паніки, внаслідок чого весь вузол впаде.
Демонстрація PoC
Ми можемо створити базовий блок, що містить інструкцію безумовного переходу, щоб кілька разів викликати функції execute_block та join. Встановивши відповідні параметри та кількість локальних змінних, можна після першого виконання зменшити довжину нової локальної карти до 8.
При другому виконанні спроба отримати доступ до неіснуючого offset призведе до panic, що викличе DoS.
Підсумок
Цей вразливість показує, що навіть у Move мові, яка пройшла сувору статичну перевірку, існують проблеми безпеки. Уразливість переповнення може обійти перевірки меж, підкреслюючи важливість аудиту коду.
Як лідери в дослідженні безпеки мови Move, ми рекомендуємо розробникам мови додати більше перевірок коду в середовище виконання Move, щоб запобігти виникненню непередбачених ситуацій. Наразі Move проводить основні перевірки безпеки на стадії верифікації, але на стадії виконання бракує достатнього зміцнення безпеки, що може призвести до більш серйозних проблем.
Ми також виявили ще одну вразливість мови Move, яку ми поділимося з вами пізніше.