Полгода назад мы убили две недели на подбор гиперпараметров LightGBM для детекта C2-бэконов в корпоративном трафике. F1 болтался между 0.71 и 0.74 - хоть learning rate крути, хоть max_depth, хоть число деревьев. Потом за один вечер добавили три фичи: стандартное отклонение межпакетных интервалов, энтропию размеров payload и соотношение исходящих к входящим байтам на уровне flow. F1 прыгнул до 0.93 на том же тестовом сете. Алгоритм тот же, данные те же - изменилось то, что модель «видит» в трафике. В feature engineering сетевого трафика для машинного обучения качество признаков определяет потолок модели, а алгоритм лишь приближает к этому потолку.
Feature Engineering - процесс преобразования исходных данных в формат, который лучше подходит для обучения моделей машинного обучения.
Алгоритм вторичен: что говорят цифры
Типичная ошибка при построении ML-моделей обнаружения вторжений - вкладывать 80% времени в архитектуру модели и 20% в подготовку данных. Пропорция должна быть обратной.Три алгоритма: Random Forest, LightGBM, простая полносвязная нейросеть. Подаём сырые flow-фичи из Zeek conn.log - duration, orig_bytes, resp_bytes, orig_pkts, resp_pkts, proto. Разброс F1 по классу «malicious» укладывается в 2–4 процентных пункта. Добавляем производные признаки - статистику по временным окнам, энтропию, ratio-фичи - и каждый из алгоритмов показывает прирост 15–25% F1. Разница между самими алгоритмами на обогащённых фичах остаётся в тех же 2–3%.
Random Forest - это алгоритм машинного обучения, который объединяет множество независимых «деревьев решений» в один ансамбль.
LightGBM - это популярная библиотека алгоритмов градиентного бустинга над деревьями решений, разработанная Microsoft.
Причина простая: алгоритм находит разделяющие гиперплоскости в том признаковом пространстве, которое ему дали. Но он не умеет придумывать новые оси этого пространства. Если в данных нет фичи «стандартное отклонение IAT за 60 секунд», ни одна модель не обнаружит jittered beacon по его сигнатуре в межпакетных интервалах - ей просто негде увидеть этот паттерн.
По данным IBM X-Force Threat Intelligence Index 2025, 70% инцидентов, расследованных X-Force в 2024 году, затронули критическую инфраструктуру. Сигнатурный IDS на базе Suricata или Snort ловит известные паттерны, но не адаптируется к новым TTPs. ML-based NIDS теоретически решает эту проблему - при условии, что фичи отражают реальное поведение трафика, а не шум в красивой обёртке.
Три уровня признаков сетевого трафика
Извлечение признаков из сетевого трафика для машинного обучения работает на трёх уровнях. Каждый добавляет контекст и увеличивает детективную способность, но требует больше инженерной работы.Пакетный уровень: атомарные характеристики
Самый низкий уровень - отдельный пакет. Из него вытягиваются: размер пакета (total_length из IP-заголовка), TTL, TCP-флаги (SYN, ACK, FIN, RST, PSH, URG), протокол транспортного уровня, порты источника и назначения, размер payload.В изоляции пакетный уровень почти бесполезен для классификации. Один SYN-пакет на порт 443 неотличим от начала легитимного TLS-хэндшейка и от первого пакета SYN-скана. Контекст появляется только при агрегации.
Но пакетные фичи - фундамент. Если на этом уровне вы неправильно парсите флаги через Scapy или теряете retransmissions, все производные признаки будут загрязнены. Wireshark тут - ваш лучший друг для визуальной верификации того, что парсер выдаёт на конкретной сессии.
Flow-уровень: агрегация потоков
Flow - группа пакетов с общим 5-tuple (src_ip, dst_ip, src_port, dst_port, proto). Здесь начинается реальная работа с признаками для IDS.| Инструмент | Плюсы | Ограничения | Когда использовать |
|---|---|---|---|
| CICFlowMeter | 80+ готовых фичей, GUI | Java-зависимость, OOM на pcap >1 ГБ, слабая расширяемость | Быстрый прототип, работа с CIC-IDS2017 |
| Zeek conn.log | Стабильный, масштабируется, интеграция с SIEM | Базовый набор flow-фичей, нужна дообработка в pandas | Продакшен-пайплайн |
| Scapy + Python | Полный контроль над каждым битом | Медленно на больших pcap (>500 МБ), весь код - ручной | Исследование кастомных фичей |
Zeek conn.log выдаёт по каждому flow: ts, uid, id.orig_h/p, id.resp_h/p, proto, service, duration, orig_bytes, resp_bytes, conn_state, orig_pkts, resp_pkts. CICFlowMeter генерирует более 80 статистических фичей: средний, минимальный, максимальный размер пакета в прямом и обратном направлении, стандартное отклонение, полный набор IAT-статистик.
Критический параметр на этом уровне - таймаут flow. Zeek определяет границы TCP-сессий по FIN/RST, для UDP использует таймаут неактивности. CICFlowMeter по умолчанию ставит 120 секунд. Эта настройка напрямую бьёт по фичам: слишком длинный таймаут склеит два разных сеанса, слишком короткий - разорвёт один. Для обнаружения C2-бэконов с интервалом 300 секунд дефолтный таймаут CICFlowMeter уже не подходит - и это ловушка, на которую наступают многие.
Поведенческий уровень: окна и энтропия
Самый ценный и самый сложный уровень. Здесь агрегируется не один flow, а поведение группы flow за временное окно - обычно 30–120 секунд.Ключевые поведенческие фичи для обнаружения аномалий в сетевом трафике:
- Количество уникальных dst_port от одного src_ip за 60 секунд. Значение >20 - горизонтальный скан. Нормальный пользователь за минуту обращается к 2–5 портам.
- Энтропия dst_port по Шеннону. Если src_ip ходит на 200 разных портов за окно, энтропия будет выше 7 бит. У легитимного клиента - ниже 1.5 бит.
- Ratio SYN-only к total TCP-соединениям за окно. SYN-flood или SYN-scan дают ratio > 0.8; нормальный трафик - 0.05–0.15.
- Стандартное отклонение IAT (inter-arrival time). C2-бэкон с jitter показывает характерный паттерн: малое std на фоне случайного шума нормального трафика. Стабильный IAT (std < 0.5 с) при длинной сессии - красный флаг.
- Энтропия payload на уровне байтов. Выше 7.5 бит на байт при малом размере пакета - вероятный признак шифрованного или обфусцированного канала.
- Byte ratio (sent/received) на уровне flow. C2 с командами «poll» показывает резко асимметричный паттерн: много маленьких исходящих, редкие крупные входящие.
Какие фичи ловят какие TTPs: маппинг на MITRE ATT&CK
Проблема ML-based IDS - объяснимость. Модель говорит «аномалия», но а что именно аномально? Feature engineering с привязкой к конкретным TTPs снимает этот вопрос: каждая фича отражает конкретное поведение атакующего.
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Практический пайплайн: от pcap до обученной модели
Требования к окружению
- ОС: Ubuntu 22.04+ или Kali Linux 2024+
- RAM: минимум 8 ГБ для pcap до 500 МБ; рекомендуется 16 ГБ для крупных датасетов (CIC-IDS2017 в pcap-формате занимает ~50 ГБ)
- Zeek: 6.0+ (
sudo apt install zeekили сборка из исходников) - Python: 3.10+, зависимости:
pandas,numpy,scikit-learn,lightgbm,shap - Режим: полностью offline после установки зависимостей; интернет не требуется
Шаги пайплайна
1. Генерация conn.log. Командаzeek -r capture.pcap создаст структурированные логи в текущей директории. Каждая строка conn.log - один flow с полями ts, uid, id.orig_h, id.orig_p, id.resp_h, id.resp_p, proto, service, duration, orig_bytes, resp_bytes и десятком дополнительных.2. Парсинг и базовые производные фичи.
Python:
import pandas as pd
df = pd.read_csv('conn.log', sep='\t', comment='#',
names=['ts','uid','src_ip','src_port','dst_ip','dst_port',
'proto','service','duration','orig_bytes','resp_bytes',
'conn_state','missed_bytes','history','orig_pkts',
'resp_pkts','orig_ip_bytes','resp_ip_bytes'])
df['byte_ratio'] = df['orig_bytes'] / (df['resp_bytes'] + 1)
df['pkt_ratio'] = df['orig_pkts'] / (df['resp_pkts'] + 1)
df['bytes_per_pkt_fwd'] = df['orig_bytes'] / (df['orig_pkts'] + 1)
+ 1 в знаменателях - не лень, а необходимость. Без этого деление на ноль при однонаправленных flow (типичная ситуация для SYN-scan, где resp_bytes = 0 и resp_pkts = 0) заполнит датасет NaN-ами и убьёт обучение.3. Поведенческие фичи через временные окна. Сортируем по timestamp и для каждого flow считаем: сколько уникальных dst_port от этого src_ip за последние 60 секунд. На больших датасетах наивный
groupby + rolling работает часами. Эффективнее: pd.merge_asof или предварительная агрегация в бакеты по 60 секунд с подсчётом nunique. Энтропия dst_port вычисляется функцией Шеннона: H = -sum(p * log2(p)) по распределению портов в окне.4. Обучение и проверка через SHAP.
Python:
import shap
import lightgbm as lgb
model = lgb.LGBMClassifier(n_estimators=200, max_depth=6)
model.fit(X_train, y_train)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values[1], X_test, max_display=15)
Ловушки feature engineering в сетевом трафике
Утечка через IP-адреса
Если оставить src_ip и dst_ip как категориальные фичи (или закодировать через one-hot), модель запомнит: «трафик с 192.168.1.105 = атака». На обучающем датасете - F1 под 0.99. На новом трафике - 0.50. Модель выучила адреса лабораторных машин атакующего, а не поведение.Я видел эту ошибку в трёх разных проектах, и каждый раз команда удивлялась, почему «идеальная» модель разваливается в продакшене. Решение: никогда не включать IP-адреса в фичи напрямую. Используйте поведенческие агрегаты по IP - количество уникальных dst_port, среднюю длительность сессий, энтропию обращений.
Временная утечка
При вычислении оконных фичей ошибка в сортировке по timestamp или использование двунаправленного окна (захват будущих событий) создаёт ситуацию: модель видит «в ближайшие 60 секунд после этого flow произойдёт ещё 50 SYN». Она предсказывает атаку с подозрительной точностью, но в продакшене будущего нет - и модель разваливается.Проверка: обучите модель на данных до определённого timestamp, протестируйте на данных после. Если F1 падает на 15+ процентных пунктов по сравнению с random split - утечка есть.
Ловушка CICFlowMeter
CICFlowMeter генерирует 80+ фичей, и соблазн «кинуть всё в модель» понятен. Проблема: многие из них - линейные комбинации друг друга. total_fwd_packets умноженный на avg_fwd_packet_length приблизительно равен total_length_fwd_packets. Мультиколлинеарность не убивает деревья напрямую, но маскирует реальный вклад каждой фичи. SHAP показывает 15 «важных» признаков, из которых 10 - одно и то же, вид сбоку. До обучения считайте корреляционную матрицу и отбрасывайте фичи с попарной корреляцией > 0.95. Это скучно, но без этого интерпретация модели - фикция.Слепые зоны ML-фичей: что атакующий обходит в feature-based IDS
Feature engineering - инструмент с чёткими границами. Атакующий, который понимает устройство детекции, целенаправленно обходит конкретные фичи. Для пентестера знание этих механизмов - операционный контекст, определяющий OPSEC на внутреннем пентесте.Mimicry-атаки. Если IDS опирается на byte_ratio, атакующий добавляет padding в C2-ответы, выравнивая соотношение до нормального (~1:1). SHAP-профиль такого трафика становится неотличим от легитимного HTTPS. На внутреннем пентесте это делается настройкой junk-данных в профиле C2-фреймворка - Cobalt Strike malleable profiles или Sliver HTTP C2 config.
Медленное сканирование. Фича «количество уникальных dst_port за 60с» не сработает, если атакующий проверяет один порт в минуту. Расширение окна до 1 часа поможет - но создаст вычислительную нагрузку и задержку детекции, неприемлемую для real-time IDS. Классический trade-off, и идеального решения нет.
Туннелирование через легитимные протоколы. DNS-туннель с энтропией subdomain < 3 бит (короткие словарные имена вместо base64) не будет пойман фичей «энтропия subdomain > 4.5». Protocol Tunneling (T1572, C2) в таком исполнении требует не статистических, а семантических фичей - анализа содержимого DNS-запросов, что выходит за рамки классического feature engineering.
Jittered beaconing. Beacon с jitter > 30% размывает стандартное отклонение IAT до уровня нормального трафика. Детект требует фичей второго порядка: автокорреляция IAT-ряда ловит скрытую периодичность даже при значительном шуме. На практике вычисление автокорреляции для каждого src_ip в реальном времени - инженерно тяжёлая задача, и мало кто её тянет.
Traffic Signaling (T1205, C2). Атакующий использует специфические комбинации TCP-флагов или значения TTL как сигналы для активации backdoor. Статистические фичи не детектят единичные пакеты-триггеры - они «растворяются» в окне агрегации. Тут нужен другой подход - rule-based детект на уровне отдельных пакетов, а не ML.
Понимание этих ограничений критично с обеих сторон: blue team видит, где нужно усилить детекцию; пентестер понимает, какие фичи IDS отслеживает и как выстроить коммуникацию с C2, не триггерящую пороговые значения. На внутреннем пентесте в modern-инфраструктуре с ML-based IDS (Darktrace, Vectra AI) знание конкретных фичей - разница между пойманным beacon и незамеченным каналом.
Заключение
Три года я строю пайплайны обнаружения аномалий в сетевом трафике, и главный вывод неутешителен: индустрия переинвестирует в алгоритмы и недоинвестирует в фичи. На конференциях обсуждают Transformer-based IDS и graph neural networks для обнаружения lateral movement. При этом базовый набор фичей у большинства команд - сырой conn.log из Zeek без единого производного признака. Менять Random Forest на Transformer при плохих фичах - это ставить спортивный выхлоп на машину с пробитым колесом.Прирост от правильно сконструированных признаков на порядок больше прироста от смены алгоритма, и этот эффект воспроизводится на каждом датасете, с которым я работал. Второй неудобный факт: feature engineering для ML-моделей обнаружения вторжений нельзя полностью автоматизировать. AutoML подбирает pipeline, но не понимает, почему высокая энтропия dst_port за 60 секунд - это сканирование, а не CDN-балансировка. Доменная экспертиза - понимание, что стоит за числом в реальном трафике - остаётся ручной работой, и именно она разделяет IDS с precision 0.70 и IDS с precision 0.95.
Если хочется на практике разобрать pcap и понять, какой трафик аномальный, без готовых правил и подсказок - на HackerLab.pro в сетевых задачах построены как раз такие сценарии.
Последнее редактирование модератором: