Вы должны простить меня за то, что я написал (и объяснил) запутанный рецепт в CyberChef, чтобы делать то, для чего CyberChef не предназначен. Извините заранее.
Зачем тебе это делать?
CyberChef, вероятно, самый популярный веб-инструмент для декодирования, дешифрования и деобфускации вредоносных программ.
CobaltStrike - самый популярный m̶a̶l̶w̶a̶r̶e̶ ̶f̶a̶m̶i̶l̶y̶, средство защиты от ошибок, используемое злоумышленниками.
Переключение контекста замедляет работу аналитиков (так что раскрутка виртуальных машин - это плохо).
Если вам нравится решать головоломки, писать рецепты CyberChef - это весело.
Анализ конфигураций CobaltStrike Beacon - это обычная задача в моем прошлом опыте активных вторжений, следовательно, ускорение времени извлечения IOC полезно для того, чтобы помешать злоумышленникам в достижении их целей.
Как?
Мы начнем с предположения, что вход CyberChef, CobaltStrike Beacon, находится в формате исполняемого файла Stageless. (Не поэтапная полезная нагрузка, поскольку они не содержат структуры конфигурации.)
Нам нужно будет преобразовать необработанные входные двоичные данные в шестнадцатеричный.
Нам нужно будет сохранить шестнадцатеричные данные в регистре ($ R0) для дальнейшего использования.
Нам нужно будет найти флаг индикатора смещения 0xFFFFFF.
Нам нужно будет извлечь размер смещения, используя машину Руба Голдберга операций, которая в конечном итоге используется для подсчета байтов, деления количества байтов на 4 и прибавления 2.
Затем смещение полезной нагрузки сохраняется в регистре ($ R1).
Теперь нам нужно очистить наш входной буфер и заменить его исходным.
Мы можем сделать это с помощью двух операций поиска / замены, хотя одна должна работать, есть ошибка, которая не позволяет очистить буфер. Приведенные ниже операции показывают обходной путь.
Затем нам нужно захватить все, что находится после смещения, чтобы расшифровать полезную нагрузку.
Нам нужно заменить буфер полезной нагрузкой, начинающейся после смещения.
Затем нам нужно декодировать из шестнадцатеричного.
Маяки CobaltStrike, как правило, шифруются с использованием 4-байтового шаблона XOR, который выглядит следующим образом: (каждое значение байта представляет свой индекс)
00010203 - XOR - 04050607
04050607 - XOR - 08090A0B
08090A0B - XOR - 0C0D0E0F
…
Это можно упростить, записав всю полезную нагрузку в регистр, а затем применив XOR для полезной нагрузки к самой себе с 4-байтами, отброшенными с самого начала.
Мы можем сделать это в CyberChef, используя следующие операции:
Последний XOR с 0x2E используется с версией 4 CobaltStrike, тогда как 0x69 используется с версией 3.
Это ужасно!
Обычно этого достаточно, чтобы опытный глаз мог выбрать полезные данные и начать с контрмер, однако мы создадим рецепт с более приятным выходом в формате JSON для более широкой привлекательности.
Чтобы присвоить имена значениям полей JSON, нам нужно будет перечислить известные флаги. (На самом деле это не флаги. См .: Лестница Витгенштейна, если это описание вас беспокоит.)
Для целей этой записи флаги будут скопированы из JPCERT’s cobaltstrikescan.py (шестибайтовые шестнадцатеричные значения в cfg_info), вы можете легко расширить рецепт, используя другие флаги.
Нам нужно будет создать общее регулярное выражение для захвата желаемых значений конфигурации, а также нулевого совпадения. («Нулевое совпадение» здесь означает строку, которая удовлетворяет регулярному выражению данными, которые мы распознаем как имеющие значение NULL.)
Нулевое совпадение требуется для того, чтобы делать правильные предположения относительно имен регистров (они следуют схеме именования $ R0, $ R1,…, $ RN, но только если совпадают все группы захвата, чтобы не изменять все последующие индексы).
Возможные кандидаты в универсальные регулярные выражения:
Строки: (?: FLAG) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Числа: (?: FLAG) ((?: [0–9A- F] {2} \ s) {NUM_BYTES} | (?: ZZ ZZ ZZ ZZ))
Потенциальный универсальный кандидат с нулевым соответствием:
ФЛАГ ZZ ZZ ZZ ZZ
Извлечение строковых полей:
C2 Server | (?: 00 08 00 03 01 00) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Пользовательский агент | (?: 00 09 00 03 00 80) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Путь метода HTTP 2 | (?: 00 0a 00 03 00 40) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Заголовок 1 | (?: 00 0c 00 03 01 00) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Заголовок 2 | (?: 00 0d 00 03 01 00) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Процесс впрыска | (?: 00 0e 00 03 00 40) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Имя канала | (?: 00 0f 00 03 00 80) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Метод 1 | (?: 00 1a 00 03 00 10) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Метод 2 | (?: 00 1b 00 03 00 10) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Создать в x86 | (?: 00 1d 00 03 00 40) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Создать в x64 | (?: 00 1e 00 03 00 40) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Имя хоста прокси | (?: 00 20 00 03 00 80) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Имя пользователя прокси | (?: 00 21 00 03 00 40) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Пароль прокси | (?: 00 22 00 03 00 40) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Тип доступа к прокси | (?: 00 23 00 01 00 02) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
CreateRemoteThread | (?: 00 24 00 01 00 02) ((?:. *?) (? = 00) | (?: ZZ ZZ ZZ ZZ))
Нулевое совпадение строки:
C2 Server | 00 08 00 03 01 00 ZZ ZZ ZZ ZZ
Пользовательский агент | 00 09 00 03 00 80 ZZ ZZ ZZ ZZ
Путь метода HTTP 2 | 00 0a 00 03 00 40 ZZ ZZ ZZ ZZ < br /> Заголовок 1 | 00 0c 00 03 01 00 ZZ ZZ ZZ ZZ
Заголовок 2 | 00 0d 00 03 01 00 ZZ ZZ ZZ ZZ
Процесс впрыска | 00 0e 00 03 00 40 ZZ ZZ ZZ ZZ
Название трубы | 00 0f 00 03 00 80 ZZ ZZ ZZ ZZ
Метод 1 | 00 1a 00 03 00 10 ZZ ZZ ZZ ZZ
Метод 2 | 00 1b 00 03 00 10 ZZ ZZ ZZ ZZ
Создать в x86 | 00 1d 00 03 00 40 ZZ ZZ ZZ ZZ
Spawn To x64 | 00 1e 00 03 00 40 ZZ ZZ ZZ ZZ
Имя хоста прокси | 00 20 00 03 00 80 ZZ ZZ ZZ ZZ
Имя пользователя прокси | 00 21 00 03 00 40 ZZ ZZ ZZ ZZ
Пароль прокси | 00 22 00 03 00 40 ZZ ZZ ZZ ZZ
Тип доступа к прокси | 00 23 00 01 00 02 ZZ ZZ ZZ ZZ
CreateRemoteThread | 00 24 00 01 00 02 ZZ ZZ ZZ ZZ
Извлечение числовых полей:
Порт | (?: 00 02 00 01 00 02) ((?: [0–9A-F] {2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
Джиттер | (?: 00 05 00 01 00 02) ((?: [0–9A-F] {2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
Макс. DNS | (?: 00 06 00 01 00 02) ((?: [0–9A-F] {2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
Год | (?: 00 10 00 01 00 02) ( (?: [0–9A-F] {2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
Месяц | (?: 00 11 00 01 00 02) ((?: [ 0–9A-F] {2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
День | (?: 00 12 00 01 00 02) ((?: [0–9A- F] {2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
Спящий режим DNS | (?: 00 14 00 02 00 04) ((?: [0–9A-F] { 2} \ s) {2} | (?: ZZ ZZ ZZ ZZ))
Опрос | (?: 00 03 00 02 00 04) ((?: [0–9A-F] {2} \ s ) {2} | (?: ZZ ZZ ZZ ZZ))
DNS Idle | (?: 00 13 00 02 00 04) ((?: [0–9A-F] {2} \ s) {4 } | (?: ZZ ZZ ZZ ZZ))
Числовое нулевое совпадение:
Порт | 00 02 00 01 00 02 ZZ ZZ ZZ ZZ
Джиттер | 00 05 00 01 00 02 ZZ ZZ ZZ ZZ
Макс DNS | 00 06 00 01 00 02 ZZ ZZ ZZ ZZ
Год | 00 10 00 01 00 02 ZZ ZZ ZZ ZZ
Месяц | 00 11 00 01 00 02 ZZ ZZ ZZ ZZ
День | 00 12 00 01 00 02 ZZ ZZ ZZ ZZ
Спящий режим DNS | 00 14 00 02 00 04 ZZ ZZ ZZ ZZ
Опрос | 00 03 00 02 00 04 ZZ ZZ ZZ ZZ
DNS Idle | 00 13 00 02 00 04 ZZ ZZ ZZ ZZ
Регулярные выражения в разделе «Извлечение строковых и числовых полей» следует скопировать в отдельные операции с регистрами, как показано здесь:
Все связанные нулевые совпадения должны быть вставлены в конец входного буфера.
Теперь мы можем создать тело JSON, оставив имена переменных для быстрой модификации:
После захвата всех значений конфигурации мы можем заменить их их отформатированными версиями в строковом конкатенированном большом двоичном объекте JSON для упрощения вставки.
Как и все рецепты, надеюсь, вы измените его по своему вкусу. Например, я не закончил писать отдельные обработчики для каждого числового типа.
Если полезная нагрузка вашего маяка не извлекается с помощью этого рецепта, вероятно, она не зашифрована, чтобы исправить это, просто удалите расшифровку XOR, оставив последнюю 0x2e / 0x69 XOR.
Со временем на этой странице будут внесены некоторые изменения и улучшения.
Удачи.
"Рецепт блюда"
Компактный JSON для загрузки:
https://pastebin.com/t0YZetBs