Введение



Современные корпоративные системы фильтрации контента, от таких именитых производителей как Cisco, BlueCoat, FireEye имеют довольно много общего с более мощными их собратьями — DPI системами, которые усиленно внедряются на национальном уровне. Суть работы и тех и других в том, чтобы производить досмотр входящего и исходящего интернет трафика и, на основании черных/белых списков, принимать решение о запрете интернет-соединения. А так как и те, и другие в основах своей работы полагаются на схожие принципы, то и способы их обхода также будут иметь много общего.

Одной из технологий, позволяющей достаточно эффективно обходить как DPI, так и корпоративные системы, является технология домен-фронтинга. Ее суть состоит в том, что мы идем на заблокированный ресурс, прикрываясь другим, публичным доменом, с хорошей репутацией, который заведомо не будет блокирован ни одной системой, например google.com.

О данной технологии было написано уже достаточно много статей и приведено много примеров. Однако популярные и обсуждаемые в последнее время технологии DNS-over-HTTPS и encrypted-SNI, а также новая версия протокола TLS 1.3 дают возможность рассмотреть еще один вариант домен-фронтинга.

Разбираемся с технологией


Сначала немного определимся с основными понятиями, чтобы у всех было понимание who is who и зачем все это нужно. Мы упомянули механизм eSNI, работа которого будет рассмотрена дальше. Механизм eSNI (encrypted Server Name Indication) – защищенный вариант SNI, доступный только для протокола TLS 1.3. Основная суть – шифровать в том числе информацию о том, к какому домену отправляется запрос.

А теперь давайте рассмотрим работу механизма eSNI на практике.

Допустим, у нас есть интернет-ресурс, который блокируется современным DPI решением (возьмем к примеру, знаменитый торрент-трекер — rutracker.nl). При попытке захода на сайт торрент-трекера — мы видим стандартную заглушку провайдера о том, что ресурс блокируется:



На сайте РКН это домен действительно числится в стоп-листах:



При запросе whois — видно, что сам домен «спрятан» за облачным провайдером Cloudflare.



Но в отличие от «специалистов» из РКН, более технически подкованные сотрудники из билайна (или наученные горьким опытом нашего знаменитого регулятора) не стали тупо банить сайт по IP -адресу, а внесли в стоп-лист именно доменное имя. В этом легко убедиться, если посмотреть, какие еще домены прячутся за этим же IP-адресом, посетить один из них и увидеть, что доступ не заблокирован:



А как же так получается? Каким образом провайдерский DPI узнает, на какой из доменов идет мой браузер, ведь все коммуникации происходят по протоколу https, а подмены сертификатов https от билайна мы вроде пока не замечали? Уж не ясновидящий ли он или за мной идет слежка?

Попробуем ответить на это вопрос, взглянув на трафик через wireshark



На скриншоте видно, что сперва браузер получает IP-адрес сервера через DNS, потом происходит стандартное TCP-рукопожатие с сервером назначения, а затем браузер пытается установить ssl-соединение с сервером. Для этого он передает пакет SSL Client Hello, в котором присутствует имя исходного домена в открытом виде. Это поле необходимо фронтенд-серверу cloudflare для того, чтобы правильно маршрутизировать соединение. Вот тут-то нас и ловит провайдерский DPI, разрывая наше соединение. При этом мы не получаем никакой заглушки от провайдера, и видим стандартную ошибку браузера как будто сайт отключен или просто не работает:



Теперь давайте включим механизм eSNI в браузере, как это написано в инструкции для Firefox :
Для этого мы открываем страницу конфигурации Firefox about:config и активируем следующие настройки:

network.trr.mode = 2;
network.trr.uri = https://mozilla.cloudflare-dns.com/dns-query
network.security.esni.enabled = true

После этого мы проверим корректность работы настроек на сайте cloudflare по ссылке и попробуем фокус с нашим торрент-трекером еще раз.



Вуаля. Наш любимый трекер открылся, без каких-либо VPN и прокси-серверов. Давайте теперь посмотрим на дамп трафика в wireshark, что же произошло.



На сей раз пакет ssl client hello не содержит в явном виде домен назначения, а вместо этого в составе пакета появилось новое поле — encrypted_server_name — именно там и содержится значение rutracker.nl, и расшифровать это поле может только фронтенд сервер cloudflare. А раз так, то провайдерскому DPI не остается ничего кроме как умыть руки и разрешить такой трафик. А других вариантов с шифрованием и нет.

Итак, как работает технология в браузере — мы посмотрели. Теперь давайте попробуем применить ее для более специфичных и интересных вещей. И для начала мы научим тот же curl использовать eSNI для работы с TLS 1.3, а заодно посмотрим, как работает сам домен-фронтинг на основе eSNI.

Домен-фронтинг с eSNI


Ввиду того, что curl для подключения по протоколу https использует стандартную библиотеку openssl, прежде всего нам необходимо обеспечить поддержку eSNI именно там. В master-ветках openssl поддержки eSNI пока что нет, поэтому нам необходимо скачать специальную ветку openssl, скомпилировать и установить ее.

Клонируем репозиторий с гитхаба и компилируем как обычно:

$ git clone https://github.com/sftcd/openssl
$ cd openssl
$ ./config

$ make
$ cd esnistuff
$ make

Далее — клонируем репозиторий с curl и конфигурируем его компиляцию с использованием нашей собранной openssl библиотеки:

$ cd $HOME/code
$ git clone https://github.com/niallor/curl.git curl-esni
$ cd curl-esni

$ export LD_LIBRARY_PATH=/opt/openssl
$ ./buildconf
$ LDFLAGS="-L/opt/openssl" ./configure --with-ssl=/opt/openssl --enable-esni --enable-debug

Здесь важно правильно указать все каталоги, где находится openssl (в нашем случае — это /opt/openssl/) и проследить, чтобы процесс конфигурации прошел без ошибок.

В случае успешной конфигурации — мы увидим строку:

WARNING: esni ESNI enabled but marked EXPERIMENTAL. Use with caution!

$ make

После успешной сборки пакета мы воспользуемся специальным bash-файлом из состава openssl для настройки и запуска curl. Скопируем его в каталог с curl для удобства:

cp /opt/openssl/esnistuff/curl-esni 

и выполним тестовый https-запрос на сервер cloudflare, одновременно записав DNS и TLS пакеты в Wireshark.

$ ESNI_COVER="www.hello-rkn.ru" ./curl-esni https://cloudflare.com/

В ответе сервера помимо множества отладочной-информации от openssl и curl мы получим HTTP-ответ с кодом 301 от cloudflare.

HTTP/1.1 301 Moved Permanently
< Date: Sun, 03 Nov 2019 13:12:55 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< Cache-Control: max-age=3600
< Expires: Sun, 03 Nov 2019 14:12:55 GMT
< Location: https://www.cloudflare.com/

что свидетельствует о том, что наш запрос был успешно доставлен до сервера назначения, услышан и обработан.

Теперь давайте посмотрим на дамп трафика в wireshark, т.е. что увидел в данном случае провайдерский DPI.



Видно, что сначала curl обратился к DNS серверу за публичными eSNI ключом для сервера cloudflare — TXT DNS запрос на _esni.cloudflare.com (пакет №13). Затем, используя openssl-библиотеку, curl отправил TLS 1.3 запрос на сервер cloudflare в котором поле SNI было зашифровано публичным ключом, полученным на предыдущем этапе (пакет №22). Но, помимо поля eSNI, в составе SSL-hello пакета было вставлено еще и поле с обычным — открытым SNI, которое мы можем указать в произвольном порядке (в данном случае — www.hello-rkn.ru).

Данное поле открытого SNI никак не учитывалось при обработке серверами cloudflare и лишь являлось маскировочным для провайдерского DPI. Сервер cloudflare принял наш ssl-hello пакет, расшифровал eSNI, извлек оттуда оригинальный SNI и обработал его как ни в чем не бывало (сделал все именно так, как планировалось при разработке eSNI).

Единственное, за что в данном случае можно зацепиться с точки зрения DPI — первичный DNS запрос на _esni.cloudflare.com. Но мы сделали DNS запрос открытым лишь для того, чтобы показать, как данный механизм работает изнутри.

Чтобы окончательно выбить почву из-под ног DPI мы используем уже упомянутый механизм DNS-over-HTTPS. Небольшое пояснение – DOH – протокол, который позволяет защититься от атаки «человек посередине» за счет отправки DNS-запроса по протоколу HTTPS.

Выполним запрос повторно, но в это раз публичные eSNI ключи мы получим по протоколу https, а не DNS:

ESNI_COVER="www.hello-rkn.ru" DOH_URL=https://mozilla.cloudflare-dns.com/dns-query ./curl-esni https://cloudflare.com/

Дамп трафика запроса представлен на скриншоте ниже:



Видно, что сперва curl обращается к серверу mozilla.cloudflare-dns.com по протоколу DoH (https соединение на сервер 104.16.249.249), чтобы от них получить значения public ключей для шифрования SNI, а затем уже к серверу назначения, прикрываясь при этом доменом www.hello-rkn.ru.

Помимо указанного выше DoH резолвера mozilla.cloudflare-dns.com мы можем использовать и другие популярные сервисы DoH, например, от знаменитой корпорации зла.
Выполним такой запрос:

ESNI_COVER="www.kremlin.ru" DOH_URL=https://dns.google/dns-query ./curl-esni https://rutracker.nl/

И получим ответ:

< HTTP/1.1 301 Moved Permanently
< Date: Sun, 03 Nov 2019 14:10:22 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Set-Cookie: __cfduid=da0144d982437e77b0b37af7d00438b1a1572790222; expires=Mon, 02-Nov-20 14:10:22 GMT; path=/; domain=.rutracker.nl; HttpOnly; Secure
< Location: https://rutracker.nl/forum/index.php
< CF-Cache-Status: DYNAMIC
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
< CF-RAY: 52feee696f42d891-CPH



В данном случае мы обратились на заблокированный сервер rutracker.nl, использовав при этом DoH-резолвер dns.google (тут нет опечатки, теперь у знаменитой корпорации появился свой домен первого уровня) и прикрылись уже другим доменом, блокировать который строго-настрого запрещено всем DPI под страхом смертной казни. По полученному ответу можно понять, что наш запрос был удачно обработан.

В качестве дополнительной проверки того, что провайдерский DPI реагирует на открытый SNI, который мы передаем в качестве прикрытия — мы можем выполнить запрос к rutracker.nl прикрывшись каким-нибудь другим запрещенным ресурсом, например другим «хорошим» торрент-трекером:

$ ESNI_COVER="rutor.info" DOH_URL=https://dns.google/dns-query ./curl-esni https://rutracker.nl/

Ответа от сервера мы не получим, т.к. наш запрос будет заблокирован DPI- системой.

Небольшое заключение к первой части


Итак, нам удалось показать работоспособность eSNI с помощью openssl и curl и проверить работу домен-фронтинга, основанного на eSNI. Таким же образом мы можем адаптировать наши любимые инструменты, использующие библиотеку openssl, для работы «под прикрытием» других доменов. Подробнее об этом — в наших следующих статьях.

