
Привет, Хабр! Это Антон Грищенко, руководитель L1 SOC, и Назар Корниенко, ведущий аналитик L1 SOC в RED Security. Почти все разработчики хоть раз пользовались протоколом SSH. SSH-туннели — это соединения между локальной машиной и удаленным сервером через функцию перенаправления портов. Они создаются с целью обеспечить безопасный коннект по ненадежным сетям. Это мощный инструмент для работы и передачи данных.
Однако их могут использовать злоумышленники для обхода правил межсетевых экранов и скрытия своей активности. Защититься от таких махинаций можно с помощью детектирования SSH-туннелей, и в этом материале мы пошагово расскажем, как это делается c помощью логирования системных вызовов от демона SSH на Linux-хостах.
Как работают SSH-туннели
Их используют многие системные администраторы, т. к. туннели помогают подключаться ресурсу, базе данных или сервису, который находится в закрытой сети, когда есть SSH-доступ к одному из серверов в ней.
Избежать запросов к промежуточному серверу помогает процесс переадресации портов (Port Forwarding):
Допустим, есть сервер S1 с полным доступом на любой из портов.
Есть сервер S2, к 80 порту которого мы хотим получить доступ через S1.
С помощью Port Forwarding мы делаем так, чтобы через открытый порт 80 на S1 мы слали запросы на этот же порт S2 и получали ответ. Также мы можем открыть другой порт (условно 70), который будет переадресовывать запрос на 80-й порт сервера S3.
Проще говоря, использование туннелей — это применение SSH как прокси. Туннели могут быть локальными, удаленными и динамическими. В последнем SSH-клиент настраивается как SOCKS-прокси.
Локальные туннели
В простейшей форме SSH-туннель открывает порт на локальном хосте, который подключается к порту удаленного хоста:
$ ssh -L 81:127.0.0.1:80 user@remoteserver
Флаг -L
отвечает за туннелирование. Его параметр можно представить как локальный хост прослушивания: в этом примере порт 81 мониторится на нашей системе. А после идет переадресация через 80-й порт на удаленный сервер, причем к localhost на удаленном сервере.
Разберем другой пример:
$ ssh -L 127.0.0.1:81:example.com:80
Эта команда уже перенаправляет локальные подключения от 127.0.0.1:81 на 80-й порт example.com. Трафик между локальной системой и SSH-сервером идет по этому туннелю, а между SSH-сервером и example.com — нет.
Если подниматься выше, то можно сделать так, чтобы порты прослушивания связывались с другими узлами локальной сети:
$ ssh -L 0.0.0.0:81:127.0.0.1:80 user@remoteserver
Здесь уже туннель перенаправляет запросы от remoteserver к localhost на порту 80. Трафик с remoteserver к 127.0.0.1 уже не в SSH-туннеле — его сервер будет считать remoteserver-источником запросов.
Удаленные туннели
Это способ проброса сетевого трафика с удаленного сервера на локальную машину через SSH-соединение:
$ ssh -R 0.0.0.0:81:localhost:80 user@target
В этом примере мы поднимаем на удаленном сервере порт 81, направленный трафик на который будет идти через туннель на порт 80 хоста target. То есть 0.0.0.0 — это уже не локальный, а удаленный сервер — внешний трафик пробрасывается до локальной машины. Так можно закрепиться в инфраструктуре.
Кроме этого, есть возможность сделать «обратный» ssh-туннель:
$ ssh -v -R 0.0.0.0:81:127.0.0.1:80 192.168.1.100 user@remoteserver
Здесь мы настраиваем прослушивающий порт уже на удаленной машине, которая подключается к нам на localhost к нашему порту. В примере устанавливается соединение с порта 81 на remoteserver к порту 80 через наш клиент.
Динамические туннели
Они создаются с флагом -D
:
$ ssh -D 3000 REMOTESERVER
Этот вариант позволяет открыть на своем локальном хосте SOCKS прокси-сервер и использовать для подключения промежуточный сервер. Такие туннели оставляют минимум артефактов и следов, что делает их самыми незаметными.
Риски при использовании SSH-туннелей
Ими может воспользоваться не только сисадмин, но и злоумышленник. Особенно опасны динамические туннели, через которые можно получить доступ не к одному серверу, а ко всей инфраструктуре.
SSH-туннели позволяют обойти сетевые фильтры, ограничения, фаерволлы и весь защищаемый периметр. А также помогают скрыть и анонимизировать трафик, организовать обратный доступ (через тот же reverse tunnel, который описан выше) и пробросить вредоносные инструменты.
Их достаточно сложно определить, поэтому тем, кто занимается информационной безопасностью, важно понимать, с какой целью в их ландшафте используется тот или иной туннель. Также можно усложнить или даже исключить возможность их создания, обнаруживать их на уровне ОС и изолировать самого инициатора данного процесса.
Анализ bind/connect от SSHD и детектирование SSH-туннелей
На каждом linux-сервере вместе с ssh работает его демон — SSHD. Он уже обслуживает все соединения и генерирует два основных системных вызова — это bind для открытия порта и connect для подключения к хосту. Именно они чаще всего сигнализируют о создании SSH-туннеля. Так что можно логировать их через системные средства (auditd) без анализа сетевого трафика.
Auditd (Linux Audit Daemon) — компонент пользовательского пространства подсистемы аудита Linux. Он перехватывает системные вызовы от приложений и пользователей, оценивает их по набору предопределенных правил и записывает срабатывания в централизованный журнал аудита системных событий.
Давайте поподробнее разберем каждый вызов в контексте ssh-туннелей. Мы используем именно auditd для детектирования, а не стандартные логи SSH, так как обнаружить туннели можно только на уровне системы.
Syscall Bind
Этот вызов создает серверный сокет, к которому могут подключаться удаленные клиенты. На целевом сервере будет сделан bind()
, прослушивающий определенный порт и пересылающий данные на хост-источник туннеля.
Допустим, где-то был создан удаленный ssh-туннель:
$ ssh -R 0.0.0.0:9999:localhost:8080 root@11.62.10.146
И мы получаем примерно такой сырой лог вызова:

В первую очередь нам важен открытый порт и не используемый интерфейс. Если в поле 4 нуля, значит, он открыт для всех. Затем мы смотрим, что это за сессия и кто ее инициировал. По номеру сессии можно найти способ авторизации пользователя.
Syscall Connect
Для connect()
ситуация похожа. Только в первую очередь нас интересует, к какому хосту прокидывается доступ и куда идет подключение. Например, при команде создания локального туннеля:
$ ssh -L 9999:11.62.10.249:8080 root@11.62.10.146
Лог вызова будет таким:

Здесь снова можно увидеть и сессию, и пользователя.
Настройка сбора событий и их мониторинг через auditd
Для начала нужно отредактировать файл /etc/audit/rules.d
:
#######Network Events
#####Connect x64/x32 (for new auditd IPv4 saddr_fam would be 2 and IPv6 saddr_fam would be 10)
-a always,exit -F arch=b64 -S connect -F a2=0x10 -k Commonly_Used_Port
-a always,exit -F arch=b64 -S connect -F a2=0x1c -k Commonly_Used_Port
#######Bind x64/x32 (a2!=0c)
-a always,exit -F arch=b64 -S bind -F a2=0x10 -k
Commonly_Bind_Port -a always,exit -F arch=b64 -S bind -F a2=0x1c -k Commonly_Bind_Port
Данная конфигурация собирает с linux-хостов вызовы bind
и connect
, о которых мы говорили ранее.
Что еще важно: в рамках локального хоста происходит много соединений и прослушиваний, поэтому нужно не просто мониторить все вызовы. Здесь нам помогает параметр a2
— длина структуры sockaddr (addrlen). С его помощью мы можем ограничить события только теми, которые действительно относятся к сетевым соединениям по протоколам IPv4/IPv6.
Данная конфигурация auditd помогает логировать все системные вызовы connect и bind, только если это касается сетевых подключений по IPv4/IPv6:
a2=0x10 — фильтруются вызовы connect/bind для IPv4;
a2=0x1C — то же самое, но для IPv6.
Создание детектирующей логики на примере Sigma Rule и KUMA Rule
Sigma Rule — формат описания правил для обнаружения угроз в логах, разработанный проектом SigmaHQ. Это стандарт с открытым исходным кодом, который позволяет создавать и обмениваться правилами обнаружения в разных SIEM-системах.
Правило Sigma Rule в нашем случае:

KUMA Rule — это правила корреляции в системе Kaspersky Unified Monitoring and Analysis Platform.
Правило Kuma Rule выглядит так:

Сама логика относительно простая — мы ищем системные вызовы connect или bind от исполняемого файла sshd. Но кроме этого, важно, чтобы command line (proctitle) содержал в себе строку sshd. Без этого мы иначе будем реагировать на обычные подключения по SSH. А вот процессы туннелей уже содержат эту строку.
Профилирование
Для логики выше обычно будет возникать 300 срабатываний в неделю на 1 000 хостов auditd. Такое количество нужно снижать. Для этого исключим:
Подстроку
Proctitle contains [listener] 0 of 10-100 startups
— это логовая запись демона SSH об активных подключениях.Целевой порт 0 (неопределенный порт, тестирование или отладка, ошибки).
Точные целевые порты, связанные с x-сервером. Если невозможно исключить конкретные порты, то можно взять диапазон (например, чтобы целевой порт не был в промежутке от 6 000 до 6 200).
Пример расследования через вызов connect
Допустим, мы настроили мониторинг и профилирование и получили вот такое событие:

Оно было зафиксировано на адресе 11.62.10.146. Произошло там следующее:

Процесс SSHD выполнил подключение на HTTP-порт 8080 целевого хоста 11.62.10.249. Сразу задаем вопрос: зачем sshd обращается на HTTP-порт?
Нам стоит обратить внимание на номер сессии (в данном случае — 33440). Мы видим способ входа на хост в рамках этой сессии и видим источник — 11.62.10.253. И здесь уже можно сделать вывод, что злоумышленник пытался получить прямой доступ на критический сервер Target через порт 8080:

Для этого он создал локальный туннель, обратившись на порт 9999 хоста Hacker, и с помощью туннеля через ProxyServer получил доступ к целевому хосту.
Аналогичным образом вызов connect показывает динамический туннель. В данном случае нам помогает исключительно наше правило — мы не видим используемое ПО и команды, но можем отловить момент подключения.
Пример расследования через вызов bind
Все аналогично начинается после срабатывания события:

Смотрим на ID сессии и понимаем, что порт открыт на прослушивание. Алгоритм такой же, как и в случае с connect
: выясняем способ подключения к хосту и строим гипотезу или смотрим по событиям с хоста-источника действия злоумышленника. В данном случае у нас удаленный туннель, при котором мы «публикуем» локальный порт хоста 11.62.10.253:8080 (Target) через хост 11.62.10.146:9999 (ProxyServer).

Цель злоумышленника может заключаться в публикации сервиса 11.62.10.253:8080, доступного только с 11.62.10.146. Он закрепляется через команду в cron (задачнике в Linux, который позволяет делать отложенный вызов команд — например, каждый день в 18:00 поднимать SSH-туннель).
Харденинг ssh
Если следовать максимальным требованиям, то нужно выставить в /etc/ssh/sshd_config
следующие параметры:
+ `AllowTcpForwarding no`
+ `GatewayPorts no`
+ `PermitTunnel no`
+ `PermitRootLogin no`
+ `PermitRootLogin without-password no`
Не бегите прямо сейчас копировать их! Сначала изучите требования своих сервисов и выставляйте параметры в зависимости от вашей конфигурации. В данном случае настройка ssh запрещает маршрутизацию и туннелирование сетевого трафика.
Что хочется сказать в конце
SSH-туннель — полезная вещь, но она удобна не только вам, но и злоумышленникам. Всегда нужно иметь задокументированные стандарты безопасности (например, для харденинга ssh, как в случае выше). В этом материале мы показали детектирование SSH-туннелей на основе системных вызовов от демона SSH, который покрывает большинство случаев.
Можно пойти и другими путями: через анализ сетевого трафика на аномалии, следить за метриками потребления ресурсов sshd. Но логирование bind и connect позволяет в целом увидеть картину: какой тип туннеля использует злоумышленник и как он попадает в систему (подключается или открывает порт). Так можно отслеживать подобные проникновения на уровне системы, без расшифровки трафика.
Если у вас остались вопросы, замечания или иные полезные заметки, пишите в комментарии. Спасибо за внимание!