Back to Blog

Как была обнаружена и своевременно исправлена критическая уязвимость в сети Solana

Code Auditing
June 7, 2022
5 min read

В апреле наша система обнаружения уязвимостей выявила проблему в rBPF от Solana (т.е. в виртуальной машине, на которой работают все dApps Solana: https://github.com/solana-labs/rbpf). После тщательного расследования мы обнаружили, что это уязвимость в системе безопасности, которая может привести к неверному пути исполнения контракта. Мы сообщили об ошибке команде безопасности Solana, и команда немедленно подтвердила и устранила уязвимость. Кроме того, наша команда получила вознаграждение в размере 800 000 долларов США в токенах SOL.

Уязвимость существует в новых версиях rBPF (от 0.2.26 до 0.2.27). К моменту, когда мы сообщили о проблеме, валидаторы, используемые в основной сети (mainnet), еще не были обновлены до затронутых версий. Наша система обнаружила проблему до того, как затронутая версия была объединена (merged), что сделало валидаторы основной сети невосприимчивыми к этой уязвимости.

Ниже мы подробно описываем детали этой уязвимости.

1. eBPF и rBPF

eBPF (Extended Berkeley Packet Filter) изначально был разработан для фильтрации пакетов в ядре. Благодаря безопасности, эффективности и масштабируемости eBPF, сейчас он используется в различных областях, таких как сети, трассировка, профилирование и т.д.. Учитывая широкие возможности eBPF, Solana использует его в качестве движка выполнения для смарт-контрактов. Для создания dApps на Solana разработчики пишут свои смарт-контракты на Rust и компилируют их в байт-код eBPF.

Solana использует rBPF, виртуальную машину, написанную на Rust, для выполнения скомпилированного байт-кода BPF. Однако неизвестно, является ли предложенная виртуальная машина (т.е. rBPF) надежной, безопасной и точной. Если внутри rBPF существуют проблемы с безопасностью, это может затронуть все валидаторы, содержащие rBPF, что приведет к огромным потерям (например, потере средств) для всей сети Solana.

2. Первопричина

Мы разработали инструмент, который может автоматически находить ошибки реализации rBPF и периодически сканировать код rBPF. В процессе сканирования в rBPF (версия 0.2.26) была выявлена одна серьезная проблема, которая может привести к неверному пути исполнения контракта.

В частности, инструкция sdiv используется как инструкция знакового деления, представленная как функция, включенная по умолчанию в rbpf 0.2.26. sdiv поддерживает деление операндов как в 32 битах (т.е. sdiv32), так и в 64 битах (sdiv64). Для инструкции sdiv32 результат вычисления сохраняется в регистр bpf, который является 64-битным. Однако если инструкции, следующие за sdiv32, считывают результат вычисления как 64-битный, результат может отличаться. Это связано с тем, что rBPF при JIT-компиляции не расширяет знак результата вычисления sdiv32 до правильного 64-битного значения.

Например, если положительное число (т.е. 12) делится на отрицательное число (т.е. -4) с помощью sdiv32, правильным результатом должно быть -3 как в 32 битах, так и в 64 битах. Ниже приведен пример кода.

После запуска и трассировки в режимах JIT и интерпретации мы смогли наблюдать различия между ними:

2.1 Режим интерпретации (Interpreted Mode)

0 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    29: lddw r5, 0x10000000c
 1 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 000000010000000C, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    31: sdiv32 r5, -4
 2 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, FFFFFFFFFFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    32: jslt r5, 0, lbb_7
 3 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, FFFFFFFFFFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    36: exit

2.2 Режим JIT (JIT Mode)

0 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    29: lddw r5, 0x10000000c
 1 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 000000010000000C, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    31: sdiv32 r5, -4
 2 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 00000000FFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    32: jslt r5, 0, lbb_7
 3 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 00000000FFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    33: lddw r0, 0x1
 4 [0000000000000001, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 00000000FFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    35: exit

В режиме интерпретации регистр r5 устанавливается в 0xFFFFFFFFFFFFFFFD (-3 как в 32-битной, так и в 64-битной форме), тогда как в режиме JIT r5 устанавливается в 0x00000000FFFFFFFD. В этом случае r5 будет распознан как положительное число (т.е. 0x00000000FFFFFFFD) для инструкции jslt, которая получает 64-битное значение. После этого путь выполнения будет полностью неверным.

3. Влияние

Эта неправильная реализация может привести к неверному пути исполнения контракта и вызвать серьезные проблемы.

Например, если важная операция в смарт-контракте опирается на результат инструкции sdiv32, это может привести к неверным результатам выполнения и быть использовано злоумышленниками.

Эта проблема была введена в https://github.com/solana-labs/rbpf/pull/283, что означает, что rBPF был уязвим начиная с версии 0.2.26. Мы выявили проблему и сообщили о ней команде безопасности Solana 28 апреля 2022 года. Команда оперативно ответила на наш отчет и за несколько часов исправила проблему, добавив операцию расширения знака для инструкции sdiv32. Исправление находится по адресу https://github.com/solana-labs/rbpf/pull/310. Благодаря своевременному обнаружению и отчету нашей команды, валидаторы основной сети не пострадали от этой уязвимости.

Эта проблема была классифицирована как ошибка жизнеспособности протокола (protocol liveness bug), что привело к выплате вознаграждения в размере 800 000 долларов США от Solana.

Хронология

  • 28.04.2022: Мы сообщили о проблеме команде безопасности Solana
  • 29.04.2022: Уязвимость была устранена
  • 09.05.2022: Был назначен CVE-2022-23066
  • 01.06.2022: Было предоставлено вознаграждение за обнаружение ошибки

О BlockSec

Команда BlockSec фокусируется на безопасности блокчейн-экосистемы и сотрудничает с ведущими DeFi-проектами для защиты их продуктов. Команда основана первоклассными исследователями безопасности и опытными экспертами как из академической среды, так и из индустрии. Они опубликовали множество работ по безопасности блокчейна на престижных конференциях, сообщили о нескольких атаках нулевого дня на DeFi-приложения и выпустили подробные отчеты об анализе инцидентов безопасности с высоким уровнем воздействия.

Twitter: [BlockSecTeam]

Medium: https://blocksecteam.medium.com

Веб-сайт: https://www.blocksec.com

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit