
шаблон для разработки пользовательских C2-каналов для Cobalt Strike с использованием IAT-перехватчиков, применяемых рефлексивным загрузчиком.
Это простой PoC и шаблон для разработки пользовательских C2-каналов для Cobalt Strike с использованием перехватов IAT, применяемых рефлективным загрузчиком.
Статья в блоге: https://codex-7.gitbook.io/codexs-terminal-window/red-team/cobalt-strike/building-custom-c2-channels-by-hooking-wininet
Демо gif TCP-канала

файлы customCallback.h, hook.c, hash.h и hook.h являются черновыми шаблонами, но их можно поместить в Crystal kit и использовать как есть в качестве PoC. Поместите их в папку udrl/src, заменив существующие копии. tcg.h не изменён (из tradecraft garden Рафаэля Маджа).
Примеры в папке examples/ можно использовать как есть, но они являются примерами, и при их написании не учитывались операционные аспекты. Используйте с осторожностью.
Текущие примеры:
Обратите внимание, что оригинальные возможности обхода в Crystal kit, такие как реализация Draugr и маскировка сна, были удалены из hook.c, чтобы сохранить чистоту и переносимость кодовой базы. Если вы хотите сохранить эти функции, вы можете добавить их обратно из оригинальных копий в Crystal kit.
Вы должны выбрать соответствующую HTTP-библиотеку winhttp/wininet в качестве HTTP-библиотеки при генерации DLL бэкона.
Гибкий профиль, на котором проводилось тестирование, предоставлен в репозитории — теоретически (большинство) другие гибкие профили должны работать, но этот шаблон тестировался с использованием этого профиля (взятого из GraphStrike).
Официальный интерфейс ExternalC2 неудобен в использовании — он требует постановки SMB-бэкона ЧЕРЕЗ канал externalc2 и (до версии 4.10) не поддерживал связь с предварительно созданным SMB-бэконом без предварительной постановки через агент externalc2. Даже в текущем состоянии он всё ещё привязан к архитектуре:
smb beacon --named pipe--> externalc2 agent --custom channel--> externalc2 handler --> teamserver
Да, я знаю, что UDC2 был добавлен в Cobalt Strike 4.12. В любом случае, это просто забавный эксперимент по воспроизведению этой возможности в UDRL.
Концепция проста: если вы можете перехватить WinAPI, используемые для обратного вызова, у вас уже есть все данные, необходимые для обратного вызова. Этот метод реализации пользовательских C2-каналов уже использовался ранее, как показано в GraphStrike.
Однако реализация GraphStrike всё ещё довольно привязана к самому протоколу HTTP. Цель этого репозитория — предоставить простой в использовании, модификации и расширении шаблон для реализации любого пользовательского канала, HTTP или иного, с минимальными изменениями окружающего кода.
Этот шаблон, очевидно, не предназначен для использования в состоянии по умолчанию, поэтому для реализации выбранного вами C2-канала необходимо изменить следующее:
customCallback функция для выполнения следующих действий:
Вы можете использовать исходный хост и порт HTTP-запроса (которые вы задаёте из Cobalt) через аргументы:
const char *host
INTERNET_PORT port
и изменить функцию handleCallback() в broker.py для выполнения следующих действий:
Вот и всё.
В качестве примера, PoC в функции customCallback() в customCallback.h в настоящее время делает следующее:
PoC в функции handleCallback в broker.py в настоящее время делает следующее:
process_encoded_request(encoded_request)usage: broker.py [-h] --host HOST --port PORT
где хост и порт указывают на HTTP-прослушиватель на вашем teamserver. Брокер извлекает HTTP-запрос и отправляет его на реальный прослушиватель.
Вероятно, это самый «ленивый» способ сделать это, но также один из самых стабильных. Он просто упаковывает весь HTTP-запрос и отправляет его брокеру любым удобным способом, который затем отправляет его на реальный teamserver для запуска в качестве HTTP-бэкона, полностью прозрачно для teamserver.
Существует (технически) более чистая реализация, которую я пробовал. Она включала использование минимального гибкого C2-профиля (я использовал профиль из graphstrike) и парсинг различных компонентов данных бэкона в соответствии со спецификацией malleable внутри самих перехватчиков wininet (id, метаданные, вывод и т.д.). Это позволило бы немного уменьшить размеры блобов обратного вызова, но от этой реализации отказались, так как кодовая база стала излишне запутанной из-за парсинга запросов, и к тому же требовалось использование этого конкретного профиля (что не так уж и критично, но довольно хакерски).
Я посчитал, что эта реализация лучше, потому что она меньше зависит от хакерского malleable-профиля (технически, она всё ещё требует, чтобы вывод ответа бэкона отправлялся в теле ответа, но это единственное требование), и код был в 10 раз легче для чтения.
Также стоит отметить, что в JSON HTTP-запроса нет никакого обфускации или шифрования, кроме кодировки base64 — вы можете добавить свои собственные механизмы, но поскольку обратные вызовы Beacon уже зашифрованы, технически никакие данные не рискуют быть расшифрованы. Худшее, что может произойти — это то, что base64-блоб, если его извлекут, может быть идентифицирован как HTTP-запрос. Распоряжайтесь этой информацией как хотите.
Да, я использовал LLM для написания части кода и комментариев. Никто не любит писать документацию или писать шаблонный парсинг JSON на C с нуля. Подавайте в суд.
Обычный отказ от ответственности: я не несу ответственности за любые преступления против человечества, которые вы можете совершить, или ядерную катастрофу, которую вы можете вызвать с помощью этого плохо написанного кода.