Не такой уж и зашифрованный этот ваш SSH

  • #SSH

О чем материал

Разбираемся, так ли безопасен SSH-протокол и что происходит внутри зашифрованной сессии

Протокол SSH задумывался как защищенная удаленная оболочка — безопасная замена протоколу Telnet, который передавал пароли и команды в открытом виде. В свою очередь, SSH предлагает альтернативу — защищенный канал и аутентификацию по открытому ключу. Но за годы своего существования он превратился из простого шелла в универсальный инструмент удаленного доступа и транспортировки данных. 

Сегодня SSH поддерживает несколько способов аутентификации, позволяет передавать файлы и строить сетевые туннели. При этом абсолютное большинство пользователей используют лишь малую часть его возможностей. Все параметры запуска протокола знают лишь немногие, но мы без сомнений ему доверяем. Вера в то, что SSH безопасен по умолчанию, укрепилась настолько, что его не боятся выставлять в интернет: от попыток перебора спасет fail2ban, а серьезных уязвимостей в нем не находили давно.

Сегодня в интернете доступно более 31 млн серверов SSH. Среди наиболее распространенных версий встречаются OpenSSH (27,7 млн), Dropbear sshd (650 тыс.) и Cisco SSH (180 тыс.). Это второй по популярности сервис в интернете. Для сравнения: публично доступных RDP-серверов сейчас около 3,9 млн, Telnet — 1,5 млн, а HTTP/HTTPS-узлов — порядка 150,8 млн.

Именно из-за популярности SSH злоумышленники используют его на всех стадиях атак. С его помощью они скрывают сетевые коммуникации от средств защиты, ведь в SSH-соединении невозможно определить даже логин, который использовался для аутентификации. Ближайшим соседом этого протокола в части скрытности можно назвать TLS. Разница в том, что SSH — самостоятельный прикладной протокол с широким функционалом, а TLS чаще служит транспортом для конкретного приложения. Но есть у них и нечто общее: несмотря на то, что основная часть TLS- и SSH-соединений зашифрована, начальный этап взаимодействия остается открытым. В обоих случаях шифрование скрывает содержимое данных, что делает эти протоколы удобными для маскировки вредоносной активности. 

Существует множество технологий для расшифровки TLS-соединений, например MiTM и решения NGFW, но мы давно научились обнаруживать вредоносную активность и без этого (благодаря PT NAD, который умеет ловить даже современные VPN-протоколы, в том числе Vless — подробности в статье). Аналогичный подход можно применить и к анализу SSH, но в если случае с TLS мы идентифицируем активность приложения в целом, то SSH-соединение можем разобрать по кирпичикам.

Побочный канал зашифрованных соединений

На первый взгляд, анализ зашифрованного содержимого TLS и SSH кажется невозможным. Однако полная секретность при использовании шифрования — популярный миф. Криптография эффективно скрывает содержимое сообщений, но не поведение участников соединения. Наблюдение за этим поведением называется анализом побочного канала. В отличие от классического анализа трафика, он опирается не на проверку содержимого сетевых пакетов, а на косвенные признаки: последовательность длин зашифрованных сообщений и временные интервалы между ними. Они не всегда образуют идеально устойчивые сигнатуры, однако во многих сценариях можно выделить характерный паттерн зашифрованного трафика, позволяющий с высокой вероятностью определить, что происходит у него внутри.

Рисунок 1. Поведение инструмента Dogtunnel в зашифрованном трафике

Обычно при анализе TLS-соединений задача звучит достаточно просто: нас интересует, какое приложение работает внутри защищенного канала. С SSH ситуация принципиально другая: это прикладной протокол, а не транспорт, и внутри него могут быть реализованы совершенно разные сценарии. Поэтому задача анализа меняется на «Что именно происходит внутри SSH-сессии?». 

Забегая вперед, скажу, что при анализе зашифрованного SSH-соединения мы можем сделать выводы о:

  • способе аутентификации;
  • количестве неудачных попыток аутентификации (и была ли удачная);
  • типе сессии;
  • устанавливался ли туннель;
  • кто находится по другую сторону соединения — человек или скрипт.

Стоит отметить, что подходы к анализу зашифрованных SSH-соединений существовали и раньше. Наиболее известный — метод построения отпечатков соединений JA4SSH, основанный на анализе статистики длин сообщений со стороны клиента и сервера. Он позволяет классифицировать тип сессии: обычная интерактивная работа, reverse SSH или SFTP. Однако, как и любые fingerprint-подходы семейства JA4, такие методы имеют ограниченную точность и на практике часто выдают ложный результат.

Структура SSH-сессии

SSH-сессию проще всего представить в виде конструктора, собранного из набора логических блоков. У каждого из них есть характерные признаки в трафике (см. рис. 2).

Рисунок 2. Схема логических блоков SSH-сессии

Сначала идет незашифрованная часть соединения. Клиент и сервер обмениваются баннерами, согласовывают поддерживаемые алгоритмы и обмениваются ключами. Эта часть полностью видна наблюдателю, что позволяет определить версию протокола и выбранные алгоритмы шифрования, которые напрямую влияют на зашифрованный трафик.

Сразу после этого начинается зашифрованная часть, которая представляет для нас наибольший интерес. Сначала идет аутентификация: в процессе анализа мы можем узнать ее тип и сколько попыток входа было выполнено. 

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

  • интерактивный shell: обычная работа в терминале;
  • exec: выполнение команды и немедленное завершение сессии;
  • subsystem: например, SFTP для передачи файлов.

В одной сессии может существовать сразу несколько каналов, но чаще всего открывается только один — основной. После этого начинается передача команд, их результатов или файлов. А параллельно (либо сразу после конфигурации канала) клиент или сервер может построить сетевой туннель для проброса портов.

Конечно, в реальности все выглядит менее аккуратно: отдельные этапы могут пересекаться, каналы — открываться повторно, а некоторые блоки вообще отсутствуют. Именно поэтому анализ SSH-сессий сводится не к поиску одной сигнатуры, а к распознаванию характерных последовательностей поведения.

Каждое сообщение SSH-протокола состоит из длины пакета, длины паддинга (padding), самих полезных данных и паддинга, а также имитовставки MAC (либо AEAD-тега).

Все поддерживаемые SSH наборы шифров (криптонаборы) можно разделить на три группы: классическая схема «шифрование + MAC», режим ETM (Encrypt-then-MAC) и AEAD-шифры (AES-GCM, ChaCha20-Poly1305). В зависимости от выбранного криптонабора меняется состав защищаемых полей и объем служебных данных, что влияет на размер паддинга и итоговую длину пакета. Поэтому информация о согласованных алгоритмах, полученная на открытом этапе соединения, принципиально важна для анализа. Даже при одинаковой полезной нагрузке длины зашифрованных сообщений будут различаться. Игнорирование этих особенностей может привести к ошибкам.

Рисунок 3. Структура SSH сообщения

Анализ аутентификации

Аутентификация начинается после завершения обмена ключами и заканчивается сообщением об успешном/неуспешном входе. Границы этого этапа хорошо заметны в трафике благодаря характерным коротким сообщениям от сервера о начале и завершении аутентификации: 44 и 28 байт соответственно (примеры длин здесь и далее приведены для сервера и клиента OpenSSH с использованием алгоритма шифрования chacha20-poly1305@openssh.com и без сжатия). Эти длины стабильны и практически уникальны для начала зашифрованной сессии.

Рисунок 4. Расшифрованные сообщения аутентификации клиента и сервера OpenSSH, полученные в режиме дебага

Откуда берутся длины 44 и 28 и почему мы в них так уверены? Следите за пальцами:

  1. Оригинальное сообщение SSH2_MSG_SERVICE_ACCEPT имеет размер 16 байт.
  2. Для передачи по сети к нему добавляется 1 байт длины паддинга и 7 байт самого паддинга (минимальный паддинг — 4 байта).
  3. После шифрования полученных 24 байт в начало данных добавляются еще 4 байта длины, которые тоже могут дополнительно шифроваться.
  4. А в самом конце — еще 16 байт AEAD-тега.
  5. Таким образом, из 16 байт исходного сообщения service accept получается 44 байта зашифрованного. 

Аналогичный процесс происходит для сообщения SSH2_MSG_USERAUTH_SUCCESS размером 1 байт: 1+6 байт паддинга, 4 байта длины и 16 байт AEAD-тега. Зная алгоритм, мы можем быть уверены, что будем постоянно получать одни и те же длины зашифрованных пакетов для этого сочетания клиента, сервера и криптонабора.

Анализ побочного канала позволяет не только очертить границы аутентификации по сообщениям SERVICE_ACCEPT и USERAUTH_SUCCESS, но и узнать ее детали. У разных способов аутентификации отличается сетевое поведение: так, во время успешной аутентификации по паролю клиент и сервер OpenSSH обмениваются сообщениями 148 и 28 байт (USERAUTH REQUEST/RESPONSE). Размер 148 байт выбран не случайно — это специальная защита от внешнего наблюдателя. Если бы длина сообщения аутентификации зависела от длины имени пользователя или его пароля, злоумышленнику было бы легче его подобрать. Именно поэтому сообщение добивается служебными данными до 148 байт. Отгадать пароль становится сложнее, зато нам проще определить тип аутентификации.

При использовании публичного ключа добавляется дополнительный шаг: клиент отправляет свою подпись в сообщении более крупного размера (от 228 до 1164 байт). Интерактивная аутентификация встречается реже и характеризуется большим числом сообщений, так как происходит в формате запрос-ответ с дополнительными запросами (ввод пароля, подтверждение второго фактора).

Рисунок 5. Последовательность сообщений при аутентификации по паролю

Сообщения конфигурации канала

После успешной аутентификации клиент открывает необходимые ему каналы данных. Несмотря на то, что SSH-протокол допускает их открытие в любой момент, на практике это почти всегда происходит сразу после входа (либо не происходит вовсе). Граница этапа хорошо определяется по сообщению подтверждения канала от сервера, длина которого фиксирована для каждого набора алгоритмов. Например, для chacha20-poly1305@openssh.com — 36 байт.

Рисунок 6. Пример последовательности сообщений при открытии канала shell

Чаще всего SSH открывают для интерактивного шелла — то есть когда вы открываете окно SSH и вручную вводите команды на удаленном сервере. В этом случае клиент сначала отправляет несколько последовательных запросов для настройки окружения, после чего начинается передача команд пользователя. Другие варианты каналов подразумевают разовое выполнение команды (exec) или запуск подсистемы, например SFTP. Они используют тот же механизм открытия, но отличаются меньшим количеством запросов и другим поведением. После конфигурации канала «exec» обычно следует короткий обмен данными и быстрое завершение соединения, а после «subsystem» идет длительная сессия с крупными сообщениями, характерная для передачи файлов. Размеры сообщений при конфигурации каждого из этих каналов достаточно предсказуемы. Например, последовательность c348, c68, c44, s36, s36, s36 байт на рис. 6 («c» — пакет от клиента, «s» — пакет от сервера) указывает на shell-сессию. Последовательность c84, s72 — на exec или sftp.

Открытие SSH-туннеля

Одна из самых полезных фич SSH — проброс портов (port forwarding). Она позволяет открыть локальный TCP-порт или порт на SSH-сервере и передать через него соединения на другую сторону. Отдельную любовь заслужила опция -D, которая не требует указания удаленного узла и превращает SSH-соединение в Socks5-прокси. Этим функционалом любят пользоваться злоумышленники. 