Комментарии (34)


  1. Victor_koly
    12.11.2019 12:22

    dns.goolge (тут нет опечатки, теперь у знаменитой корпорации появился свой домен первого уровня)


    Тут все же опечатка, они конечно dns.google (8.8.4.4).


    1. VirusVFV
      12.11.2019 13:32
      +1

      Да, бывает...


  1. hollow1
    12.11.2019 12:23

    Спасибо за статью, доступно изложены новые фичи протоколов. Можно брать на вооружение!


  1. ReklatsMasters
    12.11.2019 17:56

    Единственная проблема во всей этой системе, что и Гугл, и cloudflare собирают все данные, до которых только могут дотянуться. Также получается, что с внедрением doh в браузеры весь dns трафик замкнётся на 2х компаниях. Это не тот интернет, который нам нужен.


    1. Revertis
      12.11.2019 21:14
      -1

      Ставьте на своей виртуалке AdGuard Home, и пользуйтесь DoH на своём сервере. Заодно и рекламу поблочите.


    1. 0ther
      12.11.2019 22:21

      Эта проблема давно стоит, вспомнить хоть ту статью про журналистку, которая пыталась неделю не пользоваться продуктами Google.
      eSNI — лишь одна из проблем.


    1. shifttstas
      13.11.2019 09:39

      Это все ещё лучше, чем нешифрованный DNS, а если хочется приватности — используем свой личный DNS.


  1. Meklon
    12.11.2019 21:13
    +1

    Я вдруг понял, что мне это сломает фильтрацию дома на pi-hole.


  1. Revertis
    12.11.2019 21:21

    Интересно, а шифрование домена для eSNI с одним и тем же ключом даёт один и тот же результат? Или там какие-то IV есть?


    1. 0ther
      12.11.2019 21:57

      Стандарт, 7.5.5:

      This design is not forward secret because the server's ESNI key is
      static. However, the window of exposure is bound by the key
      lifetime. It is RECOMMENDED that servers rotate keys frequently.

      Может клиенты будут добавлять seed/nonce в SNI, может это даже в стандарт внесут (или уже, но я ленивая задница и не прочёл).


      1. Revertis
        12.11.2019 22:11

        Просто я подозреваю, что сам сервер DPI может делать такие же запросы, получать ключи, шифровать, добавлять в табличку, а потом просто сравнивать с такой таблицей пакеты юзеров :(


        1. 0ther
          12.11.2019 22:19

          Вы не поняли: клиент добавляет шум.
          Вместо:
          Алиса и Мэллори получает один и тот же публичный ключ и шифруют с его помощью SNI, получают один шифротекст и можно использовать его вместо SNI для бана.
          Получаем это:
          Алиса и Мэллори получает один и тот же публичный ключ и шифруют с его помощью SNI, но Алиса добавляет шум: «telegram.org:::%random_uint%», получая постоянно уникальный шифротекст, который запомнить нельзя, но получатель может расшифровать и убрать шум.
          Ну, я так надеюсь, выглядит логично, иначе толку от eSNI и правда мало.


          1. Revertis
            12.11.2019 22:22

            Ну вот я это и имел ввиду под IV. Можно в данном случае это назвать nonce. Если это используется, то хорошо.


            1. 0ther
              12.11.2019 22:29

              Хм, что это за «IV»?


              1. Revertis
                12.11.2019 22:38

                Вектор инициализации, используется в AES.


    1. VirusVFV
      12.11.2019 22:13

      У той же клаудфлары ключи меняются раз в 20-30 мин...


      1. 0ther
        12.11.2019 22:20

        А DPI делает запрос сразу же с пользовательским, получая актуальный ключ.


  1. Groosha
    12.11.2019 21:38

    Очень хочется увидеть инструкцию по поднятию собственного DNS-сервера с поддержкой eSNI, дабы "отвязаться" от Google с Cloudflare.


    1. VirusVFV
      12.11.2019 22:11

      Ну, вроде бы там ничего особо мудреного нет. Генерируем пару ключей, открытый ключ бейсим и отдаем в виде txt-записи на запросы _esni.mydomain.com
      Закрытый ключ скармливаем nginx'у чтобы он мог распарить esni.


  1. Meklon
    12.11.2019 21:45
    +2

    Это ж блин, теперь любая малварь и реклама будет резолвиться безнаказанно :-/


    1. 0ther
      12.11.2019 21:58

      Эм, клиент-то знает что он шифрует, до шифрования и блочить.


      1. Meklon
        12.11.2019 22:16
        +1

        Угу. Как мне это поможет, если приложение на телефоне начнет телеметрию слать и резолвиться мимо локального DNS?


        1. 0ther
          12.11.2019 22:24

          Настройка, что тут делать-то?
          Новые технологии требуют нового подхода, ничего необычного.
          Настроить обращение к общему для сети DNS (роутер?), например.


          1. Meklon
            13.11.2019 06:52

            У меня и так настроено. Но теперь все приложения смогут это игнорировать


    1. Revertis
      12.11.2019 22:15

      Большую часть рекламы можно зарезать на уровне ДНС. Ставите тот же dnsmasq, с файлом хостов, остальное он запрашивает у клиента, запрашивающего по протоколу DoH/DoT с вашей виртуалки в ЕС. Это если наколеночно делать, а можно взять AdGuard Home, который я выше упомянул, он всё это умеет. И код открыт.
      А то, что не зарезалось на ДНС, резать расширениями всякими. Например, если баннеры на том же домене, что и контент, только урл другой.


    1. VirusVFV
      12.11.2019 22:16

      В следующей части как раз пример будет;)


    1. shifttstas
      13.11.2019 09:41

      Многие приложения уже давно используют DOH, например — Telegram.


  1. shifttstas
    13.11.2019 09:42

    Теперь самый интересный вопрос: когда же google в chrome включит eSNI? Сейчас они только DOH включают и то, очень аккуратно...


  1. in_heb
    13.11.2019 11:41

    На sumtel не работает такой способ, там блочится TLS с неизвестным ssl hello на IP-адреса, внесённые в список РКН. По мере распространения eSNI (сейчас он доступен только ИТ-специалистам) (проще говоря когда будет в хроме по умолчанию), РКН, очевидно даст рекомендацию блочить такой трафик.

    Т.е. eSNI это лишь временная «победа» над РКН как когда-то был IPv6 («специалисты» РКН похоже долго не знали что оно существует)


    1. VirusVFV
      13.11.2019 13:23

      Так можешь блочится по причине того, что ИП внесён в список? Вся прелесть dpi как раз в том и заключается, что ркн хочет уйти от блокировки по ипам...


      1. in_heb
        13.11.2019 13:27

        нет, я проверял, если подключаться без esni на тот же ip, то не блочится (прописал ip в hosts и попробовал)


  1. 2PAE
    13.11.2019 12:39

    Возможно глупый вопрос, но кто мешает вырезать из запроса клиента поле encrypted_server_name? А то, что у него что-то там не работает, проблема клиента.
    «Отчего вы не пишите письма на открытках? Вы что-то плохое прячете.»

    Поломать возможность клиенту использовать eSNI и жить счастливо. Как вариант?


    1. VirusVFV
      13.11.2019 13:20

      Если вырезать что то из пакета, то придется пересчитывать все чек суммы, перестраивать пакеты и т.д. а это при большом трафике довольно таки накладно.
      Проще уж тогда совсем дропать такие пакеты, но тогда опять мы вернёмся к самой первой картинке в статье.


    1. alsii
      13.11.2019 13:27

      Зачем так мелочиться? Просто отправляем весь TLS и SSL в /dev/null. "Честному человеку скрывать нечего!" /sarcasm


      p.s. Говорят в телеграфных правилах СССР была такая фраза: "не подлежат передаче телеграммы, смысл которых не понятен телеграфисту".