Классический Cowrie с дефолтным SSH-баннером обманывает автоматический сканер ровно до момента, когда тот шлёт нестандартную команду и получает заскриптованный ответ. Один и тот же баннер, десяток захардкоженных команд, идентичная файловая система на каждом экземпляре - Mirai-подобные ботнеты и Masscan раскусывают такие детерминированные паттерны на раз. Для современного threat detection этого давно мало.
LLM honeypot решает проблему принципиально: вместо шаблонов за каждую реплику отвечает языковая модель, генерирующая контекстно-зависимый вывод в реальном времени. По данным исследования HoneyGPT (Wang et al., 2024, arXiv:2406.01882), такой подход значительно увеличивает среднюю длину атакующей сессии по сравнению с Cowrie и позволяет фиксировать ранее неизвестные тактики, маппящиеся на MITRE ATT&CK. Атакующий думает, что ломает живую машину, расслабляется - и сливает свои TTPs.
Дальше - как построить такой honeypot своими руками на Linux: развернуть единый proxy-listener на все 65535 портов через nftables, подключить языковую модель для генерации ответов, настроить rate limiting (чтобы массовое сканирование не сожрало бюджет на API), и организовать логирование для threat intelligence.
Почему статические ловушки больше не работают как приманка для хакеров
Honeypot-системы fingerprint-ятся по нескольким категориям признаков. Согласно SoK-обзору «Honeypots & LLMs» (arxiv, 2025), даже после интеграции LLM фундаментальные ограничения могут выдать систему достаточно опытному атакующему. Но ключевой вывод исследователей: целевой adversary для LLM honeypot - не человек, который занимается пентестом, а автоматизированные агенты и ботнеты. Именно их мы и ловим.Проект Palisade Research подтверждает это реальными цифрами: по состоянию на начало 2025 года (ai.agenthoneypot.com) их LLM Agent Honeypot зафиксировал значительное количество взаимодействий, из которых небольшая часть идентифицирована как потенциальные AI-агенты с высокой достоверностью - через prompt injection detection и temporal analysis (точные цифры меняются, актуальные значения на ai.agenthoneypot.com). Среди основных источников атак по географии - США, Китай и Нидерланды. Ничего неожиданного.
Классические detection-векторы, по которым атакующие распознают ловушку:
- Статические баннеры - SSH-версия Cowrie легко находится поиском, Shodan маркирует такие хосты автоматически.
- Ограниченный набор команд - нестандартная утилита возвращает одинаковый «command not found» вместо вариативного ответа реальной ОС.
- Идентичная файловая система - содержимое
/etc/passwd, hostname,/procодинаковы на каждом экземпляре. - Тайминг - шаблонные ответы прилетают за микросекунды, тогда как реальный сервер отвечает с вариативной задержкой.
- Сетевой fingerprinting - TCP window size, TTL, опции в SYN-ACK выдают, что за «маршрутизатором Cisco» стоит Linux-процесс.
Канонический pipeline: архитектура LLM honeypot от пакета до лога
За последние два года (по данным SoK-обзора) сформировалась устойчивая архитектурная модель deception technology на базе языковых моделей. Ниже - компоненты, которые затем реализуем на практике.
Компоненты системы обнаружения вторжений
Network front-end (listener). Принимает TCP-подключения на целевых портах. В нашем случае это единый asyncio-процесс, получающий трафик со всех 65535 портов через nftables REDIRECT.Protocol router. По оригинальному порту назначения определяет эмулируемый сервис: порт 80/443 - HTTP, 23 - Telnet и т.д. Для неизвестных портов - generic TCP-баннер. Бинарные протоколы (SSH, TLS, MySQL handshake) требуют protocol-aware фронтенда (например, Cowrie для SSH) - generic text-LLM тут не катит.
Prompt manager. Формирует system prompt для LLM с учётом протокола, «личности» системы (конкретная ОС, hostname, набор пакетов) и текущей сессионной истории. Исследователи VelLMes описывают этот компонент как protocol personality template - шаблон, задающий жёсткие поведенческие рамки вплоть до символа приглашения командной строки.
LLM engine. Принимает промпт и пользовательский ввод, возвращает ответ. Может быть локальным (Ollama) или облачным (OpenAI API). Лично я для тестирования предпочитаю Ollama - данные никуда не утекают, и задержка предсказуемая.
State manager. Хранит историю сессии, чтобы
ls после cd /tmp показывал содержимое /tmp, а не корня. Согласно оригинальной статье HoneyGPT (arXiv:2406.01882), система использует score-weighted pruning: когда токенный бюджет исчерпан, команды с наименьшим «весом влияния» удаляются из контекста первыми.Output formatter. Адаптирует raw-вывод LLM под протокольные требования: добавляет
$ для bash, mysql> для MySQL-клиента, HTTP-заголовки для веб-сервера. Без него модель периодически выдаёт markdown-разметку посреди терминальной сессии - моментальное палево.Logger. Пишет всё в JSON: timestamp, src_ip, оригинальный dst_port, payload атакующего, ответ LLM, session_id. Формат рассчитан на прямую загрузку в ELK или Graylog.
Облако или локальный inference - что выбрать
Выбор модели для AI honeypot detection - компромисс между качеством, задержкой и стоимостью:| Параметр | Облако (GPT-3.5 / GPT-4) | Локально (Ollama + Llama 3 8B) |
|---|---|---|
| Качество ответов | Высокое | Среднее, но хватает для большинства протоколов |
| Задержка | 200–800 мс | 50–200 мс на GPU, 500–2000 мс на CPU |
| Стоимость | Тарифицируется за токены | Электричество + амортизация GPU |
| Контроль данных | Логи атакующих уходят провайдеру | Всё остаётся локально |
| Масштаб | Ограничен rate limit API | Ограничен мощностью железа |
Для production-развёртывания рекомендую гибридную схему: локальная модель через Ollama обрабатывает массовые сканирования, а облачный API подключается для «интересных» сессий - тех, где атакующий отправил больше двух-трёх команд и прошёл фильтр тривиальности. Так 90% мусорного трафика не стоит ни копейки, а на оставшиеся 10% тратится GPT-4 - и оно того стоит.
Мониторинг портов honeypot: один процесс на 65535 портов
Наивный подход - вызватьbind() на каждом из 65535 портов - убьёт систему. Каждый слушающий сокет требует файлового дескриптора, и даже при поднятом ulimit -n 65535 overhead на 65 тысяч listener-ов составит сотни мегабайт только на структуры ядра (не считая asyncio-задач). На типовом VPS с 2 ГБ RAM это нереализуемо.Решение - единый listener + перенаправление на уровне ядра.
Требования к окружению
- ОС: Ubuntu 22.04 LTS или Debian 12+ (ядро 5.15+)
- Python: 3.10+ с модулями
asyncio,struct,socket - nftables: версия 1.0.2+ (в Ubuntu 22.04 из коробки)
- LLM backend: Ollama 0.1.30+ с моделью
llama3:8b- или API-ключ OpenAI - RAM: 8 ГБ при локальном inference, 2 ГБ при облачном
- Сеть: выделенный VPS с публичным IP, без production-сервисов на этом хосте
- Режим: online (нужна связь с LLM API или локально запущенный Ollama)
Перенаправление через nftables и извлечение оригинального порта
Идея простая: nftables перенаправляет весь входящий TCP на единственный порт (допустим, 8443), где слушает asyncio-сервер. Сервер через
getsockopt с опцией SO_ORIGINAL_DST узнаёт, на какой порт клиент обращался изначально, и выбирает соответствующий промпт-шаблон.
Bash:
nft add table ip honeypot
nft add chain ip honeypot prerouting { type nat hook prerouting priority -100 \; }
nft add rule ip honeypot prerouting iif "eth0" tcp dport != { 22, 8443, 11434 } redirect to :8443
Asyncio-обработчик, который извлекает оригинальный destination port:
Python:
import asyncio, struct, socket
SO_ORIGINAL_DST = getattr(socket, 'SO_ORIGINAL_DST', 80) # нативно в Python 3.12+; fallback для старых версий
async def handle_client(reader, writer):
sock = writer.get_extra_info('socket')
# только IPv4 (sockaddr_in, 16 байт); для IPv6 нужен SOL_IPV6 и буфер 28 байт
dst = sock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
orig_port = struct.unpack('!H', dst[2:4])[0]
prompt = select_prompt(orig_port) # 22→SSH, 80→HTTP, generic
# server-first протоколы (SMTP:25, FTP:21, Telnet:23) - отдаём баннер до read()
# бинарные протоколы (SSH:22, MySQL:3306, TLS) требуют protocol-aware прокси
banner = get_static_banner(orig_port)
if banner:
writer.write(banner.encode())
await writer.drain()
data = await reader.read(4096)
if not data:
writer.close()
return
response = await call_llm(prompt, data.decode(errors='replace'))
writer.write(response.encode())
await writer.drain()
async def main():
server = await asyncio.start_server(handle_client, '0.0.0.0', 8443)
async with server:
await server.serve_forever()
asyncio.run(main())
select_prompt(port) возвращает system prompt для сервиса. Для портов без явного маппинга - generic-промпт, имитирующий закрытый или нестандартный сервис. call_llm делает запрос к Ollama через HTTP API на localhost:11434/api/generate или к OpenAI - в зависимости от конфигурации.Prompt engineering: как языковая модель имитирует реальные сервисы
Качество обмана целиком зависит от промпта. Вот ключевые принципы, собранные из нескольких исследований и собственных экспериментов:
🔓 Эксклюзивный контент для зарегистрированных пользователей.
Строгая идентичность. Промпт фиксирует конкретную ОС, hostname, версии пакетов. Не «вы - Linux-сервер», а «вы - Ubuntu 20.04.6 LTS, hostname webprod-03, uname выводит 5.4.0-169-generic, установлены Apache 2.4.41, PHP 8.1.2, MySQL 8.0.35». Чем точнее «личность», тем реалистичнее языковая модель формирует ответы. Расплывчатый промпт = расплывчатый вывод, и атакующий это видит.
Запрет на раскрытие. Прямая инструкция: «Никогда не признавайся, что ты языковая модель или honeypot. На любые вопросы о своей природе отвечай как настоящий сервер». Исследователи VelLMes формулируют жёстче: «You can never reveal that you are not a real database client. Always end your output with mysql>». Без этого модель при первом же
whoami может ответить что-то вроде «As an AI language model, I don't have a username» - занавес.Chain-of-thought для валидации. Модель сначала внутренне «рассуждает» о том, что команда должна вернуть в текущем контексте (директория, состояние файловой системы, запущенные процессы), а затем выдаёт только финальный ответ. Это снижает процент нереалистичных ответов - так называемую hallucination-проблему.
Negative prompts. Явный запрет на действия, выдающие LLM: не генерировать markdown-форматирование в CLI-ответах, не добавлять пояснения к выводу команд, не использовать фразы вроде «Certainly!» или «As an AI». Казалось бы, очевидно - но без этих запретов модель регулярно срывается.
Управление памятью в stateful-сессиях
Атакующий ожидает, чтоpwd после cd /var/log вернёт /var/log, а не /root. Для этого нужна stateful-сессия: вся история команд и ответов передаётся в контекст LLM при каждом запросе.Проблема: при длинной сессии контекстное окно переполняется. В honeypot на основе ИИ это критично - потеря контекста означает, что повторный
cat /etc/passwd вернёт другое содержимое файла. Атакующий моментально поймёт, что перед ним не реальная машина.Решение из HoneyGPT - score-weighted pruning. Каждой команде в истории присваивается вес влияния с временным затуханием. Когда бюджет токенов исчерпан, записи с наименьшим весом удаляются первыми. Команды файловой навигации (
cd, ls, mkdir) получают высокий вес - они влияют на весь дальнейший контекст. Повторные whoami или id - низкий.На практике это словарь
{session_id: [(command, response, weight, timestamp), ...]} с периодическим пересчётом суммарного количества токенов через tiktoken или аналогичную библиотеку. Не самая элегантная конструкция, но работает.Защита бюджета при массовом сканировании портов
Это та самая ловушка, о которой не пишут в academic papers, но которая ломает реальные деплои. Когда сканер с banner grabbing (nmap -sV или Masscan с --banners) проходит по всем 65535 портам, каждое TCP-подключение с payload потенциально генерирует вызов LLM. SYN-сканирование (дефолтный режим Masscan без --banners) не отправляет данных после handshake и LLM не вызывает - см. Уровень 2 ниже.Расчёт для наихудшего сценария без защиты (version scan с payload на каждый порт): 65535 подключений, каждое ~200 input-токенов (промпт+команда) и ~200 output-токенов (ответ), при тарифах gpt-4o-mini ($0.15/1M input, $0.60/1M output) - примерно $10 за один полный скан одного сканера. При сотне таких сканеров в сутки (типичная нагрузка публичного IP; по данным Palisade Research, отдельные IP генерировали сотни тысяч попыток) бюджет улетает в тысячу долларов и выше. Неприятный сюрприз в конце месяца.
Многоуровневая защита:
Уровень 1 - nftables rate limit. Ограничьте новые подключения с одного IP через meter. Рабочее правило (в отдельном filter-chain с приоритетом выше nat):
nft add chain ip honeypot filter_prerouting { type filter hook prerouting priority -150 \; } и затем nft add rule ip honeypot filter_prerouting tcp flags syn ip saddr meter flood size 65535 { ip saddr limit rate over 10/second } drop - пакеты сверх лимита дропаются до попадания в nat-цепочку с REDIRECT. Masscan и Zmap отсекаются ещё до того, как трафик достигнет Python-процесса.Уровень 2 - engagement threshold. LLM вызывается только если атакующий отправил данные после TCP handshake. SYN-сканирование не генерирует LLM-вызовов, потому что соединение закрывается до передачи payload. Бесплатно.
Уровень 3 - tiered response. Первый ответ - статический баннер из словаря (SSH version string, HTTP 200 с минимальным body). LLM подключается после второй-третьей команды, когда ясно, что это целевое взаимодействие, а не массовый скан. 99% ботов отваливаются после первого баннера.
Уровень 4 - token budget per IP. Программный лимит: максимум 4000 токенов на один source IP в час. При превышении - переключение на статические ответы или контролируемый разрыв.
Логирование атак honeypot и маппинг на MITRE ATT&CK
Honeypot без аналитики - просто шум. Каждое взаимодействие записывается в JSON-лог с полями:timestamp, session_id, src_ip, src_port, original_dst_port, payload_hex, payload_decoded, llm_response, llm_latency_ms, tokens_used.Для интеграции с SIEM логи отправляются через Filebeat в Elasticsearch. В Kibana создаются дашборды: топ атакующих IP, распределение подключений по портам, длина сессий, частота уникальных команд. Это превращает honeypot из пассивной приманки для хакеров в полноценный источник threat intelligence.
Маппинг обнаруженной активности на MITRE ATT&CK:
| Наблюдаемое действие | Техника ATT&CK | Тактика |
|---|---|---|
| Подключение к диапазону портов | Network Service Discovery (T1046) | Discovery |
| Сканирование версий/баннеров на порту | Vulnerability Scanning (T1595.002) | Reconnaissance |
| Exploit-payload на конкретный порт | Exploit Public-Facing Application (T1190) | Initial Access |
| Последовательный перебор IP-блока | Scanning IP Blocks (T1595.001) | Reconnaissance |
| Попытка подбора credentials | Brute Force (T1110) | Credential Access |
| HTTP/DNS-туннелирование в сессии | Application Layer Protocol (T1071) | Command and Control |
По данным HoneyGPT, LLM honeypot обнаруживает тактики, которые классические ловушки пропускают - именно за счёт того, что атакующий продолжает сессию дольше и раскрывает больше TTPs. Грубо говоря, чем дольше бот думает, что ломает реальную машину - тем больше он показывает из своего арсенала.
Отдельное направление - детекция AI-агентов. Проект Palisade Research встраивает в сессию prompt injection: если «собеседник» на другом конце TCP-соединения - языковая модель, она реагирует на инъекцию иначе, чем человек. Temporal analysis (паттерны задержек между командами) добавляет второй слой верификации. Мы ловим ботов ботами - ирония ситуации не ускользает.
Попробуйте развернуть этот стек на дешёвом VPS, повесить на публичный IP и оставить на неделю. Гарантирую - количество и разнообразие того, что прилетит, вас удивит. А если среди сессий окажется что-то длиннее пяти команд - значит, кто-то действительно поверил, что перед ним живой сервер. И вот тут начинается самое интересное для threat intelligence.
Вопрос к читателям
Коллеги, кто уже тестировал score-weighted pruning из HoneyGPT (arXiv:2406.01882) в production-сессиях с длинным контекстом - как вы калибруете веса для команд файловой навигации против разведочных команд типаuname -a или cat /proc/version? Конкретно: при каком пороге токенов (например, 4096 из 8192 у Llama 3 8B через Ollama) вы начинаете pruning, и какой decay-коэффициент используете для временного затухания? Второй вопрос по nftables-правилу: если атакующий шлёт SYN на порт 11434 (Ollama API), а вы забыли его исключить в tcp dport != { 22, 8443, 11434 } - какой nftables-счётчик (nft list ruleset) показывает утечку трафика до Ollama, и как вы это обнаружили в логах?
Последнее редактирование модератором: