- BrainTools - https://www.braintools.ru -
На моём тестовом наборе модель показывала 55 из 55 верных вердиктов и ни одной ложной эскалации. Потом я дал ей 100 реальных алертов с лабораторного стенда Wazuh. Она зря подняла на вторую линию 39 из них и к некоторым дописала инцидент, которого не было.
Этот разрыв определил всю дальнейшую работу. Ниже что я построил, какие цифры получил и чего эти цифры не доказывают. Код и веса открыты: GitHub [1], Hugging Face [2].
Первая линия SOC тонет в однотипных алертах, а облачные LLM часто нельзя использовать: логи не должны покидать периметр. Я хотел получить помощника, который работает на обычном ноутбуке с 8 ГБ видеопамяти, ничего не отправляет наружу и готовит для второй линии проверяемую карточку.
Название kezekshi по-казахски значит «дежурный». В основе дообученная Gemma 4 E2B под лицензией Apache 2.0.
На вход один алерт как пакет доказательств E1…En. На выход JSON: вердикт из четырёх классов, краткое описание, утверждения со ссылками на номера доказательств, техники MITRE ATT&CK, чего не хватает и что проверить дальше. Ссылки нужны, чтобы вывод модели можно было проверить кодом, а не верить ему на слово.
Трудные негативы полезнее новых типов атак. PowerShell -enc из агента SCCM, vssadmin create от бэкапа, скан Nessus по заявке, impossible travel через корпоративный VPN: поверхность выглядит как атака, но в пакете есть явный признак легитимности. Я добавил 14 таких кейсов, и набор негативов вырос с 8 до 22. Ложных эскалаций на нём осталось 0, а recall не просел.
Правило в промпте иногда дешевле дообучения. Рубрика тяжести поначалу уронила recall на незнакомых типах с 20/20 до 16/20: все четыре промаха это OAuth-согласие, прочитанное как штатное действие. Одно правило, что согласие приложению, выдача прав или новый метод MFA без подтверждения в пакете это минимум suspicious, вернуло 20/20 без видеокарты.
Режим размышления дообученной модели вредит. Рабочий режим без него.
Теневой прогон по реальным алертам стенда показал то, с чего я начал. На высокочастотных правилах, которых нет в синтетике (FIM реестра 751, 752, 750, rootcheck 510), модель завышала тяжесть и сочиняла нарратив.
Я сделал две вещи.
Первая: дообучил модель на 215 реальных benign-кейсах стенда. Замер на 100 отложенных алертах:
|
Версия |
Совпадение вердикта |
Лишние эскалации |
|---|---|---|
|
v4, только синтетика |
39/100 |
39/100 |
|
v5, плюс реальный шум |
96/100 |
0/100 |
Вторая: перестал звать модель там, где исход от неё не зависит. Инъекции в логах и операционный шум из белого списка решаются детерминированно ещё до модели, причём контекст всё равно эскалирует: изменение в автозапуске или защитной службе из белого списка выпадает. На 3000 алертах стенда 98,1 % решилось без модели, 1,9 % ушло в неё. Это примерно в 50 раз меньше вызовов при тех же решениях об авто-закрытии.
Памятка правил страхует и саму модель. Пример из тестовых алертов в репозитории: на попытке входа по SSH под несуществующей учёткой v6 ответила false_positive, памятка сказала suspicious, и алерт ушёл человеку.
Шестая версия выросла в обучении [3] до 3546 примеров и стала точнее на знакомых типах, но брак ответа вырос: на типах вне обучения 9 из 20 карточек не проходили проверку. Я посмотрел сырые ответы и увидел причину: в ссылке на доказательство модель писала имя поля ev1 вместо номера E1.
Вместо переобучения я стал собирать схему Ollama под каждый алерт. Разрешены только E-id этого алерта и кандидаты MITRE из локального индекса. Заодно нашлась ошибка [4] подбора: короткие триггеры искались как подстроки, и tor срабатывал на monitor.
|
Набор |
Показатель |
Было |
Стало |
|---|---|---|---|
|
Типы вне обучения (20) |
брак ответа |
9/20 |
0/20 |
|
Типы вне обучения (20) |
MITRE, полнота |
13/36 |
25/36 |
|
Типы вне обучения (20) |
MITRE, точность |
13/46 |
25/34 |
|
Типы вне обучения (20) |
атаки подняты в L2 |
20/20 |
20/20 |
|
Знакомые типы (76) |
брак ответа |
12/76 |
1/76 |
|
Знакомые типы (76) |
атаки подняты в L2 |
42/42 |
42/42 |
|
Знакомые типы (76) |
ложные эскалации |
0/34 |
1/34 |
|
Реальный шум (100) |
совпадение вердикта |
97/100 |
93/100 |
|
Реальный шум (100) |
ложные эскалации |
0/100 |
1/100 |
Плата видна в таблице: появились единичные лишние эскалации, а проверка ссылок стала формальной. Теперь номер всегда существует, но подходит ли он к утверждению, схема не гарантирует.
Самый неприятный результат этой серии. Когда в индексе появился кандидат T1555.004, более ранняя v5.1 начала писать про «массовый сбор учётных данных» о штатном чтении Credential Manager процессом Outlook. Список кандидатов работает как намёк на атаку.
Я добавил в промпт оговорку, что кандидаты подобраны по словам и атаку не означают. Recall на новых типах упал с 20/20 до 18/20: OAuth-согласие и IDOR закрылись как штатные. Оговорку я убрал. Для SOC лишняя эскалация дешевле пропущенной атаки, но это компромисс, а не решение.
Recall на реальных атаках не измерен. В реальном наборе только шум. 20/20 получено на пяти семействах атак, этого мало.
Часть кейсов почти совпадает с обучением. 30 из 76 знакомых и 63 из 100 реальных совпадают с точностью до цифр. Вне этих совпадений вердикт на прежней схеме был 44/46 и 34/37.
Реальный шум не проверяет хвост. Почти весь такой поток закрывает белый список, а строка про 100 алертов описывает модель без него.
Эталон реального шума это суждение одного аналитика, а не решения дежурной смены.
Поэтому контур работает в режиме assist: сам закрывает только детерминированный шум, всё, что дошло до модели, смотрит человек. Как разгрузка от шума и черновик карточки он полезен. Как автономный решатель эскалаций не проверен.
В репозитории [1] код контура поверх Wazuh (только чтение), схема ответа, индекс ATT&CK, скрипты обучения и оценки, руководство оператора. Веса v6 [2] лежат на Hugging Face: GGUF на 4,9 ГБ, который ставится в Ollama одной командой.
Датасета нет, и это осознанно: обучающий набор опирается на материалы учебного курса и на алерты закрытого стенда. Модель можно запустить на своих алертах, но метрики из статьи из открытых файлов не воспроизвести. Как собрать свой набор, описано в docs/dataset.md.
Перед публикацией весов я проверил, не выдаёт ли модель имена машин стенда. Во входах обучения они есть, в ответах нет, а ошибка считается только по ответу. За 306 попыток имена не появились, как и синтетическое имя, которое стояло в 144 ответах из обучения. Риск это снижает, но запоминание [5] полностью не исключает.
Следующий шаг это управляемые атаки на стенде и разбор их дежурным: только так появится настоящий recall контура. Скрипт для этого уже есть, eval/recall_eval.py.
Если вы работаете в SOC или MSSP, мне интересно другое: какая доля потока у вас реально решается памяткой и где такой контур сломается первым. Напишите в комментариях.
Автор: ipgleg
Источник [6]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/38125
URLs in this post:
[1] GitHub: https://github.com/oleg-vdv/kezekshi-l1
[2] Hugging Face: https://huggingface.co/oleg-vdv/kezekshi-l1
[3] обучении: http://www.braintools.ru/article/5125
[4] ошибка: http://www.braintools.ru/article/4192
[5] запоминание: http://www.braintools.ru/article/722
[6] Источник: https://habr.com/ru/articles/1104570/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1104570
Нажмите здесь для печати.