Кто стучится в открытые порты: я написал ловушку для ботов и разобрал их команды


Открытый порт на сервере редко остаётся незамеченным. По интернету постоянно гуляют боты, которые перебирают адреса, проверяют доступные службы, пробуют имена и пароли. Иногда они просто смотрят, что им ответит сервер. Иногда после ответа отправляют готовый набор команд. Человека за клавиатурой при этом может вообще не быть.
Я уже столкнулся с таким гостем на практике. Из-за моей ошибки наружу смотрел порт базы данных, а сервер несколько дней уходил в перегрузку. После этого мне стало интересно. Кто на самом деле пытается подключиться к открытому порту, что успевает сделать за короткую сессию и чего добивается?
Чтобы в этом разобраться, я создал программу-приманку. Она изображает несколько обычных серверных служб, отвечает клиентам и записывает все их действия. А полученные команды она не исполняет.
В логах с активными записями приманка собрала 2406 сессий от 309 IP-адресов. Многие подключения занимали около секунды, но даже за это время клиент успевал войти и быстро проверить систему. Ниже разберу, как устроена ловушка, что именно попало в журнал и как защититься от этого.
❯ Почему ваш сервер нужен не только вам

Кажется, что сервер с личным сайтом или небольшим ботом вряд ли привлечёт чужое внимание. На нём нет крупного бизнеса, а адрес знает несколько человек.
Но автоматической программе не обязательно знать о проекте. Она проверяет IP-адреса и порты. Если служба отвечает, с ней можно попробовать поговорить: узнать её название, проверить вход, отправить знакомый запрос. Следующий адрес скрипт проверит тем же способом.
Для владельца это фоновая активность, которая не мешает сайту работать. Для сканера это поиск доступных служб. Часть клиентов ограничивается короткой проверкой, другие пытаются пройти дальше.
В моём случае проблема обнаружилась не в самом Telegram-боте. В docker-compose был опубликован порт его базы данных, хотя приложение обращалось к ней по внутренней сети. Работающий бот не помог заметить лишний внешний вход.
Подробности я описал в предыдущей статье. Там я рассматривал вероятный сценарий: подключение к PostgreSQL, получение доступа и запуск майнера. Без полных логов и снимка системы подтвердить всю цепочку было нельзя.
В этот раз я хотел не реконструировать события задним числом, а увидеть сами подключения. Причём дать клиенту пройти дальше первого приветствия и посмотреть, что он сделает.
❯ Как устроена ловушка

Ловушка такого типа называется honeypot. Вместо полезного рабочего сервиса она предлагает правдоподобную имитацию, за которой удобно наблюдать.
В DotBotHunt используется пакет honeybot. Программа изображает шесть служб.
Служба | Порт приманки | Что получает клиент |
|---|---|---|
SSH | 2222 | Приветствие сервера и имитация командной строки |
HTTP | 8080 | Несколько страниц и форм входа |
Telnet | 2323 | Запрос имени и пароля, затем приглашение |
FTP | 2121 | Приветствие и ответы файлового сервера |
SMTP | 2525 | Ответы почтового сервера |
Redis | 6379 | Ответы, похожие на ответы базы Redis |
Это не шесть установленных сервисов и не отдельная виртуальная машина. Программа сама формирует ответы. Для клиента она изображает сервер web-01 с Ubuntu 22.04, но эти сведения не описывают систему, на которой на самом деле запущена ловушка.
Схема простая: клиент подключается к одному из портов, а приманка отвечает от имени соответствующей службы. На SSH и Telnet она принимает первый пароль, чтобы увидеть действия пользователя после входа. Все попытки входа, введённые команды, время и адрес подключения записываются. Затем приманка возвращает подготовленные ответы, не передавая команды настоящей оболочке.
Если клиент спрашивает о системе, он получает сведения о вымышленной машине. Попытка скачать файл не приводит к настоящей загрузке. SMTP не рассылает письма, а SSH не становится туннелем к чужим адресам. Сетевые соединения по командам клиента не открываются.
Первый принятый пароль здесь не означает, что бот угадал секрет. Это поведение ловушки. Если бы она сразу отказывала во входе, я увидел бы меньше действий после авторизации.
Для просмотра записей есть локальная панель и HTML-отчёт. Панель не нужно публиковать в интернет.
При этом имитация команд сама по себе не делает сетевую программу неуязвимой. Для таких экспериментов лучше отдельное окружение без рабочих данных, секретов и доступа к внутренним сервисам. Такая ловушка нужна для наблюдения, а не для защиты проектов, запущенных рядом.
❯ Разбор логов
Общая картина
К моменту выгрузки отчёта в журнале накопилось 2406 сессий от 309 IP-адресов. Записи охватывают три дня: 7, 11 и 12 октября 2026 года. На SSH по этому отчёту пришлось 68% подключений. Остальные службы привлекли заметно меньше внимания.

