Обход AMSI
Очень познавательная статья с Хакера:
И это ответ тем, кто считает что скриптами нельзя делать низкоуровневые вещи, можно всё при должном умении и знании...)
Казалось бы, макровирусы давно и безвозвратно ушли в прошлое. Уж что‑что, а вредоносные макросы в документах Office современные антивирусные программы должны обнаруживать легко и непринужденно. Именно так и обстоят дела, если макрос, конечно, не обфусцирован. Эффективными приемами обхода антивирусного детекта зловредных VBA-макросов поделился в своей публикации независимый исследователь Брендан Ортиз, а мы расскажем о его изысканиях тебе.
Готовясь к сертификации OSEP, Брендан Ортиз наверняка выпил ведро кофе и перелопатил гигабайты технической документации. Насчет кофе мы не уверены, а вот то, что результатом этой подготовки стало
, не подлежит никакому сомнению. Брендан убедился, что на сайте
(альтернатива
) оригинальный файл обнаруживался как минимум 7 антивирусными движками из 20. Тогда он всерьез взялся за обфускацию своих художеств и смог в итоге сбить показатель детектирования до 2 из 20. Поскольку антивирусные базы периодически обновляются, со временем этот параметр вырос с 2 до 5. Но все равно — результат получился впечатляющий.
Проверка эмуляции
Многие антивирусы, использующие для детектирования вредоносов эвристику, запускают VBA-скрипты в «песочнице» с целью убедиться в их безопасности. Потому настоящие вирусописатели в первую очередь проверяют, работает ли их сценарий в эмулированной среде, и, если так — останавливают выполнение вредоносного пейлоада, чтобы антивирус не забил тревогу.
Брендан Ортиз предложил три простых теста для определения эмулятора. Эти тесты располагаются в самом начале созданного им макроса и не позволяют ему выполнить никаких зловредных действий, если обнаружится, что скрипт запущен в изолированной среде.
Первый тест исследователь назвал «Document Name». Во многих случаях, когда антивирусный движок эмулирует выполнение макроса VBA, он меняет имя документа либо добавляет к этому имени некоторое число, чтобы отследить и предотвратить многократный запуск скрипта. Этот тест проверяет, совпадает ли с именем оригинального документа имя того документа, в котором выполняется полезная нагрузка.
Чтобы зашифровать статическую строку, в которой хранится имя документа, используется сценарий PowerShell, а затем во время выполнения скрипта для ее расшифровки вызывается собственная функция Joy. Если имя активного документа не совпадает с указанным именем, проверка считается непройденной и выполняется выход из подпрограммы.
На втором этапе проверяется путь. Если мы заранее знаем, из какой папки будет открыт документ с вредоносным сценарием (например, из папки «Загрузки» пользователя Windows), то мы можем сравнить ее с текущим путем. Несовпадение пути укажет на то, что скрипт, скорее всего, работает в антивирусном движке. В этом случае мы также выходим из подпрограммы.
Наконец, простой тест на время. Когда скрипт выполняется в эмуляторе антивируса, тот обычно пропускает фрагменты кода, в которых реализована пауза или «засыпание», иначе любящие «поспать» макровирусы попросту завесят движок. Поэтому Брендан предложил следующую нехитрую проверку: фиксируем актуальное время, «спим» две секунды, а затем просыпаемся и вновь фиксируем время. Если разница между двумя этими значениями составит менее 2 секунд, скорее всего, мы в антивирусном движке. В этом случае подпрограмма также завершается.
Если все три проверки завершились полным и безоговорочным успехом, макрос переходит к выполнению полезной нагрузки, которая выглядит примерно так, как показано на следующем рисунке.
Несколько слов об AMSI
В ходе своих экспериментов Брендан Ортиз выяснил, что поначалу его вредоносный макрос успешно детектировался эвристикой большинства антивирусных движков. Кроме того, несмотря на многочисленные попытки исследователя запустить макрос при включенной защите Windows Defender, система убивала скрипт. По какой же причине макрос помечался как вредоносный? Все дело в том, что интерфейс
заглядывает в сценарий VBA и контролирует его поведение.
AMSI — это разработанный Microsoft интерфейс, который приспособлен к работе с любыми антивирусами и позволяет им запрашивать информацию о процессах, происходящих во время исполнения программ. Это значит, что антивирусу передаются даже бесфайловые угрозы, например команды PowerShell, если антивирус вызывает AMSI. Еще это означает, что ты можешь как угодно обфусцировать полезную нагрузку, но как только она деобфусцируется во время выполнения, AMSI начнет отслеживать поведение кода.
Когда пользователь пытается выполнить команду в PowerShell, AMSI сначала загружает, а затем проверяет эту команду. Если обнаруживаются какие‑либо элементы, которые обычно используются для вредоносных действий, в частности, вызовы API Win32 или COM (то есть, срабатывают заложенные в AMSI «триггеры»), то AMSI приостанавливает подозрительный процесс.
На картинке ниже показано, как VBA интегрируется с AMSI. С учетом всего этого исследователь стал искать способы ускользнуть от пристального внимания AMSI при выполнении скриптов VBA и PowerShell.
Импорт Windows API в VBA
Начать Брендан решил с импорта функций. Чтобы пропатчить AMSI в памяти, необходим доступ к некоторым низкоуровневым библиотекам и функциям Windows. В VBA разрешено импортировать API Windows для использования в макросе. Такая возможность радикально расширяет функционал VBA.
Злоумышленник, вызывая определенные API Windows из VBA, C#, PowerShell и т. д., должен хорошенько в них разобраться, чтобы все сделать правильно. Дело в том, что нативные функции для этих целей в соответствующих языках отсутствуют. Но вооружившись некоторыми «подготовительными знаниями», заполнить эти пробелы довольно легко. Когда ты знаешь, какие API Windows хочешь импортировать, для начала обязательно погугли соответствующую документацию Microsoft. Там ты найдешь подробную информацию об интересующей тебя функции, в частности, каковы ее возвращаемые значения, какие параметры она принимает, в какой DLL находится интересующий тебя API и т. д. Так ты получишь по‑настоящему хорошее представление о событиях, происходящих «под капотом» скрипта.
Брендан использовал в своем макросе объявление PInvoke (Platform-Invoke, «Платформенный вызов»). PInvoke – это коллекция определений для вызова нативных функций API Windows из языков программирования, в которых может отсутствовать такой низкоуровневый функционал. Особенности работы этого инструмента подробно описаны
. Там можно найти декларации для импорта API в различные сценарии на все случаи жизни.
Обход AMSI
Импорт необходимых API Windows в VBA — это только первый шаг. Чтобы придуманный Бренданом Ортизом скрипт работал, нужно обойти AMSI. Для этого исследователь решил пропатчить AMSI прямо в памяти. А конкретнее, пропатчить первые несколько байтов в функциях AmsiScanBuffer и AmsiScanString из библиотеки Amsi.dll, загружаемой из запущенного процесса. Эти функции, если верить документации Microsoft, отвечают за сканирование содержимого буфера в поисках характерных для малвари строк.
Сначала объявляется список указателей переменных для хранения адресов функций. Затем в переменной lib сохраняется адрес из библиотеки amsi.dll, это делается при помощи направляемого к API Windows вызова LoadLib. Все строки в этом патче памяти AMSI должны быть обфусцированы. На первом этапе автор скрипта использовал для этого функцию VBA Chr(), чтобы скрыть некоторые характерные строки вроде amsi.dLl и AmsiUacInitialize.
Затем нужно найти, где лежит функция AmsiScanString. Для этого Брендан использовал функцию Windows API GetProcAddress, переименованную в GetPrAddr. Поскольку AMSI еще не пропатчен, а вредоносы часто пытаются отключить его, нацеливаясь на функции AmsiScanString и AmsiScanBuffer, автор скрипта воспользовался относительной адресацией начиная с функции AmsiUacInitialize.
Для начала Брендан вычел 96 из адреса функции AmsiUacInitialize и сохранил результат в переменной func_addr. Затем вызвал GetPrAddr со строками‑аргументами, первая из которых – это адрес amsi.dll, а вторая – строка AmsiUacInitialize, к которой применена обфускация. Зачем вычитать 96? Всё очень просто: Брендан прошелся по процессу Microsoft Word отладчиком WinDbg и выяснил, что адрес функции AmsiScanString отстоит на 96 байтов от функции AmsiUacInitialize.
Если просмотреть в WinDbg содержимое загруженной в память библиотеки AMSI.dll начиная с функции AmsiScanBuffer, расположенной по адресу 0x100 в шестнадцатеричной системе (256 байтов в десятичной), то можно увидеть, как выглядит верхняя часть вывода.
Стрелка указывает на первые опкоды в функции AmsiScanBuffer, а слева показан ассоциированный с этой инструкцией адрес в памяти: 0x00007ffa054f35e0. Следовательно, функция AmsiScanBuffer начинается именно в этой точке. Изучив вывод далее, можно найти следующую функцию — AmsiScanString, и выяснить ее адрес: 0x00007ffa054f36e0.
Далее Брендан отыскал в выводе отладчика значение переменной func_addr, которую загрузил в свой макрос VBA при помощи функции GetPrAddr. В этой переменной находится адрес функции AmsiUacInitialize: 0x00007ffa054f3740.
Все, что осталось сделать автору скрипта, — это просто вычесть из адреса функции AmsiUacInitialize адреса функций AmsiScanString и AmsiScanBuffer. Так будут получены относительные адреса последних.
0x054f3740 (AmsiUacInitialize Start Address) - 0x054f36e0 (AmsiScanString Start Address) = 0x60 = 96 bytes
0x054f3740 (AmsiUacInitialize Start Address) - 0x54f35e0 (AmsiScanBuffer Start Address) = 0x160 = 352 bytes
Следующая часть созданного Бренданом Ортизом макроса меняет защитные механизмы памяти и порядок доступа к ней целевых функций. Это делается при помощи API-функции VirtualProtect, которую автор переименовал в VirtPro.
Функция VirtualProtect меняет характеристики защиты в области виртуальной памяти, которая относится к адресному пространству вызывающего процесса. С ее помощью можно изменить показатели защиты для конкретного участка памяти (с атрибутами «только для чтения» или «для исполнения»), в результате чего появляется возможность редактировать расположенные там инструкции.
VirtualProtect принимает следующие параметры: адрес того места, которое нужно отредактировать (это адрес интересующей нас функции), размер области памяти, которую требуется редактировать (32 байта от начала адреса), уровни защиты памяти, которые требуется обеспечить (передается в виде значения, которое будет обрабатываться при помощи побитовой операции AND). Наконец, в сценарии содержится переменная flOldProtection. Функция сохраняет в эту переменную старое значение параметров защиты памяти – для последующего использования.
Получив доступ к чтению и записи тех областей памяти, что предназначены только для записи, Брендан перешел непосредственно к пропатчиванию AMSI. Для этого он использовал WinAPI-функцию RtlFillMemory. Такие функции, как RtlMoveMemory, часто используются вредоносами, поэтому легко обнаруживаются эвристикой антивирусов. А вот RtlFillMemory – не самый частотный вариант, из‑за чего ее могут и не сэмулировать движки, работа которых основана на эвристике. Да и при статическом анализе антивирусы далеко не всегда обращают на нее внимание. Брендан переименовал эту функцию в patcher.
В скрипте выполняется вызов функции RtlFillMemory по псевдониму patcher, ей передаются адреса функций AmsiScanString и AmsiScanBuffer. Затем нужно указать, сколько байтов требуется заполнить, и, наконец, передать шестнадцатеричное значение кода операции, которое требуется туда добавить: 0x90 — это код NOP, он ничего не делает. Затем выполняется то же действие, но с увеличением адреса на 1, при этом функции передается код операции return, который равен 0xc3.
В результате в начале функции AmsiScanString будет поставлен неоперационный код 0x90, а после него – код возврата 0xC3, и эта функция просто завершится сразу после того, как будет вызвана, что позволит обойти AMSI в VBA. Вот как выглядит выполнение функции AmsiScanString после применения патча в отладчике WinDbg.
Все, что остается – повторить весь процесс для функции AmsiScanBuffer, смещение для которой уже известно. Тут стоит отметить, что есть и более изощренные способы сделать это, например, динамически искать начальные байты каждой функции, как только адрес Amsi.dll будет загружен в память. Кроме того, есть некоторые отличия в процессе патча библиотеки в 32-разрядной и 64-разрядной версиях Word.
На некоторых сайтах можно встретить описания способов обхода AMSI на VBA с использованием совершенно иных, альтернативных методов, не требующих взаимодействия с API Windows. Правда, некоторые из них требуют записывать на диск файлы и с этим можно влипнуть. Примеры на эту тему можно найти в следующем
.
Обфускация строк
Поскольку созданный Бренданом Ортизом VBA-скрипт использует WMI-объект для порождения процесса PowerShell, он неизбежно привлечет повышенное внимание любого антивирусного движка. Наиболее очевидным решением этой проблемы является шифрование статических строк внутри макроса.
Например, при создании объекта WMI используются такие строки, как winmgmts:, Win32_ProcessStartup, Win32_Process и им подобные. При статическом сканировании макроса эти строки будут идеальными мишенями для антивируса, он просто неизбежно пометит их как вредоносные. Брендан использовал следующий метод обфускации.
Он объявил переменную, в которой будут сохраняться статические строки, присутствующие в макросе VBA. Затем он объявил выходную переменную, в которой будет содержаться зашифрованная строка. После этого он преобразовал полезную нагрузку из строковой переменной в символьный массив. Затем десятичное значение каждого символа суммируется со значением 26. Далее он забил символ дополнительными нулями, чтобы позже, в процессе деобфускации VBA, получились предсказуемые 3-символьные значения. Если длина вывода составляет 1 символ, то к нему добавляется два нуля, если длина – два символа, добавляется один ноль, а если три символа, ничего не добавляется.
Таким образом формируется зашифрованная выходная строка, которая отправляется в буфер обмена через конвейер. После запуска скрипта в буфер обмена помещается строка примерно следующего содержания.
Деобфусцирующий макрос в VBA выглядит примерно так.
В скрипте создаются подпрограммы со случайными именами, которые не выдадут антивирусному сканеру, что процедура может быть вредоносной, а также запутают любого аналитика, которому попадет в руки такой макрос. В VBA подпрограмма вызывается при помощи инструкции, которая имеет примерно следующий вид.
Сначала зашифрованная полезная нагрузка поступает в подпрограмму Joy и запускает цикл Do While. Затем она отправляется в подпрограмму Man, извлекающую первые 3 числа (вот зачем понадобилось заполнение нулями), после чего передает эти символы функции bread. Она вычтет 26 из 3 символов, чтобы получить конкретное дешифрованное число. Это число является ASCII-эквивалентом символа и сохраняется в переменной green.
Далее вся зашифрованная полезная нагрузка отправляется функции rand, которая удаляет из нее первые три символа и сохраняет их обратно в переменную paper. Затем процесс повторяется до тех пор, пока вся полезная нагрузка не редуцируется до нуля.
Наконец, дешифрованная полезная нагрузка возвращается вызывающей процедуре при помощи инструкции Joy = green.
Выполнение PowerShell при помощи WMI
Далее необходимо запустить экземпляр процесса PowerShell при помощи WMI. Для этого создается объект WMI посредством вызова функции VBA GetObject с зашифрованной строкой winmgmts:, результат сохраняется в переменной ObjWMIService.
После этого вызывается функция Get из объекта WMI Service с зашифрованной версией строки Win32_ProcessStartup и сохраняется в переменную objStartup. Это позволяет задать параметры запуска для создаваемого скриптом процесса PowerShell. Следующие действия направлены на то, чтобы спрятать окно PowerShell (для этого значение переменной objConfig.showWindow устанавливается в 0) — пользователь даже не узнает, что в фоновом режиме выполняется какой‑то скрипт.
Созданному процессу передаются заранее сформированные параметры и настройки в виде нескольких сцепленных строк. Весь ход создания объекта WMIObject выглядит примерно так.
Полезная нагрузка PowerShell
Прежде чем запустить вредоносную команду PowerShell, необходимо отключить AMSI. Поскольку инстанс PowerShell порождается в отдельном процессе, здесь возникает загвоздка с еще одним случаем интеграции AMSI, который придется обойти прежде, чем скрипт начнет какую‑либо вредоносную активность.
Работа макроса начинается с отключения AMSI в VBA, чтобы во время исполнения система не пометила содержимое макроса как вредоносное.
Для этого объявляются три разные строки — str1,str2 и str3, а затем им передается в качестве значения полезная нагрузка, которую нужно прогнать через объект WMI (процесс создания этого объекта был описан выше). Это сделано ради удобочитаемости и пригодности к отладке: разделив полезную нагрузку на фрагменты, можно выявить, где именно она прекращает выполняться.
Первая строка (str1) получает в качестве нагрузки зашифрованную версию кода:
Powershell.exe -ep bypass -nop -c
Если не удается создать процесс PowerShell при выполнении макроса, то уже известно, что проблема заключается в первом зашифрованном фрагменте.
Вторая строка (str2) получает в качестве нагрузки следующий зашифрованный код:
Эта строка – зашифрованный обход AMSI. Шифрование выполнено по основанию 64, так как при переводе с VBA на PowerShell могут возникнуть проблемы при передаче специальных символов. В декодированном виде обход укладывается в одну строку, а в несвернутом выглядит примерно так.
Если будет получено уведомление Microsoft Defender о блокировке скрипта на данном этапе, станет очевидно, что обход не удался. Если не удастся установить соединение с веб‑сервером для скачивания полезной нагрузки, но сам скрипт PowerShell запустится успешно, это также будет означать, что проблема возникла именно здесь.
Третья строка (str3) получает в качестве нагрузки такой код:
Так извлекается первая часть полезной нагрузки, загрузчик шелл‑кода, который будет загружать байт‑код со стадии 2 и выполнять его непосредственно в памяти.
Загрузчик шелл-кода
После запуска из PowerShell скрипт скачивает сгенерированный шелл‑код, а затем, воспользовавшись рефлексией, загружает функции Windows API, эквивалентные VirtualAlloc, CreateThread и WaitForSingleObject.
Сгенерированный шелл‑код использует загруженные API Windows, чтобы выполнить код в текущем процессе PowerShell. Это и называется рефлексией, в ходе нее функции создаются и загружаются в память. Есть причина, по которой нужно действовать именно так, а не использовать, например,
из арсенала PowerShell.
Дело в том, что Add-Type создает временный файл и сохраняет его на диск. Такой файл – это артефакт, по которому вирусный аналитик может догадаться, что в системе происходит что‑то нехорошее. Кроме того, такой файл может быть замечен и просканирован антивирусными движками, когда он уже сохранен на диск.
В итоге, созданный Бренданом Ортизом скрипт принял следующий вид:
При исполнении шелл‑код выполняет обратный вызов к серверу C2, а затем возвращает управление шелл‑коду.
Стомпинг VBA
Остается последний этап — стомпинг VBA. Это акт удаления частично сжатой версии исходного кода VBA, сохраненного в документе – так, что остается только предварительно скомпилированный код. Когда документ открывают на целевой машине в той же версии Word, для которой создавался скрипт, код VBA не компилируется заново — вместо этого выполняется сохраненный макрос VBA.
Таким образом, если нам заблаговременно известно, с какой версией VBA будет выполняться наш макрос, мы можем удалить из документа исходный код и оставить только скомпилированный. Благодаря этому радикально сужается возможность анализа кода и снижается вероятность его обнаружения антивирусом.
Для такой работы особенно хорош
, который даже позволяет заменить нескомпилированный макрос VBA безвредной версией VBA-кода. Таким образом, ты можешь заменить вредоносный макрос на код, который пишет "Hello World!". Когда документ будет открыт, выполнится и вредоносный макрос, если версия Office – именно та, для которой он компилировался. Все вышеперечисленное EvilClippy может выполнить при помощи единственной строки кода, выглядящей примерно так:
В результате выполнения команды в папке с исходным файлом создается новый документ, к имени которого добавлена строка _EvilClippy.
Обрати внимание, насколько два этих файла отличаются по размеру. Теперь остается только загрузить вредоносный документ на
и посмотреть, сколько антивирусных движков распознают его как вредоносный. В данном случае обязательно пользоваться antiscan.me, а не VirusTotal, поскольку первый из этих двух сайтов не передает вредоносную нагрузку антивирусным компаниям для ее добавления в базы сигнатур.
Выводы
А вот и результат!
Усилия вознаграждены. Угрозу обнаружили всего 5 из 26 антивирусных движков, и автору скрипта удалось ускользнуть от самых распространенных, таких как Windows Defender, Sophos, Kaspersky и McAfee. С помощью примененных им методов скрипту удалось отключить AMSI и выполнить вредоносный VBA-скрипт на машине, защищенной Windows Defender.
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
И это ответ тем, кто считает что скриптами нельзя делать низкоуровневые вещи, можно всё при должном умении и знании...)
Казалось бы, макровирусы давно и безвозвратно ушли в прошлое. Уж что‑что, а вредоносные макросы в документах Office современные антивирусные программы должны обнаруживать легко и непринужденно. Именно так и обстоят дела, если макрос, конечно, не обфусцирован. Эффективными приемами обхода антивирусного детекта зловредных VBA-макросов поделился в своей публикации независимый исследователь Брендан Ортиз, а мы расскажем о его изысканиях тебе.
Готовясь к сертификации OSEP, Брендан Ортиз наверняка выпил ведро кофе и перелопатил гигабайты технической документации. Насчет кофе мы не уверены, а вот то, что результатом этой подготовки стало
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Проверка эмуляции
Многие антивирусы, использующие для детектирования вредоносов эвристику, запускают VBA-скрипты в «песочнице» с целью убедиться в их безопасности. Потому настоящие вирусописатели в первую очередь проверяют, работает ли их сценарий в эмулированной среде, и, если так — останавливают выполнение вредоносного пейлоада, чтобы антивирус не забил тревогу.
Брендан Ортиз предложил три простых теста для определения эмулятора. Эти тесты располагаются в самом начале созданного им макроса и не позволяют ему выполнить никаких зловредных действий, если обнаружится, что скрипт запущен в изолированной среде.

