Библиотека — FreeBSD VPS: Honeypot и мониторинг безопасности [X]
Шифр
freebsd-honeypot-guide
Разделы
freebsd security
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
16 290 знаков · 36 разделов · чтения около 15 минут

FreeBSD VPS: Honeypot и мониторинг безопасности


Обзор архитектуры

Интернет
    │
    ▼
┌─────────────────────────────────────┐
│  FreeBSD Host (pf firewall)         │
│                                     │
│  vtnet0 (внешний IP)                │
│  ├── порт 22  → rdr → jail cowrie   │
│  ├── порт 52222 → настоящий SSH     │
│  │                                  │
│  bastille0 (loopback bridge)        │
│  └── 10.17.89.0/24                  │
│       └── 10.17.89.1 — jail cowrie  │
│            └── Cowrie :2222         │
└─────────────────────────────────────┘

Реальный SSH живёт на нестандартном порту (52222), защищён ключами и rate limiting. Порт 22 проброшен через pf в jail с Cowrie — SSH-honeypot, который имитирует уязвимый сервер и записывает все действия атакующих.


Повседневные операции

Управление Cowrie

Команды ниже относятся к установке из клона репозитория, где точка входа — src/cowrie/scripts/cowrie.py. Апстрим с тех пор переехал: Cowrie 3.x ставится из PyPI в виртуальное окружение и управляется собственной командой (cowrie init, cowrie start), минимальная версия Python — 3.10. При переустановке с нуля разумнее брать этот путь, а приведённые вызовы читать как описание уже работающей установки.

# Запуск
doas bastille cmd cowrie su -m cowrie -c \
  "/usr/local/bin/python3.11 /home/cowrie/cowrie/src/cowrie/scripts/cowrie.py start"

# Остановка
doas bastille cmd cowrie su -m cowrie -c \
  "/usr/local/bin/python3.11 /home/cowrie/cowrie/src/cowrie/scripts/cowrie.py stop"

# Перезапуск
doas bastille cmd cowrie su -m cowrie -c \
  "/usr/local/bin/python3.11 /home/cowrie/cowrie/src/cowrie/scripts/cowrie.py stop"
doas bastille cmd cowrie su -m cowrie -c \
  "/usr/local/bin/python3.11 /home/cowrie/cowrie/src/cowrie/scripts/cowrie.py start"

# Статус (проверить, слушает ли порт)
doas bastille cmd cowrie sockstat -l | grep 2222

Управление jail

# Список jail
doas bastille list

# Войти в консоль jail
doas bastille console cowrie

# Запуск/остановка jail
doas bastille start cowrie
doas bastille stop cowrie

# ZFS-снапшот jail (перед изменениями)
doas zfs snapshot zroot/bastille/jails/cowrie@backup-$(date +%Y%m%d)

# Список снапшотов
zfs list -t snapshot

# Откат к снапшоту
doas zfs rollback zroot/bastille/jails/cowrie@backup-YYYYMMDD

Мониторинг

Скрипты мониторинга

Система мониторинга состоит из двух скриптов:

/usr/local/bin/honeypot-monitor — запускает/переключает tmux-сессию мониторинга:

#!/bin/sh

SESSION="honeypot"

# Если сессия уже есть — переключиться/подключиться
if tmux has-session -t $SESSION 2>/dev/null; then
  if [ -n "$TMUX" ]; then
    exec tmux switch-client -t $SESSION
  else
    exec tmux attach -t $SESSION
  fi
fi

# Создать сессию с логом в реальном времени (без debug и сырых дампов)
tmux new-session -d -s $SESSION -n logs \
  "doas bastille cmd cowrie tail -f /home/cowrie/cowrie/var/log/cowrie/cowrie.log | grep --line-buffered -v -E 'debug|discarded direct-tcp'"

# Панель снизу — успешные входы и команды
tmux split-window -t $SESSION:logs -v \
  "doas bastille cmd cowrie tail -f /home/cowrie/cowrie/var/log/cowrie/cowrie.json | grep --line-buffered -E 'login.success|command'"

# Второе окно — статистика
tmux new-window -t $SESSION -n stats "honeypot-stats"

# Вернуться на первое окно и переключиться/подключиться
tmux select-window -t $SESSION:logs
if [ -n "$TMUX" ]; then
  tmux switch-client -t $SESSION
else
  tmux attach -t $SESSION
fi

/usr/local/bin/honeypot-stats — обновляемая статистика (вынесен отдельно, чтобы избежать проблем с экранированием кавычек в tmux):

