В апреле наша система обнаружения уязвимостей выявила проблему в 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



