
CobaltStrike BOF для генерации Beacons с помощью перехвата каталога приложений DLL
DropSpawn — это BOF для CobaltStrike, используемый для запуска дополнительных Beacons с помощью относительно неизвестного метода DLL-хайекинга. Работает x86-x86, x64-x64, а также x86-x64/наоборот. Используйте как альтернативу внедрению процессов.
Исполняемые файлы Windows будут следовать порядку поиска DLL при попытке загрузить DLL, чьи абсолютные пути не были указаны:

DLL-хайекинг обычно требует, чтобы либо:
A. Пользователь имел права на запись в папке с более высоким приоритетом в порядке поиска, чем та, где находится настоящая DLL
или
B. Что указанная DLL не существует нигде в системе, и в этом случае её можно разместить в доступной для записи папке в переменной %PATH% пользователя (например, %USERPROFILE%\appdata\local\microsoft\windowsapps).
Эти требования исключают DLL-хайекинг для исполняемых файлов, находящихся в C:\Windows\System32, так как почти все DLL, которые загружают эти исполняемые файлы, также находятся в System32. Копирование исполняемого файла из System32 в доступное для записи место и его запуск оттуда — это вариант, но он не очень безопасен с точки зрения OPSEC, поскольку двоичные файлы System32, запущенные из альтернативных мест, легко обнаружить.
DropSpawn позволяет выполнять DLL-хайекинг с использованием исполняемых файлов System32 (и других, найденных в дополнительных папках, недоступных для записи пользователем), путём подмены «Каталога, из которого загружается приложение» на произвольный, заданный пользователем.
Публичный выпуск DropSpawn немного отличается от непубличного. Непубличный выпуск использует проприетарный генератор полезных нагрузок, что делает работу оператора гораздо более плавной. Публичный выпуск был немного изменён с учётом того, что у пользователей будут свои способы генерации полезных нагрузок, совместимых с DLL-хайекингом. Включён скрипт Python3, а также исходный код демонстрационной DLL, чтобы помочь пользователям интегрировать и использовать DropSpawn.
Определите некоторые целевые исполняемые файлы, которые пытаются загрузить DLL, не указывая их абсолютные пути. Это можно сделать, скопировав exe в доступный для записи каталог и запустив его, наблюдая за ним с помощью Procmon. В этом примере мы будем использовать WerFault.exe, который обычно находится в C:\Windows\System32\WerFault.exe

В приведённом выше примере cryptsp.dll, wer.dll, dbghelp.dll и bcrypt.dll являются подходящими кандидатами, поскольку их абсолютные пути не были указаны в WerFault; в результате WerFault сначала попытается загрузить их из своего каталога приложения, прежде чем обратиться к остальному порядку поиска DLL. Обратите внимание, что обычно это не проблема, потому что каталог приложения WerFault — это System32.
Загрузите одну из DLL, подлежащих перехвату, с целевой системы.
Это необходимо, чтобы мы могли извлечь её экспортируемые функции и включить их в нашу полезную нагрузку DLL. Важно взять DLL, подлежащую перехвату, с той же машины, на которой вы планируете использовать DropSpawn, так как DLL меняются в зависимости от версий Windows. Кроме того, если вы используете x86 beacon и хотите создать x64 beacon с помощью DropSpawn, обязательно загрузите x64-версию настоящей DLL, указав 'C:\windows\sysnative...' вместо 'C:\windows\system32...'.
Запустите generate_dll.py, передав загруженную DLL и желаемую архитектуру полезной нагрузки. generate_dll.py — это модифицированная версия этого скрипта. Он разберёт предоставленную DLL, создаст файл .def, содержащий экспортируемые функции DLL, и вызовет MingW для компиляции нашей демонстрационной полезной нагрузки DLL. Когда порождённый процесс попытается вызвать реальную функцию внутри подменённой DLL, наша полезная нагрузка DLL перенаправит вызов к настоящей DLL, находящейся в System32, чтобы процесс-хост не упал.

Вызовите dropspawn, используя сгенерированную полезную нагрузку DLL.
dropspawn <payload DLL> <x86|x64> <program to spawn> [writable target folder] [parent]
payload DLL — полный путь к сгенерированной полезной нагрузке DLL.
architecture — архитектура процесса, который вы хотите создать
program to spawn — имя/путь процесса, который вы хотите создать. Если этот процесс находится в System32 (или syswow64), вы можете указать только имя. В противном случае укажите полный путь. Вы также можете передать аргументы командной строки процессу. Если в пути есть пробелы или вы используете аргументы, заключите всё в кавычки.
writable target folder — необязательно. Если оставить пустым, dropspawn попытается использовать текущий каталог Beacon. Используйте кавычки, если в пути есть пробелы.
parent — необязательно. Имя процесса, который будет использоваться для подмены PPID для вновь созданного процесса. Если указан процесс, имеющий несколько запущенных экземпляров с разными уровнями привилегий (например, svchost.exe), dropspawn попытается определить тот, который можно использовать для подмены PPID.
Пример: dropspawn /root/gitlab/DropSpawn_BOF/dist/dbgcore.dll x64 "WerFault.exe -u -p 4352 -s 160" C:\users\user\appdata\local\temp explorer.exe
Это сбросит полезную нагрузку DLL 'dbgcore.dll' на диск по пути 'c:\users\user\appdata\local\temp\dbgcore.dll' и создаст процесс WerFault.exe x64 с аргументами командной строки '-u -p 4352 -s 160' и explorer.exe в качестве родительского процесса.


Очистка проста. Включив функцию Self-Deletion в полезную нагрузку DLL, которая сбрасывается на диск, она будет удалена, как только наш новый процесс запустится и загрузит её. Это меняет правила игры, так как обычно DLL остаётся заблокированной на диске до тех пор, пока процесс, загрузивший её, продолжает работу. Если техника Self-Deletion по какой-то причине не срабатывает (или процесс не удаётся запустить), DropSpawn попытается удалить полезную нагрузку DLL с диска и в любом случае сообщит пользователю о результате операции.
Внедрение процессов обычно следует цепочке: открыть удалённый процесс -> выделить удалённую память -> записать в удалённую память -> выполнить в удалённой памяти, с возможностью создать новый процесс в начале вместо использования существующего. DropSpawn только создаёт новый процесс; вновь созданный процесс отвечает за выделение, запись и выполнение shellcode, поэтому мы можем избежать многих IOC, обычно связанных с внедрением в удалённый процесс.
Эта техника, конечно, зависит от того, насколько хороши ваши полезные нагрузки DLL. Но мы можем посмотреть, что видит Windows (в следующем разделе используется частная версия DropSpawn и создание Beacons).
Что касается Event Viewer, всё выглядит нормально:

В MDE практически нечего видеть.
Запуск dropspawn:

Журналы MDE:
С подменой PPID:

Без подмены PPID:

В обоих случаях мы видим, как наш исходный процесс beacon (также werfault) сбрасывает dbgcore.dll на диск, создаёт новый процесс WerFault.exe, вновь созданный процесс загружает dbgcore.dll, а затем переименовывает (удаляет) его. Критически важно, что нет дополнительной проверки dbgcore.dll, которая часто сопровождает DLL-хайекинг, потому что мы не записываем её в какое-либо часто перехватываемое место, и WerFault.exe (или любой другой процесс, который вы решите использовать) не ассоциируется с DLL-хайекингом так, как, например, WmiPrvSE.exe.
Интересно, что делать это с подменой PPID почти более заметно, чем без неё. Однако это может варьироваться в зависимости от средства защиты.
Как упоминалось, крайне важно, чтобы пользователи загружали настоящие DLL с целевой машины, на которой они планируют использовать DropSpawn. Использование неправильной версии DLL может привести к сбою порождённого процесса, если он попытается вызвать несуществующую функцию.
DropSpawn можно использовать с исполняемыми файлами за пределами System32; однако предупреждаем, что могут возникнуть проблемы, если процесс попытается загрузить дополнительные DLL из реального каталога приложения. Поскольку мы подменили каталог приложения в другом месте, если настоящий каталог приложения также не будет доступен через порядок поиска DLL, процесс упадёт или не запустится, так как не сможет найти необходимые DLL. Всегда тестируйте потенциальные перехваты на тестовых машинах перед использованием в производстве!
Это исследование началось, когда я изучал, как процессы собирают свой окончательный порядок поиска DLL (поскольку он должен определяться во время выполнения из-за того, что исполняемые файлы находятся в разных каталогах, текущий каталог является частью пути поиска и т.д.). Моё исследование привело меня к этому сообщению на форуме, которое послужило источником двух критических недокументированных API, лежащих в основе этой техники.
Они уже упоминались выше, но этот пост о предотвращении блокировки загрузчика, этот скрипт для создания файла .def для проксирования DLL и это исследование по обеспечению самоудаления запущенных исполняемых файлов необходимы для создания эффективных, готовых к использованию полезных нагрузок DLL, подходящих для DropSpawn.
Когда я впервые опубликовал эту технику в Twitter, несколько других присоединились к разговору и создали POC. SecurityAndStuff сделал этот, а Snovvcrash свой здесь