Первый тест исследователь назвал «Document Name». Во многих случаях, когда антивирусный движок эмулирует выполнение макроса VBA, он меняет имя документа либо добавляет к этому имени некоторое число, чтобы отследить и предотвратить многократный запуск скрипта. Этот тест проверяет, совпадает ли с именем оригинального документа имя того документа, в котором выполняется полезная нагрузка.
Чтобы зашифровать статическую строку, в которой хранится имя документа, используется сценарий PowerShell, а затем во время выполнения скрипта для ее расшифровки вызывается собственная функция Joy. Если имя активного документа не совпадает с указанным именем, проверка считается непройденной и выполняется выход из подпрограммы.
На втором этапе проверяется путь. Если мы заранее знаем, из какой папки будет открыт документ с вредоносным сценарием (например, из папки «Загрузки» пользователя Windows), то мы можем сравнить ее с текущим путем. Несовпадение пути укажет на то, что скрипт, скорее всего, работает в антивирусном движке. В этом случае мы также выходим из подпрограммы.
Наконец, простой тест на время. Когда скрипт выполняется в эмуляторе антивируса, тот обычно пропускает фрагменты кода, в которых реализована пауза или «засыпание», иначе любящие «поспать» макровирусы попросту завесят движок. Поэтому Брендан предложил следующую нехитрую проверку: фиксируем актуальное время, «спим» две секунды, а затем просыпаемся и вновь фиксируем время. Если разница между двумя этими значениями составит менее 2 секунд, скорее всего, мы в антивирусном движке. В этом случае подпрограмма также завершается.
Если все три проверки завершились полным и безоговорочным успехом, макрос переходит к выполнению полезной нагрузки, которая выглядит примерно так, как показано на следующем рисунке.

