Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Сегодня поговорим об одной из продвинутых техник уклонения от средств защиты при использовании фреймворков 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 или к безопасникам.
Есть два пути решить эту проблему.
В этой статье мы на примере посмотрим, как вооружить простой PoC флуктуирующего шелл‑кода (комбинация третьего и частично второго и пункта из абзаца выше) для его использования с почти любым опенсорсным фреймворком C2. Но для начала немного экскурса в историю.
Первым опенсорсным проектом, предлагающим PoC-решение для уклонения от сканирования памяти, о котором я узнал, был gargoyle.
Если не углубляться в реализацию, его главная идея заключается в том, что полезная нагрузка (исполняемый код) размещается в неисполняемой области памяти (PAGE_READWRITE
или PAGE_NOACCESS
), которую не станет сканировать антивирус или EDR. Предварительно загрузчик gargoyle формирует специальный ROP-гаджет, который выстрелит по таймеру и изменит стек вызовов таким образом, чтобы верхушка стека оказалась на API-хендле VirtualProtectEx
, — это позволит нам изменить маркировку защиты памяти на PAGE_EXECUTE_READ
(то есть сделать память исполняемой). Дальше полезная нагрузка отработает, снова передаст управление загрузчику gargoyle, и процесс повторится.
Механизм работы gargoyle (изображение — lospi.net)
www
Принцип работы gargoyle много раз дополнили, улучшили и «переизобрели». Вот несколько примеров:
Также интересный подход продемонстрировали в F-Secure Labs, реализовав расширение Ninjasploit для Meterpreter, которое по косвенным признакам определяет, что Windows Defender вот‑вот запустит процедуру сканирования, и тогда «флипает» область памяти с агентом на неисполняемую прямо перед этим. Сейчас, скорее всего, это расширение уже не «взлетит», так как и Meterpreter, и «Дефендер» обновились не по одному разу, но идея все равно показательна.
Из этого пункта мы заберем с собой главную идею: изменение маркировки защиты памяти помогает скрыть факт ее заражения.
Вот что на самом деле происходит под капотом этой техники
В далеком 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