При локальной переадресации SSH-клиент открывает локальный порт на своем узле, и все подключения к порту пересылаются на указанный хост и порт SSH-сервера. Так, команда «ssh -L 8080:104.18.26.120:80 user@18.248.239.191» приведет к тому, что соединения на 127.0.0.1:8080 на хосте клиента будут доставляться к сервису 104.18.26.120:80 через SSH-сервер 18.248.239.191. 

В случае удаленной переадресации все происходит наоборот: порт открывается уже на стороне SSH-сервера, а подключения перенаправляются хостом-клиентом SSH. Например, команда «ssh -R 2222:10.150.0.20:22 user@203.0.113.10» откроет на SSH-сервере 203.0.113.10 локальный порт 2222, соединения на который будут перенаправлены SSH-клиентом к 10.150.0.20:22.

Проброс портов подразумевает открытие специального дополнительного канала. Его отличие от уже упомянутых заключается в инициаторе открытия: при локальной (-L) и динамической (-D) переадресации инициатором выступает клиент, а при удаленной (-R) — сервер. В запросе на открытие такого канала передаются адрес назначения (IP-адрес или доменное имя) и порт, а также адрес и порт стороны-инициатора. Сообщения с запросом на открытие туннеля и его подтверждение имеют характерные фиксированные длины (отличающиеся от длин каналов shell или exec) — 92 и 44 байта соответственно. 

Рисунок 7. Сообщения конфигурации туннелей

Интерактивная shell-сессия

В интерактивной сессии пользователь вручную посимвольно вводит команды на удаленном сервере. Получается, что при ручном вводе каждая нажатая клавиша формирует сообщение фиксированного размера — фактически это один зашифрованный символ с добавлением служебных полей и паддинга. В ответ сервер присылает идентичное сообщение, подтверждая получение данных от клиента. В результате в трафике появляется последовательность небольших пакетов одинаковой длины, которые чередуются между клиентом и сервером (например, c36, s36, c36, s36 байт).

Если же shell-сессия управляется скриптом, то, напротив, пакеты данных между клиентом и сервером будут более редкими и крупными. Кстати, технология анализа JA4SSH основывается в том числе на этом поведении. Однако она не учитывает продолжительность сессий, а также наличие туннелей, что ухудшает ее точность.

Рисунок 8. Пример сообщений при передаче команд человеком и скриптом

Сложности анализа

Теория выглядит даже слишком просто: рассчитай размеры сообщений для каждого набора параметров и анализируй сессии. Но разные клиенты и среда передачи вносят сюда ложку дегтя. Например, разные клиенты и SSH-серверы могут добавлять служебные сообщения, не влияющие на поведение сессии, или пропускать отдельные описанные шаги. 

Другая серьезная проблема заключается в том, что многие клиенты и серверы выполняют объединение нескольких SSH-сообщений в один TCP-сегмент. Например, на этапе аутентификации из двух сообщений одинаковой длины SERVICE_REQUEST и SERVICE_ACCEPT мы нарочно выбрали второе (от сервера), потому что сообщение клиента SERVICE_REQUEST часто «склеивается» с предыдущим сообщением переменной длины New Keys, что мешает анализу.

Аналогия с TLS-протоколом самая прямая: несколько TLS-записей тоже могут склеиваться в один TCP-пакет. Правда, в заголовке каждой записи присутствует поле с ее размером, что позволяет отделять их друг от друга при анализе. В SSH-сообщениях такое поле тоже присутствует, но в незашифрованном виде оно передается при использовании лишь некоторых криптографических наборов — шифров с MAC в режиме ETM (*-etm) или шифров aes*-gcm. В остальных криптографических режимах, в том числе самом популярном chacha20-poly1305, длина SSH-сообщения скрыта внутри зашифрованной части. В результате границы SSH-сообщений перестают совпадать с границами TCP-сегментов. Это значит, что каждую комбинацию SSH-клиента, SSH-сервера и выбранных алгоритмов нужно отдельно изучать для составления паттерна последовательности длин пакетов.

