Се­год­ня погово­рим об одной из прод­винутых тех­ник укло­нения от средств защиты при исполь­зовании фрей­мвор­ков Command & Control — динами­чес­ком сок­рытии шелл‑кода в памяти ожи­дающе­го про­цес­са. Я соберу PoC из дос­тупно­го на гит­хабе кода и при­меню его к опен­сор­сным фрей­мвор­кам.

Ес­ли взгля­нуть на спи­сок фич, которы­ми хвас­тают­ся все ком­мерчес­кие фрей­мвор­ки C2 сто­имостью 100500 дол­ларов в час (Cobalt Strike, Nighthawk, Brute Ratel C4), пер­вой в этих спис­ках зна­чит­ся, как пра­вило, воз­можность укло­нить­ся от ска­ниро­вания памяти запущен­ных про­цес­сов на пред­мет наличия сиг­натур аген­тов этих самых C2. Что, если поп­робовать вос­создать эту фун­кцию самос­тоятель­но? В статье я покажу, как я это сде­лал.

Итак, что же это за зверь такой, этот флук­туирующий шелл‑код?

 

Проблематика

В основном мой хлеб — это внут­ренние пен­тесты, а на внут­ренних пен­тестах быва­ет удоб­но (хотя и сов­сем не необ­ходимо) поль­зовать­ся фрей­мвор­ками C2. Пред­ставь, что ты раз­ломал рабочую стан­цию поль­зовате­ля, име­ешь к ней админ­ский дос­туп, но вор­вать­ся туда по RDP нель­зя, ведь нарушать биз­нес‑про­цес­сы заказ­чика (то есть выбивать сот­рудни­ка из его сес­сии, где он усер­дно запол­няет ячей­ки в очень важ­ной нак­ладной) «запад­ло».

Од­но из решений при работе в Linux — ква­зиин­терак­тивные шел­лы вро­де smbexec.py, wmiexec.py, dcomexec.py, scshell.py и Evil-WinRM. Но, во‑пер­вых, это чер­тов­ски неудоб­но, во‑вто­рых, ты потен­циаль­но стал­кива­ешь­ся с проб­лемой double-hop-аутен­тифика­ции (как, нап­ример, с Evil-WinRM), а в‑треть­их и далее — ты не можешь поль­зовать­ся объ­ективно полез­ными фичами C2, как, нап­ример, исполне­ние .NET из памяти или под­нятие прок­си через ском­про­мети­рован­ную тач­ку.

Ес­ли не рас­смат­ривать сов­сем уж инва­зив­ные под­ходы типа пат­чинга RDP при помощи Mimikatz (AKA ts::multirdp), оста­ется работа из аген­та С2. И вот здесь ты стол­кнешь­ся с проб­лемой бай­паса средств защиты. Спой­лер: по моему опы­ту, в 2022-м при активнос­ти любого «уво­жаемо­го» анти­виру­са или EDR на хос­те твой агент C2, которо­го ты так дол­го пытал­ся получить (и все же получил, зак­рипто­вав наг­рузку миль­ён раз), про­живет в луч­шем слу­чае не боль­ше часа.

Все­му виной баналь­ное ска­ниро­вание памяти запущен­ных про­цес­сов анти­виру­сами, которое выпол­няет­ся по рас­писанию с целью поис­ка сиг­натуры извес­тных злов­редов. Еще раз: по­лучить агент с активным AV (и даже нем­ного из него порабо­тать) нет­рудно; сде­лать так, что­бы этот агент про­жил хотя бы сут­ки на машине‑жер­тве, бес­ценно уже слож­нее, потому что, как бы ты ни крип­товал и ни энко­дил бинарь, PowerShell-стей­жер или шелл‑код аген­та, вре­донос­ные инс­трук­ции все рав­но ока­жут­ся в памяти в откры­том виде, из‑за чего ста­нут лег­кой добычей для прос­того сиг­натур­ного ска­нера.

KES поднимает тревогу!

Ес­ли тебя спа­лят с вре­доно­сом в сис­темной памяти, который не под­креп­лен подоз­ритель­ным бинарем на дис­ке (нап­ример, ког­да име­ла мес­то инъ­екция шелл‑кода в про­цесс), тот же Kaspersky Endpoint Security при дефол­тных нас­трой­ках не опре­делит, какой имен­но про­цесс заражен, и в качес­тве решения нас­той­чиво пред­ложит тебе перезаг­рузить машину.