Несколько слов об AMSI
В ходе своих экспериментов Брендан Ортиз выяснил, что поначалу его вредоносный макрос успешно детектировался эвристикой большинства антивирусных движков. Кроме того, несмотря на многочисленные попытки исследователя запустить макрос при включенной защите Windows Defender, система убивала скрипт. По какой же причине макрос помечался как вредоносный? Все дело в том, что интерфейс
Чтобы увидеть нужно авторизоваться или зарегистрироваться.

AMSI — это разработанный Microsoft интерфейс, который приспособлен к работе с любыми антивирусами и позволяет им запрашивать информацию о процессах, происходящих во время исполнения программ. Это значит, что антивирусу передаются даже бесфайловые угрозы, например команды PowerShell, если антивирус вызывает AMSI. Еще это означает, что ты можешь как угодно обфусцировать полезную нагрузку, но как только она деобфусцируется во время выполнения, AMSI начнет отслеживать поведение кода.
Когда пользователь пытается выполнить команду в PowerShell, AMSI сначала загружает, а затем проверяет эту команду. Если обнаруживаются какие‑либо элементы, которые обычно используются для вредоносных действий, в частности, вызовы API Win32 или COM (то есть, срабатывают заложенные в AMSI «триггеры»), то AMSI приостанавливает подозрительный процесс.
На картинке ниже показано, как VBA интегрируется с AMSI. С учетом всего этого исследователь стал искать способы ускользнуть от пристального внимания AMSI при выполнении скриптов VBA и PowerShell.

