
Имплант скрытого канала DNS для Red Teams.
WEASEL — это небольшой имплант, работающий в памяти, на Python 3 без зависимостей. Клиент-маяк отправляет небольшой объём идентифицирующей информации о своём хосте на DNS-зону, которую вы контролируете. Сервер WEASEL может давать клиентам команды на выполнение предварительно подготовленных или произвольных команд.
WEASEL — это нагрузка первого этапа, предназначенная для того, чтобы её было трудно обнаружить, и полезная для восстановления доступа, когда ваши шумные полнофункциональные этапы перехвачены.
Статус
Инструкции по использованию см. в README клиента и README сервера.
Для запуска сервера или клиента выполняйте скрипты напрямую или передавайте их интерпретатору Python.
Убедитесь, что для каждого C2-домена установлена NS-запись с IP-адресом хоста, на котором выполняется server.py.
WEASEL требует Python версии 3.6+.
Клиент является самодостаточным и использует только стандартные библиотеки, поэтому может работать на macOS, Linux и т.д.
У сервера есть несколько зависимостей из pip, включённых в requirements.txt сервера. Сервер должен запускаться на Linux, но нет никаких препятствий для работы на macOS или других *nix.
В этом случае нет необходимости обфусцировать и минимизировать маяк. Операторы print сохраняются. Как и в разделе «Использование» выше, убедитесь, что NS-записи для домена(ов) в servers в beacon.py указывают на IP-адрес server.py.
На хосте сервера:
sudo python3 server.py
На хосте жертвы (может быть тем же, что и сервер):
python3 beacon.py
Вам не нужно понимать всё это, чтобы использовать WEASEL.
Маяк общается по DNS с помощью AAAA-запросов и ответов. Он не использует TXT-записи, так как известно, что они используются DNS-вредоносным ПО и туннелями. Синие команды часто имеют детекции DNS-туннелирования, которые срабатывают на большие TXT-запросы.
Клиентская сторона не требует прав root, не использует raw-сокеты и не создаёт некорректные DNS-пакеты. Она использует обычные системные и языковые интерфейсы для выполнения DNS-запросов. Информация кодируется и шифруется в самих записях.
Этот маяк предназначен для работы в медленном и тихом режиме, с малой пропускной способностью. Он должен сообщать нам, на каких хостах он находится, и давать возможность запускать дальнейшие этапы по мере необходимости, и не более. Хотя он поддерживает выполнение произвольных команд, он не предназначен для использования в качестве обычного интерактивного шелла или канала связи.
WEASEL — это первый этап, который вы оставляете работающим, обеспечивая постоянный доступ, когда ваши полнофункциональные (и, следовательно, более шумные) этапы перехватываются.
WEASEL изначально был нацелен на серверы с высоким временем безотказной работы, где у нас был надёжный вектор доступа/эксплуатации. Обход криминалистики был приоритетом. В результате он не имеет собственных механизмов постоянства.
Вы можете сделать его постоянным, добавив его выполнение в вашу любимую технику обеспечения постоянства — это оставлено в качестве упражнения для читателя :)
Запрос (от клиента) — это одиночный AAAA-запрос для имени, отформатированного как:
<preamble><data>.<stream>.<session>.domain.tld
Преамбула занимает 2 байта. Преймбул[0] — порядковый номер этого пакета. Преймбул[1] — общее количество пакетов в этом потоке.
Данные ограничены 50 байтами (настраивается) и содержат полезную нагрузку. Полезная нагрузка кодируется в base32 с использованием пользовательского алфавита.
Сначала все символы 'w' заменяются на '-'.
Затем символ заполнения заменяется с '=' на 'w' для соответствия набору символов DNS: [a-z0-9] и [-].
Мы не заменяем '=' на '-' напрямую, потому что заполнение всегда будет в конце строки, а завершение имени хоста на '---' подозрительно и противоречит DNS RFC. Таким образом, когда строка имеет заполнение, она будет заканчиваться на 'www', что менее подозрительно и соответствует RFC.
Ответ (от сервера) состоит из одного или нескольких AAAA-ответов.
Каждый AAAA-ответ представляет собой 16-байтовую зашифрованную полезную нагрузку, представленную как IPv6-адрес с помощью socket.inet_ntop. Ответы в DNS-ответе не сохраняют свой порядок при передаче, поэтому они упорядочиваются и собираются заново, как и запросы клиента.
Транспортная полезная нагрузка — это строка элементов данных, разделённых символом ^.
Запросы и ответы следуют этому формату:
<type>|<data>
Сессии долгоживущие: клиент инициирует сессию при первом запуске маяка, и эта сессия должна длиться всё время работы маяка на этом клиенте. Обратите внимание, что поскольку маяк работает в памяти и не является постоянным, данные сессии хранятся в памяти этого процесса Python. Любой новый запуск маяка инициирует новую сессию.
Инициирование сессии включает создание клиентом сообщения с уникально идентифицирующей преамбулой, не содержащей данных (чтобы сигнализировать серверу, что это новая сессия): конкатенация 32-байтового открытого ключа Диффи-Хеллмана и 16-байтового случайного AES IV.
Сервер принимает это и отвечает своим собственным 32-байтовым открытым ключом. На этом этапе клиент и сервер устанавливают общий ключ сессии, который будет использоваться в течение всей жизни этой сессии для шифрования полезных нагрузок данных с помощью AES-128 в режиме CTR. Обмен эфемерными ключами Диффи-Хеллмана гарантирует, что каждое соединение клиент-сервер использует уникальный ключ сессии с прямой секретностью.
Криптография намеренно плохая по ряду причин:
Вот некоторые известные проблемы криптосхемы:
p — это RFC 3526 Группа 5, усечённая до первых 32 байт. Это не только ограничивает открытые и закрытые ключи 32 байтами, но и Группа 5 уже устарела и не рекомендуется. Я называю это плохое решение «Группой 1».a вместо CSPRNG.Каждое сообщение, отправляемое между клиентом и сервером, должно быть разбито на пакеты максимум по 50 байт, чтобы оставаться под лимитом в 52 байта для обычных детекций скрытых каналов DNS. Все пакеты конкретного сообщения являются частью одного потока. Сообщение == Поток.
Потоки идентифицируются 2-байтовым случайным hex-числом. Напомним, формат запроса клиента:
<preamble><data>.<stream>.<session>.domain.tld
2-байтовая преамбула каждого пакета в потоке содержит порядковый номер и общее количество пакетов в потоке. Это позволяет серверу узнать, когда всё прибыло.
Поскольку это DNS, всё происходит поверх UDP, который не даёт никаких гарантий относительно порядка прибытия дейтаграмм. Вот почему WEASEL должен учитывать упорядочивание, сборку и отслеживание нескольких потоков от многих маяков.
Каждый поток повторно инициализирует глобально общий шифр AES-128-CTR для шифрования/дешифрования полезных нагрузок.
Полезную нагрузку можно расшифровать только после завершения потока (прибытия всех пакетов). Если бы не base32, мы могли бы расшифровать то, что у нас есть от сообщения, даже если бы отсутствовали пакеты (потому что AES-CTR — поточный шифр), но мы не можем декодировать base32 для частичных потоков. Ну что ж. Из-за природы DNS-клиентов запросы выполняются несколько раз (обычно 2 или 4 раза), пока не будет получен ответ, поэтому у нас есть хорошая вероятность получить все пакеты потока, так как каждый пакет должен быть отправлен клиентом как минимум дважды. Если мы потеряем пакеты или потоки, это не страшно — маяк проверит связь позже и, вероятно, ему больше повезёт.
См. файл CONTRIBUTING о том, как помочь.
WEASEL лицензирован по лицензии MIT, как указано в файле LICENSE.
| Тип | Значение (отправитель) | Также известен как | Данные |
|---|
| 0 | Подтверждение | ACK | Случайный hex |
| 1 | Проверка связи (клиент) | PING | Случайный hex |
| 2 | Завершить себя (сервер), завершаю себя (клиент) | FIN | |
| 3 | Инициализационное сообщение (клиент) | SYN | version|hostname|kernel |
| 4 | Переподключение (сервер) | RST | |
| 5 | Установить интервал обратного вызова (сервер) | секунды | |
| 6 | Получить данные о сетевом интерфейсе | eth0 1.2.3.4/24\neth1 fe80:::/64\n... | |
| 8 | Выполнить произвольный код Python3 размером до 666 байт (сервер), вернуть первые 400 байт вывода (клиент) | EVAL | скрипт python3 в одну строку |
| 9 | Выполнить произвольную команду размером до 666 байт (сервер), вернуть первые 400 байт вывода (клиент) | EXEC | bash-команда |