Domain Fronting. версия 1.3

В данной статье я хочу немного поговорить о такой старой и знакомой заядлым пентестерам теме как Domain fronting. Тем более, что после недавних нововведений от компании Cloudflare эта избитая тема заиграла новыми красками.

Краткое содержание предыдущих серий

Давным-давно, во времена, когда «зловещие русские хакеры» были ещё совсем маленькими, а такое модное сейчас слово как APT (advanced persistent threat) не звучало из каждого утюга пентестерского телеграм-канала, у хакеров и сетевых профи существовала возможность прятаться от систем слежения, антивирусов и фильтрующих прокси (EDR/XDR тогда ещё и в помине не было) с помощью специальной техники прикрытия известными доменными именами, пользующимися доверием у пользователей и средств защиты. Если кратко, то можно представить эту схему так: мы коннектимся к серверам, например, яндекса по проколу https, а уже внутри http-протокола пишем Host: xaker.com. Фронт яндекса видит этот заголовок и переадресует наше подключение уже на сервер xaker.com

Обычный Domain FrontingОбычный Domain Fronting

Таким образом, для антивируса или прокси-сервера, который запрещает нам напрямую подключаться к xaker.сom, это подключение будет вполне себе легитимным. В качестве фронт-доменов выбирались CDN-сервисы крупных провайдеров: Майкрософта, Амазона, Яндекса, Cloudlare.
Когда данная фишка сокрытия от зорких глаз товарища майора пошла в народ, и её подхватили различные малварщики, CDN и облачные провайдеры поняли, что с этим надо как-то бороться. Они стали выключать данную возможность в своих облаках для всех, кроме аккаунтов и доменов с корпоративной подпиской, то есть лиц, пользующихся определенным доверием.

В 2018-м году компания Cloudflare придумала и первой внедрила новую фичу в протокол TLS1.3 ESNI (encrypted server name identifier). Это когда поле SNI (server name identifier) в составе пакета client-hello отсылалось на сервер в зашифрованном виде, а шифровалось оно с помощью публичного ключа, который надо было предварительно запросить у сервера через DNS TXT запись домена. Т.е опять же, если кратко: сначала мы запрашиваем TXT запись _esni.xaker.com, получаем публичный ключ, потом этим ключом шифруем поле SNI в ssl пакете client-hello, и отправляем его на сервер. Как мы всею, надеюсь, знаем из школьных уроков криптографии, при таком подходе никто, кроме владельца закрытого ключа, т.е. серверов Cloudflare не мог расшифровать это поле и понять, кому же предназначены данные соединения.

Domain Fronting через Cloudflare с применением ESNIDomain Fronting через Cloudflare с применением ESNI

Дополнительной фишкой данного соединения являлось то, что помимо обязательной зашифрованной части ESNI пакета client-hello мы могли снабдить его и стандартной незашифрованной частью SNI. И вот в нём уже указать абсолютно любой домен, которым мы хотим прикрыться. Мы подробно описывали эту технологию в 2019-м году в статьях на Хабре, показывая на примерах как подключиться к рутрекеру под прикрытием домена kremlin.ru. Прочтите — вполне доходчиво и интересно.

https://habr.com/ru/post/475372/ — первая часть

https://habr.com/ru/post/477696/ — вторая часть

Когда эта TLS фишка опять же пошла в народ была взята на вооружение малварщиками, инженеры из Cloudflare сказали «Ой» спешно её закрыли (правда, как и в прошлый раз, оставив её работать для своих любимых клиентов с корпоративной подпиской). С 2020-го года прикрыться чужим доменом уже не представлялось возможным, но механизм с ESNI по-прежнему работал. То есть в составе SSL client-hello пакета уже не было открытой части server name, а была лишь только закрытая.

И вот, то ли весной, то ли летом 2022 года Cloudflare полностью убрала поддержку ESNI со своих серверов. Сделали они это не потому, что этим механизмом начали пользоваться все малварщики мира, а потому что на смену ESNI пришёл ECH (Encrypted Client Hello). О принципах работы данного механизма инженеры Cloudflare написали несколько статей. Там много нововведений, но если вкратце, то суть примерно такая же — сначала мы запрашиваем публичный ключ для домена, а затем шифруем этим ключом теперь уже весь client-hello пакет, включая и server name поле.

Подробнее про переход с ESNI на ECH на русском — https://tcinet.ru/press-centre/articles/7563/

Переходим к водным процедурам (практическим занятиям)

Прежде всего, давайте проверим в браузере как работает ECH. Берём последний Google Chrome (на момент написания статьи — 108й) и проделываем с ним пару нехитрых конфигураций:

Включаем SecureDNS и выбираем провайдера

53c48e74078ff02d08a4fe44407265ef.png

Через настройки включаем сам ECH

acea1b9abf29883c1dbf12bbe722f995.png

На этом конфигурирование Хрома завершено. Для проверки можно открыть специальную страницу проекта defo.io (комьюнити, работающее над внедрением ECH) — https://defo.ie/ech-check.php

b0323b396365d95df4486a4c27f8ce05.png

Заветная зелёная галочка сообщает нам, что соединение с сервером произошло с использованием ECH. Мы обращались к домену defo.ie, именно это значение было в зашифрованной части ClientHello, а в качестве «прикрытия» использовали домен cover.defo.ie, что можно видеть, если посмотреть трафик в wireshark«е.

f7b640a1f9e1f6a2690a1620da59882f.png

Так же проверить работу можно и на сайте Клаудфлары.

42994632825751eda20c0ea72381c115.png

В качестве другого практического занятия попробуем собрать openssl и curl с поддержкой ECH. Благо, ребята из проекта DEFO уже позаботились о нас и подробно описали как это сделать:

https://github.com/sftcd/openssl/blob/ECH-draft-13a/esnistuff/building-curl-openssl-with-ech.md

Просто следуем инструкциям, качаем с гитхаба соответсвующие сорцы и собираем у себя.
Новый curl имеет соответствующий параметр --echpublic, который как раз и инструктирует нашу сборку, относительно домена прикрытия. Наш пример с курлом из прошлых статей будет выглядеть так:

5382373cc7f5a829f9aafddf34c54869.png

В данном случае сначала необходимо получить от DNS-сервера ECH-ключи (так называемый ECHConfig). Это можно сделать запросив соответствующую запись с DNS-сервера, обслуживающего интересующий нас домен. Необходимые нам ключи содержатся в ресурсной DNS-записи HTTPS (или TYPE65) и получить их можно, например, с помощью утилиты dig:

595bdc41c69fa2a46b95329f2aa1712c.png

Получить ECHConfig необходимо непосредственно перед выполнением запроса, потому что, как и в случае с ESNI, ключи меняются примерно раз в полчаса.
Тут так же следует отметить, что для своих клиентов (доменов) сервера клаудфлары не генерируют отдельные ECHConfigs и не отдают их при соответствующих запросах, но ECHConfig для домена crypto.cloudlare.com будет работать для любых доменов, заведенных за Cloudflare. Видимо, даже в самой клаудфларе процесс внедрения ECH до сих пор не «устаканен» до конца.

Поближе к хакерству

Итак, мы посмотрели вблизи на новый механизм ECH и как его можно применить на практике. Теперь давайте адаптируем какой-нибудь хакерский инструмент (curl не в счет) под эту новую технологию. В пошлый раз мы это делали с Rsocks-туннелером. Теперь же проведём данную операцию с другим похожим инструментом (также реализованным на GO), а именно — туннелером Ligolo-ng от французского чатланина разработчика (https://github.com/nicocha30/ligolo-ng).

Изначально ligolo-ng не умеет работать с websocket. А поддержка данного протокола необходима для работы через Cloudflare. Добавление websocket в ligolo-ng выходит за рамки данной статьи. Скажу лишь, что ничего особо нового тут не требуется, и для websocket я использовал те же стандартные подходы/библиотеки, что и для Rsocks.
Для того чтобы научить ligolo-ng работать с ECH необходимо использовать соответствующий форк GO от Cloudflare, обеспечивающий поддержку ECH (на момент написания статьи это GO 1.18.3) — https://github.com/cloudflare/go. Качаем его с гитхаба, собираем у себя и используем в качестве GOROOT при компиляции ligolo-ng.
После компиляции у нас получаются два исполняемых модуля: proxy — серверная часть и agent — клиентская часть туннелера.

Сервер запускаем как обычно, с указанием нужного порта и префикса https, который сообщит туннелеру, что работать нужно по протоколу websocket:

87fddf79edffa4ef4aabb52389e2bd81.png

Агента также запускаем с указанием флага -ech, префикса https, и нашего домена, заведенного за клаудфлару:

0d7cdb7ff7de52071ba36185ea59d900.png

Видим, что агент удачно зачекинился на сервере. Дальше всё стандартно. Стартуем туннелинг и работаем как через VPN с пингами и UDP-протоколом. Чудеса, да и только!

Что же при этом видит Роскомнадзор wireshark:

d5464cd81e735d28e6da37648af90bb5.png

Он видит, что сначала был отправлен запрос на dns.google.com (именно через DoH агент запрашивает ключи ECHConfigs), а затем пошёл запрос на IP-адрес серверов клаудфлары, но никаких имен серверов в этих пакетах нет.
В принципе, уже неплохо. Промежуточный узел видит только IP-адреса, но не домены назначения. Однако, нам этого мало. Необходимо провести операцию прикрытия, чтобы ни у кого не возникало даже мысли о блокировке нашего соединения. Поэтому мы делаем «ход конем». Открываем файл src/crypto/tls/ech.go из GO-форка, находим строчку

hello.serverName = hostnameInSNI(string(echConfig.rawPublicName))

и меняем её на что-то более благозвучное настоящему комсомольцу ИБ-ресёрчеру:

8ad1d7f47b09477e87b3e4c2d881dd65.png

Пересобираем агента, запускаем и смотрим, что на сей раз видят ребята из Роскомнадзора.

6dbe717168f312d00269fb15021bd05f.png

А они видят, что теперь самый главный домен страны надежно защищен лучшим в мире CDN, немного удивляются конечно, но одновременно успокаивают себя тем фактом, что теперь «хоть что-то у нас в безопасности».

Заключение

В данной статье мы познакомились с новой технологией шифрованния ClientHello-пакетов протокола TLS1.3 и увидели, что данную технологию можно применять как с точки зрения defensive (защита от слежки со стороны РКН и ему подобных), так и с точки зрения offensive (DomainFronting). Пока что форк Ligolo-NG с возможностями websocket и ECH размещен на гитхабе автора статьи в отельной ветке (https://github.com/virusvfv/ligolo-ng/tree/websocket), но сделан соответсвующий pull-request и вполне возможно, что он войдет в основную ветку проекта.

Вангую, что довольно-таки скоро, когда уже и эта фишка с ECH DomainFronting «уйдет в народ», ребята из клаудфлары опять скажут «Ой», и отключат возможность прикрытия произвольным сleartext SNI (outer server name), как это уже было с ESNI. Но всё-таки технология ECH не стоит на месте, стандарт TLS 1.3, как я полагаю, довольно скоро станет уже настоящим стандартом, его начнут поддерживать остальные CDN (Google, Amazon, Microsoft), а браузеры начнут работать по ECH уже из коробки. Что тогда будет делать РКН — увидим.

Но это будет потом. Впереди зима, надеюсь не ядерная…

© Habrahabr.ru