Да-да, мы поняли
Да‑да, мы поняли

Та­кое поведе­ние вызыва­ет еще боль­шее негодо­вание у пен­тесте­ра, потому что испу­ган­ный поль­зователь сра­зу побежит жаловать­ся в IT или к безопас­никам.

Есть два пути решить эту проб­лему.

  1. Ис­поль­зовать C2-фрей­мвор­ки, которые еще не успе­ли намозо­лить гла­за блю­тиме­рам и чьи аген­ты еще не попали в спи­сок лег­кодетек­тиру­емых. Дру­гими сло­вами, писать свое, искать малопо­пуляр­ные решения на гит­хабе с уче­том реги­ональ­ных осо­бен­ностей AV, который ты соб­рался бай­пасить, и тому подоб­ное.
  2. При­бег­нуть к прод­винутым тех­никам сок­рытия инди­като­ров ком­про­мета­ции пос­ле запус­ка аген­та C2. Нап­ример, под­чищать ано­малии памяти пос­ле запус­ка потоков, исполь­зовать связ­ку «неис­полня­емая память + ROP-гад­жеты» для раз­мещения аген­та и его фун­кци­они­рова­ния, шиф­ровать наг­рузку в памяти, ког­да вза­имо­дей­ствие с аген­том не тре­бует­ся.

В этой статье мы на при­мере пос­мотрим, как воору­жить прос­той PoC флук­туирующе­го шелл‑кода (ком­бинация пун­ктов из абза­ца выше) для его исполь­зования с поч­ти любым опен­сор­сным фрей­мвор­ком C2. Но для начала неболь­шой экскурс в исто­рию.

 

A long time ago in a galaxy far, far away...

 

Флипы памяти RX → RW / NA

Пер­вым опен­сор­сным про­ектом, пред­лага­ющим PoC-решение для укло­нения от ска­ниро­вания памяти, о котором я узнал, был gargoyle.

Ес­ли не углублять­ся в реали­зацию, его глав­ная идея зак­люча­ется в том, что полез­ная наг­рузка (исполня­емый код) раз­меща­ется в неис­полня­емой области памяти (PAGE_READWRITE или PAGE_NOACCESS), которую не ста­нет ска­ниро­вать анти­вирус или EDR. Пред­варитель­но заг­рузчик gargoyle фор­миру­ет спе­циаль­ный ROP-гад­жет, который выс­тре­лит по тай­меру и изме­нит стек вызовов таким обра­зом, что­бы вер­хушка сте­ка ока­залась на API-хен­дле VirtualProtectEx, — это поз­волит нам изме­нить мар­киров­ку защиты памяти на PAGE_EXECUTE_READ (то есть сде­лать память исполня­емой). Даль­ше полез­ная наг­рузка отра­бота­ет, сно­ва передаст управле­ние заг­рузчи­ку gargoyle, и про­цесс пов­торит­ся.

Механизм работы gargoyle (изображение — lospi.net)
Ме­ханизм работы gargoyle (изоб­ражение — lospi.net)

www

Прин­цип работы gargoyle мно­го раз допол­нили, улуч­шили и «пере­изоб­рели». Вот нес­коль­ко при­меров:

Так­же инте­рес­ный под­ход про­демонс­три­рова­ли в F-Secure Labs, реали­зовав рас­ширение Ninjasploit для Meterpreter, которое по кос­венным приз­накам опре­деля­ет, что Windows Defender вот‑вот запус­тит про­цеду­ру ска­ниро­вания, и тог­да «фли­пает» область памяти с аген­том на неис­полня­емую пря­мо перед этим. Сей­час, ско­рее все­го, это рас­ширение уже не «взле­тит», так как и Meterpreter, и «Дефен­дер» обно­вились не по одно­му разу, но идея все рав­но показа­тель­на.

Из это­го пун­кта мы заберем с собой глав­ную идею: изме­нение мар­киров­ки защиты памяти помога­ет скрыть факт ее зараже­ния.

Вот что на самом деле происходит под капотом этой техники
Вот что на самом деле про­исхо­дит под капотом этой тех­ники
 

