Перейти к содержимому

Vbs-загрузчик с powershell, Aes, .net assembly и msbuild: разбор цепочки атакующих техник

Открыл обычный VBS, а внутри обнаружил PowerShell, AES и MSBuild

На первый взгляд файл с расширением `.vbs` выглядел совершенно обычно. Небольшой сценарий на VBScript, обращение к временной папке, запуск `cmd.exe` и PowerShell - ничего, что сразу указывало бы на сложную конструкцию. Первое впечатление было простым: очередной загрузчик, который выполняет несколько команд и получает основной компонент из сети.

Однако при внимательном изучении выяснилось, что скрипт не загружает содержимое извне. Он хранит все необходимые данные внутри самого себя, а затем последовательно распаковывает их, расшифровывает и передаёт дальше по цепочке.

VBS читает собственное содержимое

В начале сценария создаётся объект `WScript.Shell`, после чего определяется путь к каталогу `%TEMP%`. Там же присутствуют функции, генерирующие случайные имена файлов. Это распространённый приём: временные объекты получают непредсказуемые названия, из-за чего их сложнее обнаружить простыми правилами мониторинга.

Затем происходит более интересная операция - скрипт открывает собственный файл и считывает его содержимое. Внутри текста находится специальная метка `DATA:`. Всё, что расположено после неё, рассматривается как встроенный контейнер с дополнительными данными.

Содержимое очищается от переводов строк, а затем разделяется по другой маркерной последовательности - `[SPLIT]`. В результате образуются три фрагмента:

- два бинарных блока, которые временно сохраняются как файлы `.dat`;
- третий фрагмент, являющийся PowerShell-сценарием.

Таким образом, исходный VBS одновременно выполняет роль оболочки и архива. Ему не требуется отдельный архиватор: все компоненты уже находятся внутри файла и извлекаются непосредственно во время запуска.

PowerShell оказывается лишь следующим уровнем

Полученный PowerShell также не содержит готовую DLL или обычную строку Base64. Перед записью сценария в файл в него подставляются пути к созданным временным объектам. После этого формируется `.ps1`, который запускается скрыто - без отображения окна пользователю.

Сам PowerShell использует собственный формат представления данных. Сначала создаётся таблица символов, куда последовательно заносятся буквы латинского алфавита в верхнем и нижнем регистрах. Затем строка разбирается на блоки фиксированной длины - по 10 символов. Из каждого такого блока извлекаются байты.

Это не стандартная кодировка, а самодельный механизм преобразования текста в бинарные данные. Дополнительно в скрипте присутствуют два отдельных набора: один обозначен как DLL, второй - как payload. Для каждого из них применяются собственные ключи и векторы инициализации.

Несколько слоёв перед AES

Наиболее примечательной частью стала функция `_Expand`. Данные в ней проходят несколько последовательных преобразований:

1. выполняется операция XOR с IV;
2. производится вращение битов;
3. результат снова подвергается XOR с ключом;
4. после этого запускается расшифровка AES-CBC.

Иными словами, AES - только финальный этап. До него необходимо восстановить исходную последовательность байтов через нестандартное кодирование, две операции XOR и битовые преобразования.

После завершения всех процедур формируется массив `$b1`. Ожидаемо было бы увидеть сохранение данных на диск, например запись в DLL-файл с последующим запуском. Но этого не происходит.

Расшифрованные байты сразу загружаются в память как .NET Assembly. Физического файла библиотеки в системе не появляется. Затем PowerShell ищет нужный тип и вызывает его метод через Reflection. Такой подход уменьшает количество артефактов на диске и затрудняет расследование по стандартным журналам файловой системы.

Где здесь MSBuild

Следующий уровень цепочки связан с системным `MSBuild.exe`. В коде присутствует путь к исполняемому файлу MSBuild, а вторым параметром передаётся содержимое второго расшифрованного блока - payload.

В упрощённом виде последовательность выглядит так:

VBS → встроенные данные → PowerShell → самодельное декодирование → AES-CBC → .NET Assembly в памяти → MSBuild → payload