Импорт Windows API в VBA
Начать Брендан решил с импорта функций. Чтобы пропатчить AMSI в памяти, необходим доступ к некоторым низкоуровневым библиотекам и функциям Windows. В VBA разрешено импортировать API Windows для использования в макросе. Такая возможность радикально расширяет функционал VBA.
Злоумышленник, вызывая определенные API Windows из VBA, C#, PowerShell и т. д., должен хорошенько в них разобраться, чтобы все сделать правильно. Дело в том, что нативные функции для этих целей в соответствующих языках отсутствуют. Но вооружившись некоторыми «подготовительными знаниями», заполнить эти пробелы довольно легко. Когда ты знаешь, какие API Windows хочешь импортировать, для начала обязательно погугли соответствующую документацию Microsoft. Там ты найдешь подробную информацию об интересующей тебя функции, в частности, каковы ее возвращаемые значения, какие параметры она принимает, в какой DLL находится интересующий тебя API и т. д. Так ты получишь по‑настоящему хорошее представление о событиях, происходящих «под капотом» скрипта.
Брендан использовал в своем макросе объявление PInvoke (Platform-Invoke, «Платформенный вызов»). PInvoke – это коллекция определений для вызова нативных функций API Windows из языков программирования, в которых может отсутствовать такой низкоуровневый функционал. Особенности работы этого инструмента подробно описаны
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Обход AMSI
Импорт необходимых API Windows в VBA — это только первый шаг. Чтобы придуманный Бренданом Ортизом скрипт работал, нужно обойти AMSI. Для этого исследователь решил пропатчить AMSI прямо в памяти. А конкретнее, пропатчить первые несколько байтов в функциях AmsiScanBuffer и AmsiScanString из библиотеки Amsi.dll, загружаемой из запущенного процесса. Эти функции, если верить документации Microsoft, отвечают за сканирование содержимого буфера в поисках характерных для малвари строк.

Сначала объявляется список указателей переменных для хранения адресов функций. Затем в переменной lib сохраняется адрес из библиотеки amsi.dll, это делается при помощи направляемого к API Windows вызова LoadLib. Все строки в этом патче памяти AMSI должны быть обфусцированы. На первом этапе автор скрипта использовал для этого функцию VBA Chr(), чтобы скрыть некоторые характерные строки вроде amsi.dLl и AmsiUacInitialize.
Затем нужно найти, где лежит функция AmsiScanString. Для этого Брендан использовал функцию Windows API GetProcAddress, переименованную в GetPrAddr. Поскольку AMSI еще не пропатчен, а вредоносы часто пытаются отключить его, нацеливаясь на функции AmsiScanString и AmsiScanBuffer, автор скрипта воспользовался относительной адресацией начиная с функции AmsiUacInitialize.
Для начала Брендан вычел 96 из адреса функции AmsiUacInitialize и сохранил результат в переменной func_addr. Затем вызвал GetPrAddr со строками‑аргументами, первая из которых – это адрес amsi.dll, а вторая – строка AmsiUacInitialize, к которой применена обфускация. Зачем вычитать 96? Всё очень просто: Брендан прошелся по процессу Microsoft Word отладчиком WinDbg и выяснил, что адрес функции AmsiScanString отстоит на 96 байтов от функции AmsiUacInitialize.
Если просмотреть в WinDbg содержимое загруженной в память библиотеки AMSI.dll начиная с функции AmsiScanBuffer, расположенной по адресу 0x100 в шестнадцатеричной системе (256 байтов в десятичной), то можно увидеть, как выглядит верхняя часть вывода.

