Флуктуация шелл-кода. Пишем инжектор для динамического шифрования полезной нагрузки в памяти

Содержание статьи

  • Проблематика
  • A long time ago in a galaxy far, far away…
  • Флипы памяти RX → RW / NA
  • Cobalt Strike: Obfuscate and Sleep
  • Флуктуация шелл-кода на GitHub
  • ShellcodeFluctuation
  • fastTrampoline
  • MyMessageBoxA
  • Пилим свой флуктуатор на С# 
  • Прототипирование
  • Реализация
  • Использование с агентом C2
  • Бонус. Реализация API Hooking с помощью MiniHook.NET
  • Выводы

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

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

  • Ис­поль­зовать C2-фрей­мвор­ки, которые еще не успе­ли намозо­лить гла­за блю­тиме­рам и чьи аген­ты еще не попали в спи­сок лег­кодетек­тиру­емых. Дру­гими сло­вами, писать свое, искать малопо­пуляр­ные решения на гит­хабе с уче­том реги­ональ­ных осо­бен­ностей AV, который ты соб­рался бай­пасить, и тому подоб­ное.
  • При­бег­нуть к прод­винутым тех­никам сок­рытия инди­като­ров ком­про­мета­ции пос­ле запус­ка аген­та 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)

    www

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

    • Bypassing Memory Scanners with Cobalt Strike and Gargoyle
    • Bypassing PESieve and Moneta (The “easy” way….?)
    • A variant of Gargoyle for x64 to hide memory artifacts using ROP only and PIC

    Так­же инте­рес­ный под­ход про­демонс­три­рова­ли в 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.

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

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

    Од­нако на любое дей­ствие есть про­тиво­дей­ствие (или наобо­рот), поэто­му люди из 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}

    Источник: xakep.ru

    Ответить

    Ваш адрес email не будет опубликован. Обязательные поля помечены *