Сам по себе MSBuild - легитимный инструмент разработки и сборки проектов .NET. Именно поэтому его часто используют в подобных схемах: запуск системного компонента может выглядеть менее подозрительно, чем выполнение неизвестного стороннего бинарника.

Зачем нужна такая сложная конструкция

Технически автор мог реализовать всё значительно проще. Например, положить DLL рядом со скриптом, сохранить payload отдельным файлом, применить обычный Base64 или загрузить данные через стандартный сетевой cmdlet.

Но простая реализация быстро раскрывает намерения сценария. В данном случае аналитику приходится последовательно проходить несколько уровней:

- обнаружить чтение собственного файла;
- найти секцию `DATA:`;
- понять назначение маркера `[SPLIT]`;
- извлечь PowerShell;
- разобраться в функции `Read-StreamConfig`;
- восстановить алгоритм преобразования байтов;
- изучить `_Expand`;
- получить .NET-сборку;
- определить роль MSBuild.

Такая многоступенчатая структура рассчитана прежде всего на затруднение ручного анализа. При просмотре только верхнего уровня файл действительно может показаться обычным скриптом с временными файлами и командной строкой.

Проверки среды выполнения

В PowerShell обнаруживается и дополнительная логика, не связанная непосредственно с расшифровкой. Например, установлен таймер со значением `0x927C0`, что соответствует 600 000 миллисекундам, или десяти минутам. Если выполнение не завершится за это время, процесс принудительно прекращается.

Также проверяется языковой режим PowerShell. При работе не в `FullLanguage` сценарий завершает выполнение. Это может использоваться для отказа от запуска в ограниченных средах, песочницах или системах, где PowerShell работает с урезанными возможностями.

Подобные проверки говорят о том, что автор учитывал не только упаковку полезной нагрузки, но и особенности окружения. Скрипт пытается понять, находится ли он в подходящей системе, и не продолжает работу при неподходящих условиях.

Что особенно важно для анализа

Главная проблема таких образцов заключается в том, что анализировать их нужно не как один файл, а как последовательность преобразований. Если открыть только VBS, можно увидеть лишь оболочку. Если остановиться на извлечённом PowerShell, останется непонятным назначение бинарных блоков. А если не восстановить алгоритм `_Expand`, сама DLL будет выглядеть как набор случайных байтов.

Для безопасного исследования желательно работать в изолированной виртуальной машине, отключить доступ к рабочей сети и заранее включить журналирование PowerShell, создания процессов и загрузки сборок. Особое внимание следует уделять событиям, связанным с:

- запуском `wscript.exe` или `cscript.exe`;
- появлением PowerShell-процессов с отключённым окном;
- созданием файлов в `%TEMP%`;
- запуском `MSBuild.exe` из необычного родительского процесса;
- загрузкой .NET Assembly без соответствующего файла на диске.

Полезно также сравнивать содержимое исходного VBS с временными файлами и сохранять промежуточные результаты декодирования. При таком подходе каждый этап можно проверять отдельно, не пытаясь сразу восстановить всю цепочку целиком.

Итог

Перед нами не просто VBS-файл, а многоуровневый загрузчик, совмещающий несколько техник сокрытия:

- хранение данных внутри собственного тела;
- разделение содержимого на несколько компонентов;
- генерация случайных имён временных файлов;
- самодельное кодирование байтов;
- XOR, вращение битов и AES-CBC;
- загрузка .NET-сборки непосредственно в память;
- использование Reflection;
- запуск легитимного `MSBuild.exe`;
- контроль времени выполнения и языкового режима PowerShell.

Наиболее интересна здесь не отдельная технология, а их сочетание. Ни один этап сам по себе не является принципиально новым, однако вместе они образуют цепочку, которая заметно усложняет статический анализ и уменьшает количество очевидных следов на диске. Именно поэтому даже "обычный" файл `.vbs` не стоит считать безобидным только по внешнему виду: за несколькими строками VBScript может скрываться полноценная многоступенчатая .NET-инфраструктура.

Прокрутить вверх