Стрелка указывает на первые опкоды в функции AmsiScanBuffer, а слева показан ассоциированный с этой инструкцией адрес в памяти: 0x00007ffa054f35e0. Следовательно, функция AmsiScanBuffer начинается именно в этой точке. Изучив вывод далее, можно найти следующую функцию — AmsiScanString, и выяснить ее адрес: 0x00007ffa054f36e0.

Далее Брендан отыскал в выводе отладчика значение переменной func_addr, которую загрузил в свой макрос VBA при помощи функции GetPrAddr. В этой переменной находится адрес функции AmsiUacInitialize: 0x00007ffa054f3740.

Все, что осталось сделать автору скрипта, — это просто вычесть из адреса функции AmsiUacInitialize адреса функций AmsiScanString и AmsiScanBuffer. Так будут получены относительные адреса последних.
0x054f3740 (AmsiUacInitialize Start Address) - 0x054f36e0 (AmsiScanString Start Address) = 0x60 = 96 bytes
0x054f3740 (AmsiUacInitialize Start Address) - 0x54f35e0 (AmsiScanBuffer Start Address) = 0x160 = 352 bytes
Следующая часть созданного Бренданом Ортизом макроса меняет защитные механизмы памяти и порядок доступа к ней целевых функций. Это делается при помощи API-функции VirtualProtect, которую автор переименовал в VirtPro.

Функция VirtualProtect меняет характеристики защиты в области виртуальной памяти, которая относится к адресному пространству вызывающего процесса. С ее помощью можно изменить показатели защиты для конкретного участка памяти (с атрибутами «только для чтения» или «для исполнения»), в результате чего появляется возможность редактировать расположенные там инструкции.
VirtualProtect принимает следующие параметры: адрес того места, которое нужно отредактировать (это адрес интересующей нас функции), размер области памяти, которую требуется редактировать (32 байта от начала адреса), уровни защиты памяти, которые требуется обеспечить (передается в виде значения, которое будет обрабатываться при помощи побитовой операции AND). Наконец, в сценарии содержится переменная flOldProtection. Функция сохраняет в эту переменную старое значение параметров защиты памяти – для последующего использования.
Получив доступ к чтению и записи тех областей памяти, что предназначены только для записи, Брендан перешел непосредственно к пропатчиванию AMSI. Для этого он использовал WinAPI-функцию RtlFillMemory. Такие функции, как RtlMoveMemory, часто используются вредоносами, поэтому легко обнаруживаются эвристикой антивирусов. А вот RtlFillMemory – не самый частотный вариант, из‑за чего ее могут и не сэмулировать движки, работа которых основана на эвристике. Да и при статическом анализе антивирусы далеко не всегда обращают на нее внимание. Брендан переименовал эту функцию в patcher.

В скрипте выполняется вызов функции RtlFillMemory по псевдониму patcher, ей передаются адреса функций AmsiScanString и AmsiScanBuffer. Затем нужно указать, сколько байтов требуется заполнить, и, наконец, передать шестнадцатеричное значение кода операции, которое требуется туда добавить: 0x90 — это код NOP, он ничего не делает. Затем выполняется то же действие, но с увеличением адреса на 1, при этом функции передается код операции return, который равен 0xc3.
В результате в начале функции AmsiScanString будет поставлен неоперационный код 0x90, а после него – код возврата 0xC3, и эта функция просто завершится сразу после того, как будет вызвана, что позволит обойти AMSI в VBA. Вот как выглядит выполнение функции AmsiScanString после применения патча в отладчике WinDbg.

Все, что остается – повторить весь процесс для функции AmsiScanBuffer, смещение для которой уже известно. Тут стоит отметить, что есть и более изощренные способы сделать это, например, динамически искать начальные байты каждой функции, как только адрес Amsi.dll будет загружен в память. Кроме того, есть некоторые отличия в процессе патча библиотеки в 32-разрядной и 64-разрядной версиях Word.
На некоторых сайтах можно встретить описания способов обхода AMSI на VBA с использованием совершенно иных, альтернативных методов, не требующих взаимодействия с API Windows. Правда, некоторые из них требуют записывать на диск файлы и с этим можно влипнуть. Примеры на эту тему можно найти в следующем
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Обфускация строк
Поскольку созданный Бренданом Ортизом VBA-скрипт использует WMI-объект для порождения процесса PowerShell, он неизбежно привлечет повышенное внимание любого антивирусного движка. Наиболее очевидным решением этой проблемы является шифрование статических строк внутри макроса.
Например, при создании объекта WMI используются такие строки, как winmgmts:, Win32_ProcessStartup, Win32_Process и им подобные. При статическом сканировании макроса эти строки будут идеальными мишенями для антивируса, он просто неизбежно пометит их как вредоносные. Брендан использовал следующий метод обфускации.

Он объявил переменную, в которой будут сохраняться статические строки, присутствующие в макросе VBA. Затем он объявил выходную переменную, в которой будет содержаться зашифрованная строка. После этого он преобразовал полезную нагрузку из строковой переменной в символьный массив. Затем десятичное значение каждого символа суммируется со значением 26. Далее он забил символ дополнительными нулями, чтобы позже, в процессе деобфускации VBA, получились предсказуемые 3-символьные значения. Если длина вывода составляет 1 символ, то к нему добавляется два нуля, если длина – два символа, добавляется один ноль, а если три символа, ничего не добавляется.
Таким образом формируется зашифрованная выходная строка, которая отправляется в буфер обмена через конвейер. После запуска скрипта в буфер обмена помещается строка примерно следующего содержания.

Деобфусцирующий макрос в VBA выглядит примерно так.

В скрипте создаются подпрограммы со случайными именами, которые не выдадут антивирусному сканеру, что процедура может быть вредоносной, а также запутают любого аналитика, которому попадет в руки такой макрос. В VBA подпрограмма вызывается при помощи инструкции, которая имеет примерно следующий вид.

Сначала зашифрованная полезная нагрузка поступает в подпрограмму Joy и запускает цикл Do While. Затем она отправляется в подпрограмму Man, извлекающую первые 3 числа (вот зачем понадобилось заполнение нулями), после чего передает эти символы функции bread. Она вычтет 26 из 3 символов, чтобы получить конкретное дешифрованное число. Это число является ASCII-эквивалентом символа и сохраняется в переменной green.
Далее вся зашифрованная полезная нагрузка отправляется функции rand, которая удаляет из нее первые три символа и сохраняет их обратно в переменную paper. Затем процесс повторяется до тех пор, пока вся полезная нагрузка не редуцируется до нуля.
Наконец, дешифрованная полезная нагрузка возвращается вызывающей процедуре при помощи инструкции Joy = green.
Выполнение PowerShell при помощи WMI
Далее необходимо запустить экземпляр процесса PowerShell при помощи WMI. Для этого создается объект WMI посредством вызова функции VBA GetObject с зашифрованной строкой winmgmts:, результат сохраняется в переменной ObjWMIService.
После этого вызывается функция Get из объекта WMI Service с зашифрованной версией строки Win32_ProcessStartup и сохраняется в переменную objStartup. Это позволяет задать параметры запуска для создаваемого скриптом процесса PowerShell. Следующие действия направлены на то, чтобы спрятать окно PowerShell (для этого значение переменной objConfig.showWindow устанавливается в 0) — пользователь даже не узнает, что в фоновом режиме выполняется какой‑то скрипт.
Созданному процессу передаются заранее сформированные параметры и настройки в виде нескольких сцепленных строк. Весь ход создания объекта WMIObject выглядит примерно так.