Наконец, основная проблема анализа побочного канала SSH-соединений заключается в сжатии. Современные реализации протокола поддерживает только алгоритм сжатия zlib, но и он используется сравнительно редко. Например, в OpenSSH сжатие отключено по умолчанию и включается на клиенте принудительно — с помощью опции -C. Но этого вполне достаточно, чтобы значительно усложнить анализ SSH-сессий. После успешной аутентификации алгоритм сжатия начинает постепенно накапливать словарь передаваемых данных и использовать его в последующих сообщениях. В результате длины пакетов начинают меняться нелинейно и все сильнее зависят от предыдущего содержимого сессии (одно и то же сообщение в разных контекстах при включенном сжатии может иметь разную длину). Поэтому наиболее устойчивые признаки обычно наблюдаются именно на этапе аутентификации, тогда как дальнейший анализ со временем становится практически невозможным.

Склеим все воедино

Собрав результаты исследования, мы буквально можем разобрать SSH-сессию по кирпичикам. На рис. 10 приведено обычное соединение встроенным в Ubuntu SSH-клиентом на сервер Debian. 

Рисунок 9. Соединение встроенным в Ubuntu SSH-клиентом на сервер Debian

Что же происходит внутри?

  • Блок 1: открытая часть. Клиент и сервер обмениваются баннерами и поддерживаемыми алгоритмами. Алгоритм выбран самый популярный — chacha20-poly1305.
  • Блок 2: после обмена ключами начинается этап аутентификации. Отчетливо видны сообщения от клиента и сервера длиной 44 байта, которые соответствуют сообщениям SERVICE_REQUEST и SERVICE_ACCEPT. Аутентификация завершается успешно, о чем говорит сообщение от сервера USERAUTH_SUCCESS длиной 28 байт. Кстати, при неуспешной аутентификации сообщение имело бы длину от 44 до 76 байт. На использование метода password намекает пакет длиной 148 байт.
  • Блок 3: согласование канала интерактивной shell-сессии обозначено характерным запросом клиента и ответом сервера с длинами 376 и 108 байт. Особенность клиента и сервера OpenSSH в том, что они выполняют объединение всех сообщений CHANNEL_REQUEST в один запрос и CHANNEL_SUCCESS в один ответ.
  • Блок 4: интерактивная сессия. Много идущих подряд сообщений длиной 36 байт. Каждое из них соответствует передаче одного зашифрованного символа, что говорит о работе человека, а не скрипта.

Пустоты между упомянутыми блоками занимают сообщения без устойчивых паттернов длины, не несущие практической пользы для анализа. Речь идет о CHANNEL_OPEN, GLOBAL_REQUEST и CHANNEL_OPEN_CONFIRMATION, через которые устанавливается управляющий служебный канал и передается hostkey сервера. А также о сообщениях SSH_MSG_CHANNEL_DATA, которые содержат приветственную строку терминала.

Итак, после анализа десятка сообщений сессии мы выяснили: пользователем был человек, который с первого раза успешно ввел пароль от SSH и открыл интерактивное окно терминала. С одной стороны, это совершенно обычное SSH-соединение и анализ побочного канала здесь выглядит избыточно. Но что вы скажете, если вдруг обнаружите множество неуспешных попыток или сетевой туннель в привычной картине входящих SSH-подключений к вашим серверам? 

***

Многие, в том числе злоумышленники, привыкли считать, что шифрование сетевых коммуникаций оставляет систему анализа сетевого трафика не у дел. Это распространенный миф, с которым борется наша команда. Любое шифрование — это компромисс между скрытностью и эффективностью. Порой сам факт шифрования — это и есть самый красный флаг ;)

Приведенные в статье алгоритмы успешно реализованы в системе анализа сетевого трафика PT NAD, ознакомиться с ними можно здесь

Мы дěлаем Positive Research → для ИБ-экспертов, бизнеса и всех, кто интересуется ✽ {кибербезопасностью}