Привет, Хабр! Это Антон Грищенко, руководитель L1 SOC, и Назар Корниенко, ведущий аналитик L1 SOC в RED Security. Почти все разработчики хоть раз пользовались протоколом SSH. SSH-туннели — это соединения между локальной машиной и удаленным сервером через функцию перенаправления портов. Они создаются с целью обеспечить безопасный коннект по ненадежным сетям. Это мощный инструмент для работы и передачи данных.

Однако их могут использовать злоумышленники для обхода правил межсетевых экранов и скрытия своей активности. Защититься от таких махинаций можно с помощью детектирования SSH-туннелей, и в этом материале мы пошагово расскажем, как это делается c помощью логирования системных вызовов от демона SSH на Linux-хостах.

Как работают SSH-туннели

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

Избежать запросов к промежуточному серверу помогает процесс переадресации портов (Port Forwarding):

  1. Допустим, есть сервер S1 с полным доступом на любой из портов.

  2. Есть сервер S2, к 80 порту которого мы хотим получить доступ через S1.

  3. С помощью 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. Такое количество нужно снижать. Для этого исключим:

  1. Подстроку Proctitle contains [listener] 0 of 10-100 startups — это логовая запись демона SSH об активных подключениях.

  2. Целевой порт 0 (неопределенный порт, тестирование или отладка, ошибки).

  3. Точные целевые порты, связанные с 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 позволяет в целом увидеть картину: какой тип туннеля использует злоумышленник и как он попадает в систему (подключается или открывает порт). Так можно отслеживать подобные проникновения на уровне системы, без расшифровки трафика.

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

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