Полезная нагрузка PowerShell
Прежде чем запустить вредоносную команду PowerShell, необходимо отключить AMSI. Поскольку инстанс PowerShell порождается в отдельном процессе, здесь возникает загвоздка с еще одним случаем интеграции AMSI, который придется обойти прежде, чем скрипт начнет какую‑либо вредоносную активность.
Работа макроса начинается с отключения AMSI в VBA, чтобы во время исполнения система не пометила содержимое макроса как вредоносное.
Для этого объявляются три разные строки — str1,str2 и str3, а затем им передается в качестве значения полезная нагрузка, которую нужно прогнать через объект WMI (процесс создания этого объекта был описан выше). Это сделано ради удобочитаемости и пригодности к отладке: разделив полезную нагрузку на фрагменты, можно выявить, где именно она прекращает выполняться.
Первая строка (str1) получает в качестве нагрузки зашифрованную версию кода:
Powershell.exe -ep bypass -nop -c
Если не удается создать процесс PowerShell при выполнении макроса, то уже известно, что проблема заключается в первом зашифрованном фрагменте.
Вторая строка (str2) получает в качестве нагрузки следующий зашифрованный код:
Эта строка – зашифрованный обход AMSI. Шифрование выполнено по основанию 64, так как при переводе с VBA на PowerShell могут возникнуть проблемы при передаче специальных символов. В декодированном виде обход укладывается в одну строку, а в несвернутом выглядит примерно так.

Если будет получено уведомление Microsoft Defender о блокировке скрипта на данном этапе, станет очевидно, что обход не удался. Если не удастся установить соединение с веб‑сервером для скачивания полезной нагрузки, но сам скрипт PowerShell запустится успешно, это также будет означать, что проблема возникла именно здесь.
Третья строка (str3) получает в качестве нагрузки такой код:
Так извлекается первая часть полезной нагрузки, загрузчик шелл‑кода, который будет загружать байт‑код со стадии 2 и выполнять его непосредственно в памяти.
Загрузчик шелл-кода
После запуска из PowerShell скрипт скачивает сгенерированный шелл‑код, а затем, воспользовавшись рефлексией, загружает функции Windows API, эквивалентные VirtualAlloc, CreateThread и WaitForSingleObject.
Сгенерированный шелл‑код использует загруженные API Windows, чтобы выполнить код в текущем процессе PowerShell. Это и называется рефлексией, в ходе нее функции создаются и загружаются в память. Есть причина, по которой нужно действовать именно так, а не использовать, например,
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Дело в том, что Add-Type создает временный файл и сохраняет его на диск. Такой файл – это артефакт, по которому вирусный аналитик может догадаться, что в системе происходит что‑то нехорошее. Кроме того, такой файл может быть замечен и просканирован антивирусными движками, когда он уже сохранен на диск.
В итоге, созданный Бренданом Ортизом скрипт принял следующий вид:
При исполнении шелл‑код выполняет обратный вызов к серверу C2, а затем возвращает управление шелл‑коду.

Стомпинг VBA
Остается последний этап — стомпинг VBA. Это акт удаления частично сжатой версии исходного кода VBA, сохраненного в документе – так, что остается только предварительно скомпилированный код. Когда документ открывают на целевой машине в той же версии Word, для которой создавался скрипт, код VBA не компилируется заново — вместо этого выполняется сохраненный макрос VBA.
Таким образом, если нам заблаговременно известно, с какой версией VBA будет выполняться наш макрос, мы можем удалить из документа исходный код и оставить только скомпилированный. Благодаря этому радикально сужается возможность анализа кода и снижается вероятность его обнаружения антивирусом.
Для такой работы особенно хорош
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
В результате выполнения команды в папке с исходным файлом создается новый документ, к имени которого добавлена строка _EvilClippy.

Обрати внимание, насколько два этих файла отличаются по размеру. Теперь остается только загрузить вредоносный документ на
Чтобы увидеть нужно авторизоваться или зарегистрироваться.
Выводы
А вот и результат!

Усилия вознаграждены. Угрозу обнаружили всего 5 из 26 антивирусных движков, и автору скрипта удалось ускользнуть от самых распространенных, таких как Windows Defender, Sophos, Kaspersky и McAfee. С помощью примененных им методов скрипту удалось отключить AMSI и выполнить вредоносный VBA-скрипт на машине, защищенной Windows Defender.