Введение В первой части статьи Мы дали краткое описание механизма зашифрованного SNI (eSNI).
Они показали, как на его основе можно уклониться от обнаружения современными системами DPI (на примере Beeline DPI и запрещенного корневого трекера RKN), а также исследовали новую версию доменного фронтинга, основанную на этом механизме.
Во второй части статьи мы перейдем к более практическим вещам, которые пригодятся специалистам RedTeam в их непростой работе.
В конце концов, наша цель — не получить доступ к заблокированным ресурсам (для таких тривиальных вещей у нас есть старый добрый VPN).
К счастью, существует великое множество VPN-провайдеров, что называется, на любой вкус, цвет и кошелек.
Мы постараемся применить механизм доменного фронтинга к современным инструментам RedTeam, например, таким как Cobalt Strike, Empire и т. д., и предоставить им дополнительные возможности для имитации и обхода современных систем фильтрации контента.
В прошлый раз мы реализовали механизм eSNI в библиотеке OpenSSL и успешно использовали его в знакомой утилите Curl. Но, как говорится, одной курицей не удовлетворишься.
Конечно, хотелось бы реализовать нечто подобное на языках высокого уровня.
Но, к сожалению, быстрый поиск по Интернету нас разочаровывает, ведь поддержка механизма eSNI полностью реализована только в GOLANG. Таким образом, у нас нет особого выбора: либо пишем на чистом C или C++ с использованием пропатченной библиотеки OpenSSL, либо используем отдельный форк GOLANG от CloudFlare и пытаемся портировать туда свои инструменты.
В принципе, есть еще один вариант, более классический, но в то же время трудоемкий — реализовать поддержку eSNI для Python. В конце концов, Python также использует OpenSSL для обработки https. Но мы оставим этот вариант для разработки кому-то другому, а сами будем довольствоваться реализацией на Golang, тем более, что наш любимый Cobalt Strike прекрасно умеет работать с каналом связи, построенным сторонними инструментами (Внешний канал C2).
– об этом мы поговорим в конце статьи.
Стараться.
Одним из инструментов, реализованных в Go, является наша разработка для разворота в сети — туннелер.
rsockstun , который, кстати, сейчас детектируется инструментами Microsoft и Symantec как весьма вредоносное ПО, направленное на нарушение глобальной стабильности.
Было бы здорово использовать предыдущую разработку и в этом случае.
Но тут возникает небольшая проблема.
Дело в том, что изначально rsockstun подразумевает использование синхронного SSL-канала связи с сервером.
Это означает, что соединение устанавливается один раз и существует на протяжении всего времени работы туннеля.
И, как вы понимаете, протокол https не предназначен для такого режима работы — он работает в режиме запрос-ответ, где каждый новый http-запрос существует в рамках нового TCP-соединения.
Основным недостатком этой схемы является то, что сервер не может передать данные клиенту, пока клиент не отправит новый http-запрос.
Но, к счастью, есть много вариантов решения этой проблемы – потоковая передача данных по протоколу http (ведь мы как-то успеваем смотреть любимые сериалы и слушать музыку с порталов, работающих по https, а вот передача видео и аудио – это не что иное, как чем потоковая передача данных).
Одной из технологий эмуляции работы полноценного TCP-соединения по протоколу HTTP является технология WebSockets, основная суть которой заключается в организации полноценного сетевого соединения между клиентом и веб-сервером.
К нашему счастью (ура!!!), эта технология включена по умолчанию во все тарифные планы CloudFlare и прекрасно работает в сочетании с eSNI. Это именно то, что мы будем использовать, чтобы научить нашего туннелера использовать доменное управление и прятаться от современных DPI.
Немного о вебсокетах
Прежде всего, мы коротко и простыми словами поговорим о вебсокетах, чтобы каждый имел представление о том, с чем мы будем работать.Технология Websocket позволяет временно переключиться с http-соединения на стандартную потоковую передачу сетевых сокетов, не разрывая установленное TCP-соединение.
Когда клиент хочет переключиться на веб-сокет, он устанавливает несколько заголовков http в своем http-запросе.
Два обязательных заголовка - Подключение: Обновление И Обновление: вебсокет .
Он также может принудительно указать версию протокола веб-сокета ( Версия Sec-Websockset: 13 ) и что-то вроде идентификатора веб-сокета base64 ( Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA== ).
Сервер отвечает ему http-кодом 101 Switching Protocols, а также устанавливает заголовки Подключение, Обновление И Sec-WebSocket-Accept .
Процесс переключения наглядно продемонстрирован на скриншоте ниже:
После этого установку WebSocket-соединения можно считать завершенной.
Любые данные как от клиента, так и от сервера теперь будут поступать не с http, а с заголовками WebSocket (они начинаются с байта 0x82).
Теперь серверу не нужно ждать запроса от клиента на передачу данных, т.к.
TCP-соединение не разорвано.
В Golang есть несколько библиотек для работы с веб-сокетами.
Самыми популярными из них являются Горилла Вебсокет и стандарт Вебсокет .
Мы воспользуемся последним, потому что.
он проще, меньше и, как говорится, работает немного быстрее.
В коде клиента rsockstun нам необходимо заменить вызовы net.dial или tls.dial соответствующими вызовами WebSocket:
Мы хотим сделать клиентскую часть нашего туннеля универсальной и способной работать как через прямое SSL-соединение, так и через протокол WebSockset. Для этого создадим отдельную функцию func ConnectForWsSocks (строка адреса, строка прокси) ошибка {…} по аналогии с ConnectForSocks() и мы будем использовать его для работы с веб-сокетами, если адрес сервера, указанный при запуске клиента, начинается с ws: или wss: (в случае Secure WebSocket).
Для серверной части туннеля также сделаем отдельную функцию для работы с веб-сокетами.
Он создаст экземпляр класса http и установит обработчик http-соединения (функция wsHandler):
А всю логику обработки соединения (авторизацию клиента по паролю, настройку и завершение сеанса yamux) мы поместим в обработчик соединения WebSocket:
Компилируем проект и запускаем серверную часть:
И затем клиентская часть:.
/rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rd
.
/rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rd
И проверяем работу на локальном хосте:
Перейдем к фронтингу домена
С вебсокетами мы, кажется, разобрались.Теперь перейдем непосредственно к eSNI и фронтингу домена.
Как говорилось ранее, для работы с DoH и eSNI нам необходимо взять у компании специальную ветку golang. ОблакоВспышка .
Нам нужна ветка с поддержкой eSNI (pwu/esni).
Клонируем его локально или скачиваем и распаковываем соответствующий zip: git clone -b pwu/esni https://github.com/cloudflare/tls-tris.git
Затем нам нужно скопировать каталог GOROOT, заменить соответствующие файлы из клонированной ветки и установить его как master. Чтобы избавить разработчика от этой головной боли, ребята из CloudFlare подготовили специальный скрипт — _dev/go.sh. Мы просто запускаем его.
Скрипт вместе с make-файлом все сделает сам.
Просто ради интереса вы можете заглянуть в make-файл и узнать подробности.
После запуска скрипта при компиляции проекта нам нужно будет указать подготовленный скриптом локальный каталог как GOROOT. В нашем случае это выглядит так: GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….
Далее нам необходимо реализовать в туннеле функционал запроса и парсинга публичных ключей eSNI для нужного домена.
В нашем случае это будут публичные ключи eSNI от фронтенд-серверов CloudFlare. Для этого создадим три функции: func makeDoTQuery(dnsName string) ([]byte, error)
func parseTXTResponse(buf []byte, wantName string) (string, error)
func QueryESNIKeysForHost(hostname string) ([]byte, error)
Названия функций, в принципе, говорят сами за себя.
Содержимое мы возьмем из файла esni_query.go, который является частью tls-tris. Первая функция создает сетевой пакет с запросом к DNS-серверу CloudFlare по протоколу DoH (DNS-over-HTTPS), вторая анализирует результаты запроса и получает значения открытых ключей домена, а третья представляет собой контейнер для первых двух.
Далее мы добавляем соединение через веб-сокет к нашей вновь созданной функции.
ConnectForWsSocks возможность запроса ключей eSNI для домена.
Там, где работает серверная часть, задаем параметры TLS, а также задаем имя фейкового «домена прикрытия»:
Здесь следует отметить, что изначально ветка tls-tris не была предназначена для использования доменного фронтинга.
Поэтому он не обращает внимания на поддельное имя сервера (пустое поле serverName отправляется как часть пакета client-hello).
Чтобы это исправить, нам придется добавить соответствующее поле FakeServerName в структуру TlsConfig. Мы не можем использовать стандартное поле ServerName структуры, поскольку оно используется внутренними механизмами tls и если оно отличается от исходного, то рукопожатие tls завершится ошибкой.
Описание структуры TlsConfig содержится в файле tls/common.go - нам нужно это исправить:
Дополнительно нам придется внести изменения в файл tls/handshake_client.go чтобы использовать наше поле FakeServerName при формировании рукопожатия TLS:
Вот и все! Вы можете скомпилировать проект и проверить работу.
Но прежде чем запустить сканирование, вам необходимо настроить учетную запись CloudFlare. Ну как сказать настройте — просто создайте аккаунт на Cloudflare и привяжите к нему свой домен.
Все функции, связанные с DoH, WebSocket и ESNI, включены в CloudFlare по умолчанию.
После обновления DNS-записей можно проверить работу домена, запросив ключи eSNI: dig +short txt _esni.df13tester.info
Если вы видите нечто подобное для своего домена, значит, у вас все работает и можно переходить к тестированию.
Запускаем Ubuntu VPS, например, на DigitalOcean. P.S. В нашем случае только что выданный провайдером IP-адрес VPS оказался в черных списках РКН.
Так что не удивляйтесь, если с вами произойдет нечто подобное.
Мне пришлось использовать VPN, чтобы получить доступ к моему VPS.
Копируем на VPS уже скомпилированный rsockstun (это, кстати, еще одна прелесть Голанга — можно скомпилировать проект самостоятельно и запустить его на любом Linux, соблюдая только разрядность системы) и запускаем серверную часть:
И затем клиентская часть:
Как мы видим, клиент успешно подключился к серверу через интерфейсный сервер CloudFlare с помощью веб-сокета.
Чтобы проверить, что туннель работает точно так же, как туннель, можно сделать запрос на завивку через локальный носок5, открытый на сервере:
Теперь посмотрим, что видит DPI в канале связи:
Сначала туннелер с помощью механизма DoH обращается к DNS-серверу Cloudflare за ключами eSNI для домена назначения (пакеты №1-19), а затем связывается с фронтенд-сервером и устанавливает TLS-соединение, скрываясь за доменом.
www.google.com (это значение по умолчанию, если при запуске клиента не указан поддельный домен).
Чтобы указать свой поддельный домен, необходимо использовать параметр -fronfDomain:
Теперь еще одна вещь.
По умолчанию в настройках учетной записи CloudFalre установлено значение «Гибкий SSL».
Это означает, что https-запросы от клиентов к внешним серверам Cloudflare будут пересылаться на наш сервер в незашифрованном виде (http).
Именно поэтому мы запускали серверную часть туннеля в не-ssl режиме (-listen ws:0.0.0.0), а не (-listen wss:0.0.0.0).
Для того, чтобы переключиться в режим полного шифрования, необходимо выбрать Полный , или Полный (строгий) если на сервере есть настоящий сертификат. После переключения режима мы сможем принимать соединения от CloudFlare по протоколу https. Не забудьте создать самозаверяющий сертификат для серверной части туннеля.
Проницательный читатель спросит: «А как насчет Windows-клиента? Ведь наверняка основное использование туннеля — это поднять обратное соединение с корпоративных машин и серверов, а там, как правило, всегда есть Windows. Как мне скомпилировать туннелер для Windows, да ещё и с определённым стеком TLSЭ» А теперь мы представим еще одну функцию, которая показывает, насколько удобен Golang. Мы компилируем для Windows напрямую из Kali, просто добавляя параметр GOOS=windows: GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"
Или 32-битная версия: GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"
Все! И больше никаких хлопот не нужно.
Это действительно работает!
Флаги компилятора –w и –s нужны для того, чтобы удалить ненужный мусор из исполняемого файла, сделав его на пару мегабайт меньше.
Кроме того, его можно упаковать с помощью UPX для дальнейшего уменьшения размера.
Вместо заключения
В статье на примере туннеля, написанного на Golang, мы наглядно продемонстрировали использование новой технологии доменного фронтинга, реализованной на довольно интересной особенности протокола TLS 1.3. Аналогичным образом можно адаптировать существующие инструменты, написанные на Golang, для работы через серверы CloudFlare, например Мерлин - знаменитый C2 или заставить CobaltStrike Beacon использовать доменное управление eSNI при работе с Teamserver через Внешний канал C2 , реализованный на Golang или на стандартном C++ с использованием исправленной версии OpenSSL, о которой мы говорили в последней части статьи.В общем, нет предела фантазии.
Пример с туннелером и CloudFlare представлен в виде концепции и о долгосрочных перспективах такого типа доменного фронтинга пока говорить сложно.
На данный момент только CloudFlare поддерживает eSNI и им, по идее, ничто не мешает отключить этот вид фронтинга и, например, разорвать tls-соединения, если SNI и eSNI не совпадают. В общем, будущее покажет. Но пока перспектива работы под «прикрытием kremlin.ru» выглядит весьма заманчивой.
Не так ли? Обновленный код туннелера, а также скомпилированные исполняемые exe-файлы находятся в отдельной ветке проекта на github .
Обо всех возможных проблемах туннелера лучше написать на странице проекта на GitHub. Теги: #информационная безопасность #Сетевые технологии #tls 1.3 #Криптография #шифрование #DoH #cloudflare #ESNI #domain-fronting #domain-fronting #rsockstun
-
Основная Информация О Веб-Хостинге
19 Oct, 24 -
Мотивация Ит-Специалистов
19 Oct, 24 -
Субъективные Различия Между Опк И Бизнесом
19 Oct, 24 -
Несколько Мыслей О Gpp И Spo В России
19 Oct, 24 -
Юи - Что За Зверь?
19 Oct, 24 -
Привет-Новости Подкаста № 11
19 Oct, 24