По датам картина получилась неровной.
7 октября: 1646 сессий. Большой поток SSH-подключений, из которых 1166 пришли с одного адреса в сети DigitalOcean.
11 октября: 84 сессии. На этот раз больше половины подключений пришлось на HTTP.
12 октября: 676 сессий к моменту выгрузки. Снова много SSH, а рядом с ним повторяющийся сценарий через Redis.
С 8 по 10 октября сервер оставался включённым, но не отвечал на подключения. Поэтому эти дни не описывают обычную активность работающей ловушки.

Ещё одна деталь, всего два адреса дали 1513 сессий, почти 63% всех подключений. Первый появился в сети DigitalOcean, второй в сети DMZHOST. Значительная часть журнала оказалась не разнообразием новых посетителей, а повторением похожих действий.
SSH: войти и проверить машину

Клиент входит под именем ubuntu в 15:22:05, отправляет семь команд и отключается в 15:22:06.
За эту секунду он проверяет время работы системы, сведения об ОС и графические устройства. Набор запросов выглядит так:
Что проверял клиент | Какие команды использовал |
|---|---|
Как долго работает система |
|
Есть ли видеокарта NVIDIA и как она называется | Два варианта запроса через |
Какие графические контроллеры видны в системе | Три варианта запроса через |
Что это за система и машина |
|
Имена для входа менялись: ubuntu, root, admin, postgres, bitrix, guest и другие. А действия после входа оставались одинаковыми. Клиенты часто представлялись строкой SSH-2.0-Go. Это подпись SSH-клиента, а не уникальное имя бот-сети, поэтому по ней нельзя установить общего владельца этих адресов.
Особенно меня заинтересовали пять запросов для проверки графических устройств. Они позволяют узнать, есть ли у машины GPU, но не объясняют, зачем клиент его ищет.
Такие сессии похожи на автоматический сбор сведений о машине: вход, несколько проверок и отключение.
Redis: попытка захватить службу
С Redis всё выглядело заметно агрессивнее. Один адрес создал 100 подключений с почти одинаковым сценарием. В таблице из них сохранились 83 завершённые сессии. Каждая длилась от 36 до 41 секунды. Новые попытки начинались примерно раз в минуту. Клиент не останавливался после неудачи и повторял один и тот же порядок действий.
Для примера взял запись с 11:22:13 до 11:22:50. За 37 секунд клиент отправил 22 команды. Он подключился к Redis без авторизации, запросил сведения о сервере и отправлял команды очистки базы. Затем пытался записать в базу строки со сценариями скачивания и запуска файла, а также добиться выполнения команд через Lua.

Это была уже не простая проверка доступности порта. Клиент перебирал способы заставить Redis выполнить свой код. Он пытался загрузить модуль, изменить настройки, подготовить условия для дальнейших действий и добиться обратного соединения. В логах также видны многократные попытки загрузить модуль, изменить роль Redis, загрузить файл и вызвать системные процессы.
В конце атакующий отправлял содержимое SSH-ключа и менял путь и имя файла базы. Имена временных файлов и внешние адреса менялись, но общая схема атаки оставалась той же.
Но присланная инструкция и выполненная инструкция не одно и то же. Приманка сохранила команды, не запустив их и не открыв соединений наружу. По этим записям нельзя установить конкретную уязвимость или оценить, сработал бы весь сценарий на настоящем Redis.
Если бы такие команды выполнились на настоящем Redis, злоумышленник мог бы очистить или подменить данные, записать в базу вредоносные инструкции и попытаться запустить свой код на сервере. Дальше он мог бы закрепиться в системе, добавить SSH-ключ для доступа или использовать машину для новых атак.
Короткие проверки и другие подключения
Не каждый гость пытался пройти дальше первого ответа. В 410 сессиях клиент подключался, получал приветствие службы и сразу отключался
Такая короткая проверка может быть началом более серьёзного сканирования. По ответу службы можно узнать её название и версию, подобрать подходящие команды или найти известную уязвимость. В этих сессиях дальше первого приветствия дело не пошло, но сами подключения показывают, что порты кто-то активно проверял.
В HTTP чаще всего встречались запросы к /favicon.ico, /login, /manager/html, /sse и /mcp. Были и запросы CONNECT, с помощью которых клиент пытался создать туннель через прокси. Ловушка такие соединения не открывала, но сам запрос показывает, что кто-то проверял эту возможность.
Среди подключавшихся встречались программы с подписями CensysInspect, Infrawatch и research-scan/1.0. За такими именами могут стоять исследовательские сканеры, а не только инструменты для взлома. Но подпись не доказывает добрых намерений. По ней видно, каким клиентом сделан запрос, а не зачем он это сделал.
Что удалось понять по записям
Самый активный источник не оказался самым опасным. Большую часть статистики создали повторяющиеся SSH-проверки, но самые серьёзные действия были видны в Redis. Там клиент не просто осматривал службу, а пытался изменить её настройки и добиться выполнения своего кода. Поэтому для оценки риска недостаточно считать подключения, нужно смотреть на сами логи.
Есть и пределы такого наблюдения. Это одна ловушка на определённых портах, с заранее заданными ответами. Подробная таблица содержит только последние 500 сессий, а IP-адреса и названия сетей не устанавливают личности операторов.
Логи показывают, что клиент пытался сделать после ответа сервера. Но чтобы понять, сработала бы атака на настоящем сервере, нужны другие доказательства.
❯ Как защитить свой сервер

