НЕРЕЗИДЕНТНЫЙ ЛОАДЕР, ТРЕБОВАНИЯ ЦЕЛЬ Нужен загрузчик не из паблика, ранее неизвестный для АВ. Максимально приветствуются нестандартные подходы к решению задачи доставки и запуска произвольной нагрузки. ДОСТАВКА Приветствуются новые методы доставки и обхода SmartScreen: 1. использование нативных утилит ОС для скачивания нагрузки https://lolbas-project.github.io/ 2. использование стегано, нестандартных типов нагрузок (картинки, легит.файлы, упаковка под видом сертификатов, инсталляторы и пр.), как вариант в комбинации с простейшим загрузчиком нулевой ступени (на скриптах). Под загрузчиком нулевой ступени имеется в виду ситуация, когда юзера каким-либо образом побуждают скопировать с сайта/ввести вручную и выполнить короткий скрипт. 3. использование нативных утилит ОС для сборки и запуска нагрузки ТРЕБОВАНИЯ К ЛОАДЕРУ 1. Без закрепления 1.1. Без сохранения состояния на диске, в реестре или где-либо еще 2. Идентификация клиента в общем случае необязательна, для этого достаточно IP-адреса. Однако, если идентификатор клиента генерируется, к нему выставляется требование: Генерация одного и того же идентификатора на одном и том же компьютере, вне зависимости от текущего пользователя и прочих обстоятельств. Предлагаемый вариант - hardware id (хэш от суммы оборудования). Формат идентификатора не оговаривается. 2.1. Клиент также идентифицирует принадлежность себя к определенной группе (ботнету). Группа нужна для удобства эксплуатации - чтобы логически отделить одних ботов от других. Группа прошивается в лоадер в момент сборки и в течение времени жизни конкретного экземпляра лоадера не меняется никогда. 2. Разрядности x86/x64. Приоритетная разрядность x64. Запуск нагрузки только той же разрядности, что сам лоадер. 3. Сбор инфы о системе: - имя хоста - имя пользователя - версия и разрядность ОС - имя и версия АВ - результат команды net view /all - результат команды net view /all /domain - результат команды nltest /domain_trusts /all_trusts - IP-адрес получать НЕ ОБЯЗАТЕЛЬНО - это может быть сделано средствами бэкенда 4. Без повышения прав 4.1. Работа от непривилегированного пользователя. 5. Без обхода UAC 6. Возможность выполнения простой программы по запуску нагрузки: 6.1. Повторный запуск процесса при вылетах 6.2. Выдерживание циклов и таймаутов (возможность указать, сколько раз перестартуем, интервалы между попытками перезапуска) Общий смысл этого пункта: не всегда нагрузка запускается с первого раза; бывает, что нагрузка вылетает из-за ошибок; бывает, что одну нагрузку грохает АВ, а другую нет. Потому у нас всегда есть планы Б и В. Следует учесть, что админка может отдавать РАЗНЫЕ нагрузки при последовательных запросах на закачку загрузки. 7. Запуск как .exe, так и .dll 8. Запуск нагрузки бесфайловой техникой (process hollowing, dll from memory и другими подобными) 9. Мимикрия нагрузки под легитимный процесс 10. Использование открытых каналов связи для обмена с C&C (HTTPS, DNS, почта итд) 11. Мимикрия сетевой активности под легитимный процесс (странно видеть сетевые запросы от cmd.exe или WerFault.exe, буде вы решите хоститься в них) 12. Шифрование трафика; простая схема распределения ключей. Цель шифрования - защита от машинного анализа, DPI. Простая достаточно для реализации и генерации ключей, и сложная достаточно, чтобы нельзя было скачать нагрузку в броузере или прикрутить нужный диссектор в DPI. 13. Защита от машинного анализа (обфускация строк, системных вызовов; разбавление низкой энтропии шумом итд). Тут речь идет про защиту от анализа как человеком, так и АВ: 13.1. Усложняем работу по раскрытию алгоритмов работы 13.2. Сбиваем с толку эмулятор АВ 13.3. Защита от ханипотов и продвинутых песочниц допустима, только если вы предоставляете доказательства её эффективности. Как правило тут идет речь о получении характерных для песочниц ID устройств, hostname, виртуальной машины итд. Однако по отдельности эти факторы могут ничего не значить и давать ложно-позитивные сработки, тем самым просаживая трафик. 14. Наличие билдера необязательно, если вы готовы к постоянному сотрудничеству впоследствии. Но вообще мы хотим исходники. 15. ОС - Windows Server 2008 R2+, Windows Vista+ - не ниже данных версий, включая все современные десктопные и серверные винды. Если вы сделаете совместимость с Windows Server 2008 (без R2, то есть фактически это XP), это будет очень круто. 16. Компиляция в нативный код, либо (в случае скриптовых языков или .Net) лоадер должен выполняться на голой ОС. Не должно быть зависимостей. Если они крайне необходимы ради крутой идеи (например, вы решили выполняться в виртуальной машине и для этого поставить VMWare Player), то они зашиты в ЕДИНЫЙ файл. PowerShell - не выше 2.0. Допускаются любые связки cscript, jscript, .bat, при условии компоновки в единый файл. Другими словами, это должен быть один файл, и он должен работать на любых виндах без расчета на то, что у нас тут стоит .Net или Microsoft Visual C++ Redistributable нужной версии. 17. Совместимость с криптами (для нативного кода): 17.1. Сборка самого лоадера как в .exe, так и в .dll 17.2. Умеренный размер (скажем, до 1М) 17.3. Минимизация CRT/STL (не все крипты переваривают). ПРИЕМОЧНЫЙ ТЕСТ Приемка производится исходя из отсутствия у разработчика полной интеграции с админкой, для возможности гаранта самостоятельно оценить работу. Соответственно, разработчик делает демо для гаранта на своих ресурсах (VPS, виртуалках итд). 0. На тест предоставляются 1 (один) файл лоадера и файл(ы) шлюза админки + нужные для него заглушки (см.ниже), которые использованы разработчиком для внутренних тестов. Если файл компилируется в нативный .exe, дополнительно должна предоставляться .dll - для демонстрации наличия профиля сборки в .dll. В таком случае .dll запускается через rundll и также тестируется. Исходники на этом этапе не нужны, может быть какая угодно обфускация/криптование. Шлюз админки разворачивается на сервере заказчика. Настройка производится совместно заказчиком и разработчиком. Демо производится также на арендованном заказчиком VPS. Учтите, что слово V в VPS - значит virtual (виртуальная машина). 1. Статика на dyncheck.com должна быть чистой. 2. Не более 4 детектов по поведению на момент приемки на dyncheck.com. Настройки dyncheck: 240 секунд теста, полный доступ к интернету. 2.1. Отсутствие среди динамических детектов - Windows Defender - ESET Nod32 - Avast Home - Kaspersky Antivirus - Bitdefender 3. В качестве нагрузки должны проверяться поочередно несколько файлов: 3.1. putty.exe 3.2. notepad.exe 3.3. DbgView.exe https://docs.microsoft.com/en-us/sysinternals/downloads/debugview Дополнительно заказчик предложит несколько других файлов не из паблика. Это будут простейшие заглушки типа MessageBox("hello, world!"), статически скомпонованные, без зависимостей. Возможно, с минимальным использованием WinApi (функции GDI). Цель этого - продемонстрировать, что лоадер способен работать с произвольной нагрузкой. Данные файлы размешаются на веб-сервере заказчика. 4. Тест производится поочередно на двух компьютерах с включенными АВ (Windows Defender, ESET Nod32) и произвольной версией Windows из требований. Антивирусные базы обновлены, облачная защита выключена, отправка образцов выключена. Тестируются поочередно все предоставленные нагрузки. 5. Лоадер в виде "как есть" (без упаковки в архив; если это .exe - то значит .exe и качаем) скачивается с файлообменника qaz.im (или другого оговоренного) в броузере Chrome последней на момент версии. Не должно быть предупреждений о вирусе ни от броузера, ни от антивируса. 6. Лоадер запускается. Если не будет возможности подписать файл лоадера EV-сертификатом для запуска прямо из Chrome, то вопли Chrome о недоверии к файлу игнорируются, файл запускается руками из Проводника. 7. Лоадер должен скачать и запустить нагрузку. Факт скачивания мониторится на веб-сервере. Нагрузка должна проработать на компьютере минимум час и не быть снесена броузером/АВ. Тесты поочередно проводятся для каждой представленной нагрузки, с пересборкой лоадера если необходимо. Мы понимаем, что современные АВ очень умные, последовательными тестами мы только помогаем им обучаться, и на каком-то этапе может появиться детект там где его не было. И все же, этим мы имитируем реальную работу, а вы демонстрируете живучесть решения. При необходимости мы можем сменить у VPS адрес и hardware id, чтобы снять ассоциации у АВ с конкретным демо-стендом. ШЛЮЗ К АДМИНКЕ Админка уже есть. Админка предоставляет интерфейс на PHP для подключения к ней. Объект админки называется $adm и является глобалом. Сам шлюз должен быть оформлен в виде класса на PHP с определенным набором публичных методов (т.е. проэкспортировать свой интерфейс для админки). Шлюз идентифицируется в админке по имени. В случае использования отличных от HTTP протоколов для общения с лоадером, шлюз будет напрямую на нашу HTTP-прокладку, протокол для неё запрашивается и предоставляется отдельно. Это более сложный случай, мы поможем с такой интеграцией. В случае протокола поверх HTTP, способ интеграции с админкой описан ниже. ИНТЕРФЕЙС БЭКЕНДА 1. Регистрация настройки $adm->register_property($plugin_name, $prop_name, $length) зарегистрировать настройку для данного шлюза в админке $plugin_name: строка с идентификатором шлюза $prop_name: строка с идентификатором свойства $length: максимальная длина поля TODO: возможно в будущем добавятся также тип поля и параметры отображения в админке (тип виджета). Настройка хранится в двоичном виде в БД (это значит, что вам не нужно ее как-либо экранировать при сохранении). Возвращает true в случае успеха, false в случае ошибки. Свойство появляется в диалоге настройки группы (ботнета) в админке. То есть настройка меняется в разрезе группы. Например, в своей реализации протокола вы решили использовать разные имена HTTP-заголовков, URI-префиксов и прочих свойств HTTP-запроса, для разных групп. В таком случае вы регистрируете нужные настройки, в обработчике запроса получаете их значения для той группы, к которой принадлежит клиент. Дальнейший разбор запроса происходит с учетом этих данных. Настройками могут быть ключи шифрования, номера портов, какие-либо адреса итд - все что вам понадобится для организации собственного протокола. 2. Получить настройку $adm->get_property($plugin_name, $prop_name, $group = null) получить свойство (настройку) с указанным именем; опционально указывается группа $plugin_name: строка с идентификатором шлюза $prop_name: строка с идентификатором свойства $group: идентификатор группы (ботнета), опционально. Если не указано, вернется значение настройки по умолчанию без разреза группы. Возвращает значение настройки. 3. Получить список ботнетов $adm->get_groups($plugin_name) получить массив со списком групп, доступных для данного плагина $plugin_name: строка с идентификатором шлюза Возвращает словарь словарей: "имя группы" => "словарь" { "имя настройки1" => "значение настройки1" ... "имя настройкиN" => "значение настройкиN" } 3. Получить нагрузку $adm->get_payload($bitness, $group) получить нагрузку из заданной группы с указанной разрядностью $bitness: принимает два возможных значения 86 и 64 (int) $group: идентификатор группы Возвращает нагрузку, которую следует отдать лоадеру 4. Регистрация отстука $adm->knock($group, $id = null) регистрация отстука с заданного адреса $group: идентификатор группы $id: идентификатор компьютера (опционально) Ничего не возвращает. 5. Регистрация информации и системе $adm->put_sysinfo($group, $info, $id = null) информация о компьютере $group: идентификатор группы $id: идентификатор компьютера (опционально) $info: словарь с информацией о системе. Поля у словаря следующие: hostname: имя хоста uname: имя пользователя os: название ОС os_bitness: разрядность ОС os_version: версия ОС av: имя и версия АВ net: результат команды net view /all netdomain: результат команды net view /all /domain trust: результат команды nltest /domain_trusts /all_trusts Словарь расширяемый, если хотите передать еще что-нибудь полезное. Функция ничего не возвращает. 6. Отправить телеметрию $adm->put_telemetry($group, $data, $id = null) телеметрия с отстука $group: идентификатор группы $id: идентификатор компьютера (опционально) $data: произвольное сообщение от лоадера Примечание Смысл телеметрии - удаленная отладка, и сведения о работе лоадера. Неплохо было бы узнать: - о том что лоадер скачал нагрузку - о том что он её запустил - о произошедших ошибках (например, процесс нагрузки быстро помер) - о повторных попытках запуска нагрузки - о других событиях ОБРАБОТКА ОШИБОК Если вызывающий контекст обнаруживает какую-либо ошибку при работе через API, и если эта ошибка особо не оговорена, обработка прерывается. Либо молча (в боевых условиях), либо попытается вам сказать почему (если включен error_reporting). ИНТЕРФЕЙС ШЛЮЗА Шлюз - это класс на PHP, без зависимостей в виде фреймворков. Шлюз будет выполняться в контексте некоего фреймворка, но на это не следует рассчитывать. Шлюз экспортирует две публичные функции: 1. Инсталляция в систему $gate->install() Тут происходит регистрация настроек, нужных шлюзу. 2. Обработка запроса $gate->process_request($HTTP_request) Обрабатывает входящий запрос и формирует ответ. $HTTP_request: входящий запрос со всеми заголовками Возвращает словарь: code => HTTP-код ответа (число или строка) response_headers => HTTP-заголовки сформированного ответа. Если ответ не 200, могут быть пустыми. response_body => HTTP-тело сформированного ответа. Если ответ не 200, может быть пустыми. Если ответ 400 - это значит что шлюз не опознал запрос как валидный. Если ответ 404 - это значит что шлюз в целом опознал запрос как валидный, но не смог сопоставить его с группой. Если ответ 200 - это значит что ответ полностью обработан, и дальнейшая обработка не требуется. Любые другие коды ответа трактуются как ошибка обработки; на этом обработка ответа для данного шлюза завершена. PHP Версия PHP - не ниже 7.0. Можно рассчитывать на все распространенные плагины. Если нужно что-либо кастомное - включим или соберем. ПРИМЕР РЕАЛИЗАЦИИ ШЛЮЗА (PHP-ПОДОБНЫЙ ПСЕВДОКОД) require_once "adm_api.php"; // отсюда взяли объект $adm class sample_gate { string $plugin_name = "Plagin Vasi"; // функция регистрации плагина в системе // эта функция экспортируется из класса (публичная) function install() { // регаем настройку (поле) с таким-то именем и такой-то длиной // Например, хотим накрыть нагрузку шифром, дабы лоадер ее расшифровал на том конце $adm->register_property($plugin_name, "RSA_public_key", 4096); } // обработка одного запроса, сюда передается целиком HTTP-запрос // эта функция экспортируется из класса (публичная) // в ответ получаем массив с HTTP-кодом, заголовками и телом ответа function process_request(string $request) { // хочу от админки список доступных Васе групп, с их префиксами и всей херней array $groups = $adm->get_groups_list(); // разобрал запрос на части, сделал предварительную валидацию if(not valid request) return array("code=>"400"); // bad request; после этого вызывающий контекст может продолжить поиск подходящего плагина для этого запроса foreach($g => $groups) { if(request matches this group) { $adm->knock($REMOTE_ADDR); if(request is telemetry) $adm->put_telemetry($g, $message_from_request); else if(this is sysinfo) { $info = get_sysinfo_from_request(); $adm->put_sysinfo($g, $info); } else if(request for payload) { $bitness = figure out bitness from the request; $payload = $adm->get_payload($bitness, $g); $encryption_key = $adm->get_property($plugin_name, "RSA_public_key"); $HTTP_body = encrypt_payload($payload, $encryption_key); $HTTP_headers = arrange_headers_somehow(); } return array("code=>"200", "response_headers" => ...); } } return array("code=>"404"); } // дальше идут приватные функции класса, о которых админка знать не хочет function encrypt_payload() {} function arrange_headers_somehow() {} //etc }