#!/bin/sh
while true; do
  clear
  echo "=== Attacks today (connections) ==="
  doas bastille cmd cowrie grep -c "session.connect" /home/cowrie/cowrie/var/log/cowrie/cowrie.json 2>/dev/null
  echo "=== Successful logins today ==="
  doas bastille cmd cowrie grep -c "login.success" /home/cowrie/cowrie/var/log/cowrie/cowrie.json 2>/dev/null
  echo "=== Bruteforce bans ==="
  doas pfctl -t bruteforce -T show 2>/dev/null | wc -l
  echo "=== Active connections on port 22 ==="
  doas pfctl -s states 2>/dev/null | grep ":22 " | wc -l
  sleep 10
done

Оба скрипта должны быть исполняемыми: doas chmod +x /usr/local/bin/honeypot-monitor /usr/local/bin/honeypot-stats

Примечания по статистике:

  • Attacks today считает только session.connect (уникальные подключения), а не все строки лога — иначе число завышается в 5–10 раз, т.к. каждая сессия генерирует несколько событий.
  • Active connections почти всегда 0 — боты подключаются на доли секунды, и в момент опроса (раз в 10 сек) активных соединений обычно нет.
  • Bruteforce bans — защита порта 52222 (настоящий SSH), а не порта 22 (honeypot). На honeypot rate limiting не применяется намеренно.

Примечание по FreeBSD: стандартная утилита watch во FreeBSD — сетевой сниффер, не аналог GNU watch. Для периодического выполнения команд используется цикл while true или пакет cmdwatch (pkg install cmdwatch).

Управление tmux-сессиями

# Запустить/переключиться на мониторинг
honeypot-monitor

# Вернуться в рабочую сессию
Ctrl+b s              # список сессий, выбрать стрелками
Ctrl+b )              # следующая сессия

# Навигация внутри honeypot-сессии
Ctrl+b n              # следующее окно (logs → stats)
Ctrl+b p              # предыдущее окно
Ctrl+b ↑↓             # переключение между панелями в окне logs

# Отключиться от tmux (сессия продолжает работать)
Ctrl+b d

# Убить сессию мониторинга и пересоздать
tmux kill-session -t honeypot
honeypot-monitor

Анализ атак

Фильтрация событий

# Успешные входы (боты, которые «взломали» honeypot)
doas bastille cmd cowrie grep "login.success" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json

# Неудачные попытки входа
doas bastille cmd cowrie grep "login.failed" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json

# Команды, выполненные ботами после входа
doas bastille cmd cowrie grep "command" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json

# Файлы, которые боты пытались скачать
doas bastille cmd cowrie grep "session.file_download" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json

# Попытки использовать сервер как прокси (port forwarding)
doas bastille cmd cowrie grep "direct-tcp" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json

Анализ конкретной сессии

Каждая сессия имеет уникальный ID (поле session в JSON). Чтобы увидеть всё, что делал конкретный бот:

doas bastille cmd cowrie grep "SESSION_ID" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json

Типичная последовательность событий в сессии:

  1. cowrie.session.connect — подключение (IP, порт)
  2. cowrie.client.version — версия SSH-клиента (определяет инструмент)
  3. cowrie.client.kex — hassh-отпечаток (уникальная подпись клиента)
  4. cowrie.login.success/failed — попытка входа (логин, пароль)
  5. cowrie.command.input — команды в фейковом шелле
  6. cowrie.session.file_download — загрузка малвари
  7. cowrie.session.closed — отключение (длительность сессии)

Статистика

# Топ перебираемых логинов
doas bastille cmd cowrie grep "login" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json \
  | grep -o '"username":"[^"]*"' | sort | uniq -c | sort -rn | head -20

# Топ перебираемых паролей
doas bastille cmd cowrie grep "login" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json \
  | grep -o '"password":"[^"]*"' | sort | uniq -c | sort -rn | head -20

# Топ IP-адресов атакующих
doas bastille cmd cowrie grep "session.connect" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json \
  | grep -o '"src_ip":"[^"]*"' | sort | uniq -c | sort -rn | head -20

# Топ SSH-клиентов (определяет инструменты сканирования)
doas bastille cmd cowrie grep "client.version" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json \
  | grep -o '"version":"[^"]*"' | sort | uniq -c | sort -rn | head -20

# Топ hassh-отпечатков (группировка по инструменту)
doas bastille cmd cowrie grep "client.kex" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json \
  | grep -o '"hassh":"[^"]*"' | sort | uniq -c | sort -rn | head -20

