Чем хорош eXtraction and Processing
Начнем с терминологии. eXtraction and Processing (XP) — созданный в Positive Technologies язык разработки правил нормализации, корреляции и обогащения. Они используются в MaxPatrol SIEM, PT XDR и SOLDR для обнаружения атак на основе анализа потока событий с конечных точек. VS Code — это IDE (integrated development environment) для разработки программ на классических языках: JavaScript, C#, Python и др. Инструмент позволяет добавить поддержку произвольного языка с помощью языковых расширений. Реализовав расширение VSCode XP, мы получили возможность использовать VS Code для разработки контента на XP.
Почему именно eXtraction and Processing:
- Запрос со стороны сообщества на экспертную открытость вокруг языка.
- Простой текстовый формат и подробная документация.
- Наличие опыта и знаний, которыми мы готовы делиться.
- Позволяет описать сложную логику обнаружения.
- Публичные инструменты, позволяющие вести разработку контента даже без соответствующих ИБ-продуктов.
- Базовые правила нормализаций для Windows и Linux (auditd) и общедоступные правила, которые будут пополняться.
Подчеркнем, что XP можно использовать, даже если у вас нет соответствующих ИБ-решений. Прежде всего это универсальный способ описания экспертизы по обнаружению атак. А если хотите проверить, сработает ли правило на определенные события, достаточно вручную извлечь их из журналов (например, Windows Event Log), скопировать в расширение, запустить корреляцию и получить набор сработавших правил.
Начнем с нормализации событий в XP.
В левом столбце на рис. 1 показана запись в журнале Windows в формате XML (17 Sysmon). Справа — нормализованное событие в формате нашей таксономии: все поля унифицированы, приведены к единому стандарту.
Унификация — это всегда полезно. Она позволяет осуществлять поиск событий без привязки к формату журналов конкретного вендора или продукта. Например, по фильтру subject.account.name = “pushkin” and action = “login” можно найти все приложения, к которым осуществлял доступ данный пользователь.
Поля разделены на группы. Например, event_src — группа, описывающая источник события. В ней есть event_src.host — имя хоста, event_src.subsys — источник события (журнал или подсистема), event_src.vendor — вендор источника событий. Ниже находятся поля группы subject.account, содержащие информацию о пользователе. Еще один важный блок — subject.process, включающий идентификатор процесса, его имя и директорию, откуда он был запущен.
Рассмотрим пример простейшей корреляции для выявления манипуляции задачами в Windows. На рис. 2 инструкция event определяет событие, которое нужно отфильтровать из потока. Вложенная инструкция filter определяет условие, по которому будут отбираться события, а key показывает, как будут разделяться различные потоки, подходящие под фильтр и имеющие разный набор значений полей, указанных в key.
Кроме того, здесь можно использовать макросы. Они напоминают функции в классических языках программирования, определяются ключевым словом filter и хранятся в отдельных файлах. В них выносят повторяющиеся в фильтрах различных корреляций условия для повторного использования, что позволяет уменьшить дублирование кода фильтра и упростить его. Например, макрос NotFromCorrelator определяет, что среди событий будут отобраны только нормализованные, а корреляционные — пропущены. ProcessStart_Windows_any() показывает, что нам интересны именно события запуска процессов в Windows, события с Event ID: 1 (sysmon), 4688, 592. Также в макросы можно передавать параметры, что позволяет сделать код фильтра еще проще и понятнее.
Фильтр осуществляет логические проверки, которые помогают найти запущенный процесс — в нашем случае schtasks.exe (поле object.process.name). Поле object.process.cmdline содержит параметры процесса schtasks.exe, среди которых мы отбираем create, change, run, delete, end с помощью регулярного выражения. В результате мы получаем простейший детект: когда случится соответствующее событие, оно будет нормализовано и успешно пройдет фильтр, а затем коррелятор породит корреляционное событие, которое визуализируется в продукте.
После директивы rule также описывается условие корреляции, определяющее в том числе порядок и количество событий, а также временные отрезки, в которые они должны быть получены для срабатывания правила корреляции. В примере на рис. 2 ожидается появление только одного события. Узнать больше о синтаксисе условий корреляции можно здесь.
Еще одним преимуществом языка XP является простота настройки окружения по работе с ним через расширение VSCode XP. Нужно скачать релиз из репозитория XP Knowledge Base Toolkit, сделать клон репозитория c публичными правилами, и вуаля. Расширение лежит в магазине VS Code — ищите по ключевым словам «correlation, xplang, siem». Единственное, что потребуется сделать после установки — указать в настройках путь до Knowledge Base Toolkit. Буквально пара минут — и можно разрабатывать контент, изучать атаки и вносить свой вклад в сообщество.
Подробнее о работе с eXtraction and Processing мы рассказали на докладе «VSCode XP: корреляции без боли и страданий» и на воркшопе «Лучшие практики по созданию правил на языке XP».
Почему мы выбрали VS Code
Самый простой ответ — потому что большинство наших экспертов уже использовали эту IDE и имели соответствующий опыт. Кроме того, если погрузиться в функционал VSCode, можно выделить следующие преимущества этого инструмента:
- Поддержка Language server protocol (LSP), возможность легко расширить поддержку для редакторов Eclipse, Emacs, Notepad++, Sublime Text и других.
- Расширения поддерживают произвольные WebView, что позволяет реализовывать практически любые функции нативных приложений.
- Большой магазин существующих расширений: GitLens, Spell Checker и т. д.
- Кроссплатформенность (Windows, Linux, macOS).
- Доступность веб-версии через Docker.
- Множество цветовых тем на любой вкус и цвет.
Кроме того, при написании экспертизы мы хотели использовать все плюшки, которые уже давно есть у разработчиков: подсветка синтаксиса, автодополнение, сниппеты и многое другое. VS Code позволил нативно их реализовать.
В левой части интерфейса VSCode XP (см. рис. 3) представлено дерево контента. Open-xp-rules — ключевая директория системы, своего рода база знаний. В common находятся макросы, а ниже в packages — пакеты экспертизы. Обычно в директориях пакетов есть поддиректории, которые определяют тип содержащегося в них контента, например correlation_rules, enrichment_rules, normalization_formulas и др. Для упрощения поиска правил корреляции мы раскладываем их по техникам согласно классификации MITRE ATT&CK. На рис. 3 показаны несколько правил: ProxyNotShell (Initial Access), Schtasks_Commandline (Execution) и всеми любимый Mimikatz (Credential Access).
Пять правил работы с контентом в Positive Technologies
- Пишем тесты. Всегда пишем тесты. Любое правило не существует, если у него нет тестов; это значит, что перед вами какой-то код, который, возможно, работает, но, скорее всего, нет.
- Применяем test-driven development (TDD). Проводим разные варианты атак, собираем нужные события, на основе которых делаем тесты. После этого разрабатываем правило и запускаем тесты для проверки. Если все тесты прошли — правило работает, если нет — нужно исправить ошибки и доработать правило.
- Используем систему контроля версий. В нашем случае Git, но это не принципиально. Главное — иметь удобный инструмент для хранения и управления контентом.
- Используем feature branch workflow. Для создания правила от основной ветки разработки отводится feature branch, в которой создаются и исправляются правила. Затем автор заводит merge request в основную ветку, и его проверяют два других эксперта. После ревью ветка вливается в основную ветку разработки.
- Continuous integration для запуска тестов. CI поможет запустить все тесты (нормализаций, корреляций и обогащений) и через определенное время выдаст вердикт: все работает, можно собирать пакет и отдавать клиентам.
Как писать правила: разбираем на простейшем примере Mimikatz
Работа начинается со сбора событий. Предположим, у вас есть тестовая инфраструктура с настроенным аудитом. Если события заведены в MaxPatrol SIEM, можно скопировать нужные через интерфейс. Также можно копировать события в формате XML из Event Viewer или брать их из журналов Syslog и Auditd (скоро).
Следующий важный шаг — предварительная корреляция собранных событий, которая позволит понять, имеются ли какие-то сработки на данную последовательность. Здесь может быть несколько вариантов развития событий.
- Сработок нет. Если вы не можете задетектить хакерскую утилиту или атаку, пора разработать правило для выявления данной активности.
- Сработки есть, и по ним имеются адекватные данные. Поздравляем — кейс покрыт, дополнительные правила не потребуются.
- Есть ложные сработки (false positive). Если видите, что сработка не релевантна вашим событиям, необходимо скорректировать правило.
В нашем случае (см. рис. 4) VSCode XP тонко намекает, что сработок нет, значит, пора разрабатывать правило. Для удобства пользователей мы реализовали механизм создания правил из шаблонов: открываете раздел, вводите название правила и выбираете наиболее близкий из списка.
Например, шаблон For_Profiling подходит, если вы хотите разработать правила, профилирующие доступ к какой-либо системе, например GitLab, Teamcity или 1C_Enterprise. Следом идет группа шаблонов для Unix-систем. Все они привязаны к основному событию, на основании которого вы планируете писать детект. Например, сетевое подключение, запуск процесса и др. Далее следует группа шаблонов корреляций для Windows, содержащих в качестве основных событий загрузку DLL или EXE в адресное пространство процесса, создание потока в другом процессе, выполнение команд Powershell и т. д. Кроме того, существуют универсальные шаблоны, которые можно использовать, если не подошли другие. В случае с Mimikatz выбираем Windows_Process_Start.
У корреляционного события есть несколько обязательных полей, их можно задавать в on, но хорошая практика — делать это в блоке emit. Остановимся на блоке emit нашего шаблона (см. рис. 6). $correlation_type отвечает за типы корреляционных событий. Основные — event и incident, также есть subrule и draft. Субъект активности — это account; действие, которое он производит, — start; то, что он запускает, — object; статус действия — success. Поле $importance отвечает за важность срабатывания — она может варьироваться для разных типов корреляционных событий. Например, если обнаруженная атака действительно угрожает инфраструктуре, нужно использовать «high». В поля группы $category вносится информация о категории детекта, тактике и технике MITRE ATT&CK, которые соответствуют данной сработке.
Далее добавляем smoke-тесты, которые покажут, что мы подготовили полноценную заготовку для правила. Они нужны на этапе разработки правила для проверки отсутствия синтаксических ошибок, заполнения всех обязательных полей и наличия на выходе корреляционных событий (см. рис. 8).
Для этого:
- Запускаем с разными параметрами утилиту, активность которой хотим детектировать, и собираем группы сырых событий (raw events) из журналов аудита или ИБ-продуктов.
- Группы событий добавляем в поле Сырые события у разных тестов и оборачиваем в нужный конверт (технические метаданные события).
- В поле Условия прохождения теста автоматически добавляется нужный код:
expect 1 {"correlation_name": "Mimikatz"}
Если кратко, код означает следующее: после поступления собранных сырых событий с узла, их нормализации, корреляции и обогащения на выходе будет получено одно событие с полем correlation_name, равным Mimikatz. То есть будет сработка корреляционного правила с именем Mimikatz.
Кроме того, в окне интеграционных тестов есть кнопка Нормализовать событие. После ее нажатия исходное сырое событие превратится в нормализованное (внезапно). Теперь видно, какие поля и значения нужно использовать в фильтре правила. В нашем случае Windows-событие Event ID 4688 будет выглядеть как msgid: 4688.
Начнем с самого простого — в значении поля object.process.fullpath попробуем найти «Mimikatz». Берем шаблон, добавляем в него вызов внутренней функции языка regex. Передаем ей приведенное к lower-кейсу значение object.process.fullpath для избежания неоднозначности пути в ОС Windows. Вторым параметром передаем регулярное выражение, третьим — номер группы, которая извлекается из регулярного выражения и является возвращаемым значением функции regex.
Если забыли, как правильно вызвать функцию в XP, расширение подскажет, какие параметры она принимает, что возвращает, а также покажет пример правильного использования (см. рис. 10).
Следующее событие, которое мы нормализуем, — EventID 1 (Sysmon). Может получиться так, что Script kiddie, который раньше просто качал Mimikatz с GitHub, переименует его. Беспокоиться не о чем: версия с GitHub все равно содержит метаданные (object.process.meta), в которых есть description, где черным по белому написано: это Mimikatz. Для решения этой задачи достаточно расширить условия детекта.
Если хакер не только переименовал Mimikatz, но и заменил метаданные (в результате чего Mimikatz превратился, к примеру, в Bibikatz), заданными ранее условиями мы его уже не задетектим. Однако остаются параметры: их реже прячут или переименовывают, потому что для этого нужно обладать опытом разработки на С++. Снова расширяем условия детекта.
Фактически мы проверяем object.process.cmdline (см. рис. 12) на предмет ключевых слов, которые чаще всего используются Mimikatz. Если они есть в cmdline, это зловредная активность.
Прохождение smoke-тестов означает, что мы почти сделали простейшее правило по выявлению Mimikatz. Заметим, что, если бы мы писали правило с нуля, а не пользовались шаблоном, нам пришлось бы сделать еще один шаг: пробросить в корреляционное событие (присвоить корреляционному событию) значения наиболее важных и репрезентативных полей из исходного события (событий) в инструкции on. Обычно это достаточно большой кусок однообразного кода: например, на рис. 13 показан проброс только части полей события старта процесса в ОС Windows.
Пора переходить от smoke-тестов к полноценным интеграционным. Хорошая практика — помимо correlation_name проверять и другие результирующие поля правила, которые мы ранее пробросили из событий в блоках on. Для их формирования необходимо в каждом тесте нормализовать сырые события и получить ожидаемое с помощью одноименной кнопки (см. рис. 14).
После получения ожидаемого события автору нужно проверить правильность заполнения полей корреляции и добавить комментарий, описывающий данный тест. Если заполнение полей не соответствует ожиданиям, необходимо исправить код правила и повторно сгенерировать событие. И так до получения нужного результата.
Еще раз запускаем все тесты и проверяем, что они проходят. Отлично — мы готовы финишировать. Осталось несколько мелочей: добавить локализации (одну или несколько), заполнить метаданные (автор правила, описание выявляемых сценариев, необходимые события и т. д.), закоммитить правило и завести merge request с привлечением опытных ревьюверов.
Все готово, вы великолепны!
Если считаете, что детектировать Mimikatz в 2023 г. — это моветон, посмотрите, как написать с помощью VSCode XP чуть более сложное и актуальное правило на уязвимость ProxyNotShell. Это пример того, как два события мало о чем говорят по отдельности, но вместе позволяют обнаружить эксплуатацию опасной уязвимости.