Cobalt Strike: Obfuscate and Sleep

В далеком 2018 году выш­ла вер­сия 3.12 куль­товой C2-плат­формы Cobalt Strike. Релиз называл­ся «Blink and you’ll miss it», что как бы намека­ет на глав­ную фичу новой вер­сии — дирек­тиву sleep_mask, в которой реали­зова­на кон­цепция obfuscate-and-sleep.

Эта кон­цепция вклю­чает в себя сле­дующий алго­ритм поведе­ния бикона:

  1. Ес­ли маячок «спит», то есть без­дей­ству­ет, выпол­няя kernel32!Sleep и ожи­дая коман­ды от опе­рато­ра, содер­жимое исполня­емо­го (RWX) сег­мента памяти полез­ной наг­рузки обфусци­рует­ся. Это меша­ет сиг­натур­ным ска­нерам рас­познать в нем Behavior:Win32/CobaltStrike или похожую бяку.
  2. Ес­ли маяч­ку пос­тупа­ет на исполне­ние сле­дующая коман­да из оче­реди, содер­жимое исполня­емо­го сег­мента памяти полез­ной наг­рузки деоб­фусци­рует­ся, коман­да выпол­няет­ся, и подоз­ритель­ное содер­жимое маяка обратно обфусци­рует­ся, прев­раща­ясь в нераз­борчи­вый циф­ровой мусор на радость опе­рато­ру «Кобы» и наз­ло бдя­щему анти­виру­су.

Эти дей­ствия про­ходят проз­рачно для опе­рато­ра, а про­цесс обфуска­ции пред­став­ляет собой обыч­ный XOR по исполня­емой области памяти с фик­сирован­ным раз­мером клю­ча 13 байт (для вер­сий CS от 3.12 до 4.3).

Про­демонс­три­руем это на при­мере. Я возь­му этот про­филь для CS, написан­ный @an0n_r0 как PoC минималь­но необ­ходимо­го про­филя Malleable C2 для обхо­да «Дефен­дера». Опция set sleep_mask "true" акти­виру­ет про­цесс obfuscate-and-sleep.

Получили маячок
По­лучи­ли маячок

Да­лее с помощью Process Hacker най­дем в бинаре «Кобы» сег­мент RWX-памяти (при задан­ных нас­трой­ках про­филя он будет один) и пос­мотрим его содер­жимое.

Цифровой мусор или?..
Циф­ровой мусор или?..

На пер­вый взгляд, и прав­да, выг­лядит как ничего не зна­чащий набор бай­тов. Но если уста­новить инте­рак­тивный режим маяч­ка коман­дой sleep 0 и «пок­лацать» нес­коль­ко раз на Re-read в PH, нам откро­ется исти­на.

Маски прочь!
Мас­ки прочь!
Деобфусцированная нагрузка маячка
Де­обфусци­рован­ная наг­рузка маяч­ка

Воз­можно, это содер­жимое все еще не очень информа­тив­но (сама наг­рузка чуть даль­ше в памяти ста­ба), но, если пересоз­дать бикон без исполь­зования про­филя, мож­но уви­деть сер­дце маяч­ка в чис­том виде.

PURE EVIL
PURE EVIL

Од­нако на любое дей­ствие есть про­тиво­дей­ствие (или наобо­рот), поэто­му люди из Elastic, недол­го думая, за­пили­ли YARA-пра­вило для обна­руже­ния пов­торя­ющих­ся пат­тернов, «зак­сорен­ных» на одном и том же клю­че:

rule cobaltstrike_beacon_4_2_decrypt
{
meta:
author = "Elastic"
description = "Identifies deobfuscation routine used in Cobalt Strike Beacon DLL version 4.2."
strings:
$a_x64 = {4C 8B 53 08 45 8B 0A 45 8B 5A 04 4D 8D 52 08 45 85 C9 75 05 45 85 DB 74 33 45 3B CB 73 E6 49 8B F9 4C 8B 03}
$a_x86 = {8B 46 04 8B 08 8B 50 04 83 C0 08 89 55 08 89 45 0C 85 C9 75 04 85 D2 74 23 3B CA 73 E6 8B 06 8D 3C 08 33 D2}
condition:
any of them
}

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    3 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии