You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
16 KiB
301 lines
16 KiB
НЕРЕЗИДЕНТНЫЙ ЛОАДЕР, ТРЕБОВАНИЯ
|
|
|
|
ЦЕЛЬ
|
|
|
|
Нужен загрузчик не из паблика, ранее неизвестный для АВ.
|
|
Максимально приветствуются нестандартные подходы к решению задачи доставки и запуска произвольной нагрузки.
|
|
|
|
ДОСТАВКА
|
|
|
|
Приветствуются новые методы доставки и обхода 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
|
|
}
|