Въведение
В първата част дадохме кратко описание на механизма encrypted SNI (eSNI). Показахме как на базата на него можем да се измъкнем от откритията на съвременните DPI системи (на примера на DPI на Beeline и забранения RTN рутрекер), както и проучихме нов вариант на домейн-фронтиране на базата на този механизъм.
Във втората част на статията ще се насочим към по-практически аспекти, които ще бъдат полезни на RedTeam специалистите в тяхната трудна работа. В края на краищата, нашата цел не е да получим достъп до блокирани ресурси (за такива банални неща разполагаме със стария добър VPN). Щастливи сме, че има много VPN доставчици, както се казва, за всеки вкус, цвят и бюджет.
Ще се опитаме да приложим механизма на домейн-фронтиране за съвременни инструменти на RedTeam, като Cobalt Strike, Empire и т.н., и да им предоставим допълнителни възможности за мимикриране и избягване на съвременните системи за филтриране на съдържание.
В миналия път внедрихме механизма eSNI в библиотеката OpenSSL и успешно го използвахме в добре познатия инструмент curl. Но с единствено curl, както се казва, не можеш да се нахраниш. Разбира се, искаме да реализираме нещо подобно в езици с високо ниво. Но, за съжаление, бързото търсене в мрежата ни разочарова, защото поддръжката на механизма eSNI е напълно реализирана само в GOLANG. Така че, изборът ни не е особено голям: или пишем на чист C или C++ с използване на пропатчена библиотека OpenSSL, или използваме отделен форк на GOLANG от CloudFlare и се опитваме да портим нашите инструменти там. В принципе, има още един вариант, по-класически, но и трудоемък – да реализираме поддръжка на eSNI за Python. В крайна сметка Python също така използва OpenSSL за работа с https. Но този вариант го оставяме на разработка на някой друг, а сами ще се задоволим с реализацията на голанга, особено след като нашият любим Cobalt Strike прекрасно работи с канала на комуникация, построен със странични средства (External C2 channel) – за това ще разкажем в края на статията.
Try Harder…
Един от реализираните на Go инструменти – нашата разработка за пивотиране в мрежата – тунел , което, между другото, в момента се открива от компании като Microsoft и Symantec, като много злонамерен софтуер, насочен към нарушаване на световната стабилност…

Би било страхотно да използваме предходната разработка и в този случай. Но тук възниква малък проблем. Факт е, че първоначално rsockstun предвижда използването на синхронен SSL канал за комуникация със сървъра. Това означава, че връзката се установява веднъж и съществува през целия период на работа на тунела. А както разбирате, протоколът https не е точно създаден за такъв режим на работа – той работи в режим на запитване-отговор, където всяко ново http-запитване съществува в рамките на ново tcp-соединение.
Основният недостатък на тази схема е, че сървърът не може да предаде данни на клиента, докато клиентът не изпрати ново http-запитване. Но, за щастие, има много варианти за решаване на този проблем – потокова предаване на данни чрез http протокол (в крайна сметка успяваме да гледаме любимите си сериали и да слушаме музика от сайтове, работещи на https, а предаването на видео и аудио не е нищо повече от потокова предаване на данни). Една от технологиите за емулиране на полноценно tcp-соединение върху http протокол е технологията на уеб-сокетите (WebSockets), основната същност на която е в организирането на полноценно мрежово съединение между клиента и уеб-сървъра.
На наше щастие (ура-ура!!!), тази технология е включена по подразбиране във всички тарифни планове на CloudFlare и прекрасно работи в комбинация с eSNI. Точно нея ще използваме, за да научим нашия тунел да използва домейн-фронтиране и да се скрива от съвременните DPI.
Няколко думи за WebSockets
Преди всичко, накратко и с прости думи ще обясним какво представляват уеб-сокетите, за да всеки да има представа за какво ще работим.
Технологията на уеб-сокетите позволява временно да се премине от http-съединение към стандартно потоково предаване на данни чрез мрежови сокет, без да се прекъсва установеното tcp-съединение. Когато клиентът иска да премине към уеб-сокет, той в своето http-запитване излага няколко http-заглавия. Два задължителни заглавия – Connection: Upgrade и Upgrade: websocket. Също така може да посочи задължително версията на протокола на уеб-сокетите (Sec-Websockset-Version: 13) и нещо като base64 идентификатор на уебсокета (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). Серверът отговаря с http-код 101 Switching Protocols и също така установява заглавките Connection, Upgrade и Sec-WebSocket-Accept. Процесът на превключване е илюстриран на екрана по-долу:

След това установяването на WebSocket връзката може да се счита за завършено. Всички данни както от клиента, така и от сървъра сега ще бъдат снабдени не с http, а с WebSocket заглавки (те започват с байт 0x82). Сега сървърът не трябва да чака заявка от клиента, за да предаде данни, тъй като tcp връзката не се разкъсва.
В голанга има няколко библиотеки за работа с уебсокети. Най-популярните от тях са и стандартната . Ще използваме последната, тъй като е по-проста, по-малка и работи, както казват, малко по-бързо.
В кода на клиента rsockstun е необходимо да заменим извикванията net.dial или tls.dial с подходящите извиквания на WebSocket:


Искаме клиентската част на нашия туннел да бъде универсална и способна да работи както през пряка ssl връзка, така и чрез протокола WebSocket. Затова ще създадем отделна функция func connectForWsSocks(address string, proxy string) error {…} по аналогия с 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 трябва да вземем специален клон на голанга от компанията . Нуждаем се от клон с поддръжка на eSNI (pwu/esni).
Клонираме го локално или изтегляме и разархивираме съответния zip:
git clone -b pwu/esni https://github.com/cloudflare/tls-tris.gitСлед това е необходимо да копираме директорията GOROOT, да заменим съответните файлове от клонираната клонка и да я настроим като основна. За да облекчат главоболията на разработчика, хората от CloudFlare са подготвили специален скрипт – _dev/go.sh. Просто го стартирайте. Скриптът заедно с makefile ще свършат всичко сами. За интересно — можете да надникнете в makefile за подробности.
След изпълнението на скрипта, при компилация на проекта, ще трябва да укажем локалната директория, подготвена от скрипта, като 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 не е предназначен за използване на домен-фронтиране. Следователно, не е обърнато внимание на фалшивото име на сървъра (в състава на пакета client-hello се предава празно поле serverName). За да поправим това, ще трябва да добавим в структурата TlsConfig съответното поле FakeServerName. Стандартното поле 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 (в това, между другото, е още едно предимство на голанга – можете да компилирате проекта си при себе си и да го стартирате на всяка версия на Линукс, спазвайки само разрядността на системата) и стартираме сървърната част:

А след това клиентската част:

Както виждаме, клиентът успешно се свърза със сървъра през фронтенд сървъра на CloudFlare, използвайки уеб сокета. За да проверим, че тунелът работи точно като тунел, можем да направим curl заявка през локален socks5, отворен на сървъра:

Сега да видим какво вижда DPI в комуникационния канал:

Първо, тунелизаторът, използвайки механизма DoH, се обръща към DNS сървъра на Cloudflare за eSNI ключовете за дестинационния домейн (пакети №1-19), а след това се свързва с фронтенд сървъра и установява TLS връзка, прикривайки се при това с домейна (това е стойността по подразбиране, когато при стартирането на клиента не е зададен фейков домейн). За да зададете своя фейков домейн, е необходимо да използвате параметъра -fronfDomain:
![]()

Сега още един момент. По подразбиране в настройките на профила в CloudFalre е зададен режим на работа Flexible SSL. Това означава, че https заявките към фронтенд сървърите на Cloudflare от клиентите ще бъдат пренасочвани в нешифрован (http) вид към нашия сървър. Именно затова стартирахме сървърната част на тунелизатора в non-ssl режим (-listen ws:0.0.0.0), а не (-listen wss:0.0.0.0).

За да прехвърлите в режим на пълно шифроване, е необходимо да изберете Full, или Full (strict) в случай на наличие настоящия сертификат на сървера. След превключване на режима ще можем да приемаме връзки от CloudFlare по https протокол. Не забравяйте да генерирате self-signed сертификат за сървърната част на тунелера.

Задълбоченият читател ще попита: "А как е с клиента под 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 през , реализиран на golang, или на стандартен C++ с използването на патчната версия на OpenSSL, за която разказахме в предишната част на статията. Общо взето, фантазията няма граници.
Примерът с тунелера и CloudFlare е представен като концепция и в момента е трудно да се каже за далечните перспективи на подобен вид домейн-фронтинг. В момента поддръжката на eSNI е реализирана само от CloudFlare и, по идеята, нищо не пречи на тях да деактивират подобен фронтинг и, например, да разкъсват tls-съединения при несъответствие на SNI и eSNI. Общо взето, бъдещето ще покаже. Но за сега перспективата за работа под "прикритие kremlin.ru" изглежда доста примамлива. Нали така?
Актуализираният код на тунелера, както и компилираните изпълними exe файлове, са разположени в отделен клон на проекта на . За всички възможни проблеми с тунелера е най-добре да пишете issue на страницата на проекта в GitHub.
Източник: habr.com
