Индустрия вредоносного программного обеспечения переживает заметную технологическую трансформацию. Злоумышленники активно осваивают современные компилируемые языки программирования, такие как Go (Golang), Nim и Rust, постепенно отказываясь от традиционных C и C++. Эта миграция создаёт новые сложности для специалистов по информационной безопасности, вынуждая их разрабатывать новые методики анализа и детектирования.
Описание
Язык Rust, долгое время остававшийся на периферии интересов создателей вредоносного ПО, сейчас набирает популярность. Его использование пока не столь массово, как Golang, но динамика роста очевидна. В дикой природе уже были замечены такие образцы, как вымогатель BlackCat (ransomware) и открытый стейлер (информационный вор) Luca Stealer. Последний, будучи опубликованным в открытом доступе, предоставил аналитикам уникальную возможность изучить особенности реализации вредоносного кода на Rust.
Особенности анализа Rust-бинарников
Одной из ключевых задач при обратном инженерринге любого скомпилированного приложения является поиск точки входа - функции "main". В случае с Rust этот процесс имеет свою специфику. Стандартный рантайм языка в конечном счёте вызывает функцию "std::rt::lang_start_internal", передавая ей адрес пользовательской функции "main" в качестве аргумента. Именно этот вызов является основным маркером для анализаторов вроде IDA, Ghidra или Binary Ninja.
Однако версии компилятора Rust и настройки сборки могут влиять на итоговый машинный код. Более того, нестабильный ABI (Application Binary Interface) языка означает, что паттерны вызовов могут меняться от версии к версии. Это усложняет создание универсальных сигнатур для систем обнаружения вторжений (IDS) и предотвращения вторжений (IPS).
Артефакты сборки и утечка информации
Анализ статичных строк в бинарнике часто даёт первую информацию о возможностях программы. Компилятор Rust встраивает в исполняемый файл множество служебных строк, включая пути к исходным файлам зависимостей - внешних библиотек, называемых "крейтами" (crates). Система сборки Cargo, аналогичная NPM для Node.js, автоматически подтягивает и статически линкует указанные крейты.
Строки, содержащие фрагменты путей вроде "cargo\registry" или "github-", могут указывать на используемые библиотеки. Например, наличие крейта "reqwest" сразу намекает на сетевую функциональность, так как эта библиотека предназначена для выполнения HTTP-запросов. Иногда в строке отладочной информации (PDB-файла) может остаться путь к директории пользователя на компьютере сборщика, что потенциально раскрывает часть его окружения.
Сложности для обратного инженерринга
Rust был создан как безопасный с точки зрения работы с памятью язык. Компилятор активно встраивает в код проверки границ массивов и другие механизмы безопасности, которые прерывают выполнение программы (вызывают "panic") при попытке небезопасной операции. С одной стороны, это снижает количество уязвимостей. С другой - значительно увеличивает объём и сложность генерируемого ассемблерного кода по сравнению с эквивалентной программой на C.
Ещё одна особенность - строки в Rust не обязательно завершаются нуль-терминатором. Это может сбивать с толку инструменты статического анализа, ожидающие стандартные для C строки. В результате строковые данные в бинарнике могут накладываться друг на друга, требуя от аналитика ручной корректировки в дизассемблере.
Гибкость языка также играет на руку злоумышленникам. Rust позволяет писать код низкого уровня, работать с указателями, встраивать ассемблер и даже компилировать программы без стандартной библиотеки. Это открывает широкие возможности для обфускации кода и реализации сложных методов устойчивости (persistence), что затрудняет как ручной анализ, так и автоматическую детекцию.
Выводы для сообщества безопасности
Растущая популярность Rust среди разработчиков вредоносного ПО - это устойчивый тренд. Язык предлагает баланс между высокой производительностью, безопасностью и возможностями низкоуровневого управления, что привлекательно для создателей сложных угроз, таких как APT (Advanced Persistent Threat) группы или ransomware.
Ландшафт детектирования Rust-вредоносов пока незрел. Сигнатуры и эвристики антивирусных движков, а также правила для SIEM (Security Information and Event Management) и SOC (Security Operations Center), лучше адаптированы под традиционные языки. Следовательно, у злоумышленников есть окно возможности для относительно лёгкого обхода защит.
Борьба с этой тенденцией требует от защитников накопления экспертизы. Критически важно развивать навыки статического и динамического анализа Rust-кода, а также делиться наработками и инструментами, как это сделала компания Binary Defense, опубликовав скрипт для Ghidra. Только коллективные усилия исследовательского сообщества позволят сократить отставание и эффективно противостоять новой волне угроз, написанных на современных языках программирования.
Индикаторы компрометации
MD5
- 0be6fcda8467300d7f5b35c77c9af344
SHA1
- b38385bfc52eacf2e96f7a919b64f6eb7c1224e0
SHA256
- 8f47d1e39242ee4b528fcb6eb1a89983c27854bac57bc4a15597b37b7edf34a6