Что бы предотвратить даже малейшие шансы на взлом сервера, следует позаботиться об этом заранее. Разберём основные подходы защиты сервера от внешних угроз.
1. Оставить снаружи только нужные службы
Перед запуском проекта полезно составить короткий список разрешённых подключений.
Компонент | Кто должен подключаться | Как ограничить доступ |
|---|---|---|
Публичный сайт или API | Посетители | Открыть только необходимые веб-порты |
SSH и административные панели | Администратор | Разрешить доступ только с доверенных IP-адресов |
База, кэш, очередь | Приложения | Оставить доступ только во внутренней сети |
Панель ловушки | Оператор | Использовать локальный доступ или защищённое SSH-подключение |
Для общения двух контейнеров внутри одной сети Compose не требуется публиковать порт в интернет. Если внешних клиентов нет, публикация порта просто добавляет ненужный доступ и возможную точку взлома.
Для Redis тот же принцип сформулирован в документации. Обращаться должны только доверенные клиенты, а не весь интернет.
2. Проверять результат снаружи
Изнутри сервера нужно посмотреть, какие службы слушают порты и что опубликовано контейнерами. Затем проверить доступ с другой машины, вне сети сервера.
Например, в Windows PowerShell:
Test-NetConnection <IP_СЕРВЕРА> -Port 5432Для базы PostgreSQL, которая не должна быть доступна извне, в результате должно быть TcpTestSucceeded: False. Заодно проверьте публичную службу, которая должна принимать подключения. Если она тоже недоступна, возможна ошибка в проверке или сетевых правилах, а не правильная настройка.
С Docker есть нюанс. Опубликованные порты контейнеров могут обходить привычные правила UFW из-за порядка обработки трафика. Поэтому недостаточно включить файрвол и посмотреть его статус. Нужно учитывать сетевые правила Docker и проверять фактическую доступность портов снаружи.
Если сервер использует IPv4 и IPv6, ограничения нужно проверить для обоих протоколов. После изменения Compose, сетевой схемы или правил фильтрации проверку стоит повторить.
3. Ограничить число неудачных попыток
Дополнительный слой защиты даёт fail2ban. Программа следит за журналом SSH и после нескольких неудачных попыток входа временно блокирует IP-адрес нарушителя. Простая настройка обычно отсекает большую часть автоматических подключений. Бот не успевает перебрать много паролей, потому что после нескольких ошибок его адрес попадает в блокировку.
Это не отменяет остальные меры. Перебор пароля становится практически невозможным только вместе с ключами SSH, ограничением доступа и закрытием ненужных портов. Но fail2ban хорошо снижает шум и замедляет автоматические атаки.
4. Ограничивать права после входа
Сетевой фильтр, аутентификация и права решают разные задачи. Фильтр определяет, откуда можно подключиться, аутентификация проверяет клиента, права ограничивают его действия.
Для SSH стоит использовать ключи вместо пароля. Ненужный парольный вход отключают только после проверки, что доступ по ключу работает. Рабочему приложению не нужна учётная запись суперпользователя базы, а контейнеру не стоит давать привилегии или доступ к Docker-сокету без конкретной необходимости.
Смысл этих мер в том, чтобы ошибка в одном компоненте не давала полный контроль над остальным окружением.
Перенос SSH на другой порт тоже не делает его недоступным для сканирования. Сначала стоит закрыть лишний внешний доступ, настроить вход и права, а затем снижать оставшийся шум.
5. Логи и резервные копии до инцидента
Логи и резервные копии нужны до того, как сервер начнёт вести себя странно. Недостаточно просто включить запись. Стоит заранее проверить, сколько времени хранятся журналы, доступны ли они после сбоя и можно ли восстановить данные из резервной копии.
Если появилось подозрение на взлом, сначала изолируйте затронутое окружение. По возможности сохраните журналы и снимок диска, затем отзовите скомпрометированные секреты и восстанавливайте систему из проверенного источника. Удаление подозрительного процесса само по себе не возвращает доверие к серверу.
К такому выводу я пришёл после инцидента со своим сервером. Подробный разбор того случая я уже делал в отдельной статье. Если коротко, тогда мне удалось вернуть контроль переустановкой системы, но вместе с ней исчезла часть следов. Если бы у меня были журналы и свежие резервные копии, разобраться в произошедшем и восстановить работу было бы проще, а ущерб оказался бы меньше.
❯ Что получилось в итоге
DotBotHunt позволил посмотреть на действия клиентов после подключения, а не только на последствия возможного взлома. Самыми интересными оказались повторяющиеся последовательности: по ним видно, какие шаги программа выполняет автоматически и на каком месте переходит от проверки к попытке что-то изменить.
При этом ловушка не закрывает порты и не защищает соседние сервисы. Её результатом стал журнал для разбора, а не безопасный сервер.
Для рабочего проекта я бы начал с более простой вещи: сравнил список нужных внешних служб с тем, что действительно доступно из интернета. Именно такой проверки не хватило моему предыдущему проекту.
Ссылки
Может быть интересно:

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram‑канале ↩
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.