# Количество подключений за сегодня
doas bastille cmd cowrie grep "$(date -u +%Y-%m-%d)" \
  /home/cowrie/cowrie/var/log/cowrie/cowrie.json \
  | grep -c "session.connect"

Наблюдённые типы атак

1. Распределённый брутфорс (ботнет 154.49.0.0/16)

Признаки: hassh 0a07365cc01fa9fc82608ba4019af499, SSH-клиент SSH-2.0-Go, логин administrator, ротация IP (один IP — одна попытка). Словарь включает русский транслит: ghbdtn (привет), fktrcfylh (александр), а также числовые пароли: 444444, 7654321, 147852369.

После успешного входа выполняет разведывательный скрипт за 0.8 секунды: uname -a, id, cat /etc/*release, uptime, ps aux, ss -lpunt, /proc/cpuinfo, free -h, cat /etc/passwd, ip addr, df -h. Собирает полный профиль сервера (CPU, RAM, сеть, пользователи) для оценки пригодности под майнинг или DDoS.

2. Прокси-сканер (204.76.203.83)

Признаки: hassh a289c065bf37397b69226bd770e33124, логин admin/admin. Не выполняет команд, вместо этого запрашивает direct-tcpip (SSH port forwarding) к ipv4.icanhazip.com:443 — проверяет, можно ли использовать сервер как прокси для анонимизации трафика. Предварительно делает TCP-скан (подключение на 0.0 сек), через 10–15 сек — реальная попытка. Возвращается каждые ~25 минут.

JA4-отпечаток TLS: t12d131000_f57a46bbacb6_27f0a1fdd87d — идентифицирует TLS-клиент бота, аналог hassh для TLS-соединений.

3. Минималистичная разведка платформы (183.94.33.160)

Признаки: hassh 98f63c4d9c87edbd97ed4747fa031019, пароли из «креативного» словаря. Выполняет одну команду uname -s -m — определяет ОС и архитектуру. Это первый этап многоэтапной атаки: сначала определить платформу, потом вернуться с подходящим бинарником малвари.

4. Перебор украденных SSH-ключей (139.19.117.197)

Признаки: разные hassh-отпечатки с одного IP (несколько инструментов). Пытается войти по SSH-ключу (fingerprint fb:45:99:e0:...), пароли не перебирает. Более целевая атака — у бота есть база украденных ключей.


Ключевые идентификаторы для анализа

hassh — отпечаток SSH-клиента

Формируется из набора алгоритмов шифрования, которые предлагает клиент. Позволяет идентифицировать конкретный инструмент независимо от IP-адреса.

hassh Инструмент/ботнет
0a07365cc01fa9fc82608ba4019af499 Ботнет 154.49.0.x (разведка)
a289c065bf37397b69226bd770e33124 Прокси-сканер
98f63c4d9c87edbd97ed4747fa031019 Минималистичный разведчик
f1e5e9d24e5e345e8745613bde22d532 Перебор SSH-ключей

JA4 — отпечаток TLS-клиента

Аналог hassh для TLS-соединений. Формируется из параметров TLS ClientHello (шифронаборы, расширения). Актуален для ботов, использующих port forwarding.

SSH-клиенты

Версия Что это
SSH-2.0-Go Сканер на Go (masscan, zgrab и подобные)
SSH-2.0-libssh2 Автоматизированный инструмент
SSH-2.0-paramiko Python-скрипт (часто ботнет)
SSH-2.0-PUTTY Может быть ручная атака

Пароли-транслит

Ботнеты с русскоязычным словарём используют пароли, набранные в английской раскладке:

Что вводят Что имели в виду
ghbdtn привет
fktrcfylh александр

Файрвол (pf)

Текущий конфиг: /etc/pf.conf

ext_if = "vtnet0"
jail_if = "bastille0"
ssh_port = "52222"
jail_net = "10.17.89.0/24"

table <bruteforce> persist

set skip on lo0

nat on $ext_if from $jail_net to any -> ($ext_if)
rdr on $ext_if proto tcp to port 22 -> 10.17.89.1 port 2222

block in all
pass out all keep state

block in quick on $ext_if from <bruteforce>

pass in on $ext_if proto tcp to port $ssh_port \
    keep state (max-src-conn-rate 5/30, overload <bruteforce> flush global)

pass in on $ext_if proto tcp to 10.17.89.1 port 2222

pass on $jail_if

Команды pf

# Перезагрузить правила
doas pfctl -f /etc/pf.conf

# Текущие правила фильтрации
doas pfctl -s rules

# NAT и redirect правила
doas pfctl -s nat

# Активные соединения
doas pfctl -s states

# Общая статистика (заблокировано/пропущено)
doas pfctl -s info

# Таблица заблокированных IP (bruteforce на порту 52222)
doas pfctl -t bruteforce -T show

# Количество заблокированных IP
doas pfctl -t bruteforce -T show | wc -l

# Разблокировать IP
doas pfctl -t bruteforce -T delete <ip-адрес>

Автоочистка таблицы bruteforce

В crontab (doas crontab -e):

0 4 * * * /sbin/pfctl -t bruteforce -T expire 86400

Удаляет записи старше 24 часов.


Конфигурация Cowrie

Принимаемые учётные данные: etc/userdb.txt

Путь внутри jail: /home/cowrie/cowrie/etc/userdb.txt

root:x:*
admin:x:*
administrator:x:*
user:x:*
test:x:*
guest:x:*
ubuntu:x:*
oracle:x:*
postgres:x:*

Формат: username:x:password, где * — любой пароль. После изменения требуется перезапуск Cowrie.

Основной конфиг: etc/cowrie.cfg

Путь внутри jail: /home/cowrie/cowrie/etc/cowrie.cfg

Ключевые параметры:

  • hostname — имя хоста, которое видят боты (установлено: svr01)
  • listen_endpoints — порт (tcp:2222:interface=0.0.0.0)
  • [output_jsonlog] — JSON-логирование (enabled = true)

Расположение файлов внутри jail

/home/cowrie/cowrie/
├── etc/
│   ├── cowrie.cfg          # основной конфиг
│   ├── cowrie.cfg.dist     # дефолтный конфиг (справка)
│   └── userdb.txt          # принимаемые логины/пароли
├── var/
│   ├── log/cowrie/
│   │   ├── cowrie.log      # текстовый лог
│   │   └── cowrie.json     # структурированный JSON-лог
│   ├── lib/cowrie/downloads/  # скачанная ботами малварь
│   ├── lib/cowrie/tty/        # записи терминальных сессий
│   └── run/cowrie.pid      # PID-файл
└── src/cowrie/scripts/
    └── cowrie.py            # скрипт запуска/остановки

Время в логах — UTC (захардкожено в Twisted). Для Европы (CET/CEST) сдвиг +1/+2 часа.


ZFS

# Структура датасетов
zfs list -r zroot/bastille

# Снапшот перед обновлениями
doas zfs snapshot zroot/bastille/jails/cowrie@pre-update

# Список снапшотов
zfs list -t snapshot

# Откат
doas zfs rollback zroot/bastille/jails/cowrie@pre-update

# Удалить старый снапшот
doas zfs destroy zroot/bastille/jails/cowrie@old-snapshot

# Проверить использование диска
zfs list -o name,used,avail,refer
df -h

Диагностика проблем

Cowrie не запускается

# Проверить лог ошибок
doas bastille cmd cowrie cat /home/cowrie/cowrie/var/log/cowrie/cowrie.log | tail -30

# Проверить, не занят ли порт
doas bastille cmd cowrie sockstat -l | grep 2222

# Проверить, жив ли процесс
doas bastille cmd cowrie ps aux | grep cowrie

# Убить зависший процесс
doas bastille cmd cowrie kill $(cat /home/cowrie/cowrie/var/run/cowrie.pid)

Боты не попадают в honeypot

# Проверить rdr-правило
doas pfctl -s nat | grep rdr

# Проверить, открыт ли порт 22 снаружи
doas pfctl -s rules | grep 22

# Проверить, слушает ли Cowrie
doas bastille cmd cowrie sockstat -l | grep 2222

# Проверить jail
doas bastille list

Потеря SSH-доступа

Если заблокировал себя в pf — подключись через VNC/IPMI-консоль хостера и исправь /etc/pf.conf, затем pfctl -f /etc/pf.conf.


Автозапуск Cowrie при перезагрузке

На данный момент Cowrie нужно запускать вручную после перезагрузки. Для автоматизации создай скрипт в хостовой системе:

# /usr/local/etc/rc.d/cowrie_start (на хосте, не в jail)
#!/bin/sh
# REQUIRE: bastille
# PROVIDE: cowrie_start

. /etc/rc.subr
name="cowrie_start"
start_cmd="cowrie_start"

cowrie_start() {
    bastille cmd cowrie su -m cowrie -c \
      "/usr/local/bin/python3.11 /home/cowrie/cowrie/src/cowrie/scripts/cowrie.py start"
}

load_rc_config $name
run_rc_command "$1"
doas chmod +x /usr/local/etc/rc.d/cowrie_start
doas sysrc cowrie_start_enable="YES"

Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.