![[Перевод] Как навести порядок в оформлении YARA-правил](/images/cyber/bafa9b1790ff7740.jpg)
Написать рабочее yara-правило несложно. Создать такое правило, которое через полгода поймет не только автор, но и любой другой аналитик в команде — задачка со звездочкой. У YARA нет строгого стандарта, как называть правило, что писать в meta и как оформлять условие. Каждый пишет как привык, и со временем база правил превращается в набор разномастных, подчас не очень логичных файлов.
Написать рабочее yara-правило несложно. Создать такое правило, которое через полгода поймет не только автор, но и любой другой аналитик в команде — задачка со звездочкой. У YARA нет строгого стандарта, как называть правило, что писать в meta и как оформлять условие. Каждый пишет как привык, и со временем база правил превращается в набор разномастных, подчас не очень логичных файлов.
___________________________________________________
Привет, Хабр!
Меня зовут Максим Мотиков, я специалист по кибербезопасности в «Гарде». В нашей команде накопилось приличное количество YARA-правил . Мне прилетела задачка систематизировать их разработку. Полез разбираться, что уже есть в индустрии на эту тему, и наткнулся на YARA Style Guide . Этот гайд мне показался вполне рабочим: в нем уже собрано почти все необходимое — от правил именования до классификации строк по степени специфичности.
Я решил не изобретать велосипед, а перевести и адаптировать это руководство под наши корпоративные нужды. Под катом делюсь переводом гайда.
P.S. Гайд не сильно углубляется в вопросы производительности, хотя соблюдение этих практик может косвенно оптимизировать работу набора правил. За более подробными стратегиями оптимизации производительности стоит обратиться к отдельному проекту автора — YARA-Performance-Guidelines .
Выбор названия правила
Имя правила часто оказывается единственной или первой информацией, которую видит пользователь. Поэтому оно должно быть максимально информативным. Например, содержать тип угрозы, теги для классификации, описательный идентификатор и даже контекст или период создания правила.
Далее приведу примеры, как это может выглядеть.
Основные категории в имени правила
malware
hacktool
Хакерские инструменты
exploit
Код эксплойтов (PoC, полезные нагрузки и т. д.)
vulnerability
Уязвимости (уязвимый драйвер, уязвимая JAVA-библиотека и т. д.)
suspicious
Аномалии и подозрительные возможности (обфусцированный код, шелл-коды, подозрительные комбинации импортов и т. д.)
possibly unwanted applications
Потенциально нежелательные приложения
Списки категорий не исчерпывающие, их можно расширить при необходимости.
Назначение и контекст
Связь с APT-группировкой
Активность киберпреступных группировок
Общие подозрительные характеристики
Типы вредоносного ПО или файлов
Троян удаленного доступа
PEEXE (часто опускается)
Операционная система, архитектура, технология
При необходимости имя правила можно дополнить информацией о целевой платформе (операционной системе, архитектуре) и используемой технологии. Многие из этих тегов часто опускаются, если контекст и так очевиден.
Возможные значения
Операционная система
WIN (часто опускается), LNX, MacOS
X64 (часто опускается), X86 (часто опускается), ARM, SPARC
Технология (формат/язык)
PE (часто опускается), ELF, PS, PS1, VBS, BAT, JS, NET, GO, Rust, PHP, JSP, ASP, MalDoc, LNK, ZIP, RAR
Обфусцированные образцы
Закодированные полезные нагрузки
Распакованные полезные нагрузки
Код, обнаруживаемый только в памяти
Упаковщики или установщики
Если вредонос упакован определенным упаковщиком, это тоже можно отразить в имени. Чаще всего встречаются SFX (самораспаковывающиеся архивы), UPX, Themida и NSIS. Упаковщик в имени правила помогает аналитику сразу понять, потребуется ли предварительная распаковка для дальнейшего анализа.
Идентификаторы субъектов и семейств угроз
Имена конкретных субъектов угроз и семейств вредоносного ПО включаются в правило как есть, без сокращений. Например, если правило детектит активность группировки Lazarus, в имя добавляется Lazarus, а не какой-то условный код. То же самое касается семейств вредоносного ПО: CobaltStrike, PlugX, QakBot и т. д. Такой подход делает имя самодокументируемым: аналитик сразу видит, с какой угрозой или группировкой связано срабатывание.
Суффиксы для уникальности
Суффиксы снижают вероятность того, что два аналитика выберут одинаковое имя для правила.
MonthYear. Например, May23, Jan19
MonthYear. Например, May23, Jan19
Number. Например, _1, _2
Number. Например, _1, _2
Комбинирование категорий
Чтобы классификация была более точной, ключевые слова лучше комбинировать (см. примеры ниже).
Имя правила
Что означает
Цифровые следы в системах, скомпрометированных субъектом угрозы
MAL_CRIME_RANSOM_LNX_Rust_*
Rust-вредонос под Linux, используемый киберпреступными группами с вымогателями
ASP-веб-шеллы, применяемые государственными акторами
Имя правила
Что означает
APT_MAL_CozyBear_ELF_Loader_Apr18
Правило от апреля 2018 года для Linux-загрузчика, используемого Cozy Bear
SUSP_Anomaly_LNK_Huge_Apr22
Правило от апреля 2022 года для подозрительно больших LNK-файлов
MAL_CRIME_RANSOM_PS1_OBFUSC_Loader_May23
Правило от мая 2023 года для обфусцированного PowerShell-загрузчика в кампании с вымогателями
Одно из главных преимуществ такой системы именования правил состоит в том, что даже без открытия файла по одному имени правила можно сделать вывод о категории угрозы, платформе, технологии и приблизительном периоде создания. Если в компании база правил довольно крупная, то этот подход сэкономит аналитикам часы работы.
Структура правила
Прежде чем разбирать отдельные элементы, посмотрим на общий каркас правила. Ниже — шаблон с рекомендуемым автором гайда порядком секций.
Далее по порядку разберу каждый элемент.
Для повышения читаемости лучше использовать отступы. В большинстве опубликованных правил приняты 3 или 4 пробела либо табуляция.
Далее посмотрим на пример правила с «плохими» отступами.
Еще один частый антипаттерн — разная глубина отступов у ключевых слов и значений. Например, это может выглядеть следующим образом.
Теги правил
Основные категории лучше включать прямо в имя правила, так его проще идентифицировать. Но что делать с дополнительными, менее значимыми метками? Их лучше выносить в поле метаданных tags.
Такие теги могут обозначать имена субъектов угроз, семейства вредоносного ПО или типы атак.
Метаданные правила
Помимо тегов, в секции meta хранится и другая важная информация о правиле. Например, здесь указывают автора, ссылку на исследование, дату создания и любые другие значимые сведения. Часть этих полей обязательна, часть можно добавлять по необходимости.
Рассмотрим, какие поля метаданных стоит заполнять и как это лучше сделать.
Обязательных поля — всего четыре. Без них аналитик, получивший срабатывание правила, не сможет быстро понять, что оно детектит, кто его написал и откуда оно взялось.
Строка, 60–400 символов
Начинать с "Detects ...". URL-адреса выносить в reference
Полное имя или никнейм в Twitter. Несколько авторов через запятую в одном поле
Список строк
Прямые URL-адреса на стабильные публичные источники. Для собственных исследований указывать "Internal Research"
Строка, YYYY-MM-DD
Дата создания правила, а не публикации. Для изменений есть отдельное поле modified
Эти поля не обязательны для работы правила, но дают дополнительный контекст. Особенно полезны hash (чтобы быстро проверить правило на конкретном образце) и score (чтобы расставить приоритеты при массовых срабатываниях).
Список строк
Предпочтительно SHA256. Хеш самого файла, а не архива (кроме случаев обнаружения в памяти)
Число 0–100
Серьезность угрозы + специфичность правила
Строка, YYYY-MM-DD
Дата последнего изменения
Предыдущее имя правила для поиска по истории
Список тегов через запятую
Лицензия, под которой выпущено правило
Из всех необязательных полей score заслуживает отдельного разбора, потому что именно оно помогает расставить приоритеты при массовых срабатываниях. Как выставлять Score?
Score отражает сразу два параметра: насколько серьезна угроза и как точно правило ее идентифицирует. Когда правил сотни и срабатываний десятки, аналитик может расставить приоритеты в реагировании по числовой оценке, не вчитываясь в каждое правило. Score 85+ означает, что совпадение с высокой вероятностью является реальной угрозой, а 30 указывает скорее на отдельный подозрительный признак, который стоит учитывать в совокупности.
0–39
Очень низкая
Возможности, упаковщики и т. п. (часто комбинируются для более высокой итоговой оценки)
40–59
Заслуживает внимания
Редкие упаковщики, аномалии в PE-заголовках
60–79
Срабатывания эвристик, обфускация, общие правила
80–100
Прямые совпадения с вредоносным ПО / хакерскими инструментами
Строки в правиле
С метаданными разобрались. Теперь к содержательной части. Секция strings определяет, что именно YARA будет искать в файле.
Здесь $s1 — это простая строка, $s2 — последовательность байтов, а $r1 — регулярное выражение. Все рекомендации ниже относятся именно к этой секции и помогают сделать строки более читаемыми.
Читаемость строковых значений
Одна из частых ошибок — записывать обычные текстовые строки в виде hex-последовательностей. Такую запись сложно прочитать без конвертации, и при ревью правила непонятно, что именно ищется.
Такое представление лучше не использовать:
$s1 = { 46 72 6F 6D 42 61 73 65 36 34 53 74 72 69 6E 67 28 }
Достаточно такого описания:
$s1 = "FromBase64String("
Исключение составляют строки с управляющими символами вроде \t или \n, для которых hex-формат предпочтителен.
Краткие идентификаторы
Похожая ситуация — это длинные, ничего не говорящие идентификаторы вроде $string_value_footer_1 или $selection_14. Когда таких переменных много и они используются в сложном условии, код становится трудночитаемым.
Лучше использовать краткие или описательные имена. Вот пример, как это выглядит на практике.
Шестнадцатеричные идентификаторы
Для hex-значений, состоящих в основном из ASCII, полезно указывать строковое представление в комментарии.
Длинные значения можно разбивать на сегменты по 16 Б. Так правило становится удобнее читать. В примере ниже продемонстрировал, как это может выглядеть.
Категоризация строк по уровням $x*, $s*, $a*
Не все строки в правиле одинаково значимы. Одни почти со 100-процентной вероятностью указывают на конкретную угрозу, другие полезны только в комбинации, а третьи нужны по большей части для предварительной фильтрации. Автор гайда предлагает трехуровневый подход, в котором каждый уровень обозначается своим префиксом:
Высокоспецифичные строки ($x*). Уникальные индикаторы, почти всегда указывающие именно на искомую угрозу.
Высокоспецифичные строки ($x*). Уникальные индикаторы, почти всегда указывающие именно на искомую угрозу.
Групповые строки ($s*). Не уникальны по отдельности, но значимы в комбинации.
Групповые строки ($s*). Не уникальны по отдельности, но значимы в комбинации.
Строки предварительной выборки ($a*). Часто встречающиеся строки, сужающие тип/формат файла и оптимизирующие производительность поиска.
Строки предварительной выборки ($a*). Часто встречающиеся строки, сужающие тип/формат файла и оптимизирующие производительность поиска.
Разберем этот подход на примере реального правила.
Здесь строки сгруппированы по типу и разделены пустыми строками для наглядности. Обратите внимание на логику условия. Достаточно одной высокоспецифичной строки ($x*), но если таких нет, то нужен весь набор групповых строк ($s*). Это компромисс между точностью и полнотой обнаружения.
Фильтры ложных срабатываний ($fp*)
Когда правило дает много ложных срабатываний, ему перестаешь доверять, а со временем и вовсе начинаешь игнорировать. Строки, указывающие на легитимные шаблоны, помечаются префиксом fp.
Если правило совпадает и с вредоносным паттерном, и со строкой $fp*, оно не срабатывает. Вот как это реализуется на практике.
Условие срабатывания правила
Если строки определяют, что искать, то условие отвечает на вопрос, при какой комбинации находок правило считается сработавшим. Слишком широкие или нестрогие условия ведут к росту ложных срабатываний, слишком узкие — к тому, что правило пропустит модификацию угрозы.
Есть несколько приемов, которые делают условие более читаемым.
Первый прием, который стоит взять на вооружение, — переносить каждый and на новую строку. Так, при беглом просмотре сразу видно, сколько проверок содержит правило.
Второй — оборачивать части условия, объединенные через or , в отдельный блок с отступом.
Тот же принцип применим и для проверки нескольких возможных маркеров файла. Например, если правило должно работать и для Windows, и для Linux, проверку маркера тоже стоит обернуть в блок.
Оптимизация. Строковое сопоставление вместо циклов
Последний раздел гайда касается производительности. Нередко для поиска паттернов в PE-заголовках используют циклы и хеширование:
Однако этот подход менее эффективен, чем кажется. YARA изначально заточена под прямое сопоставление строк и паттернов. Проще и быстрее просто включить те же 256 Б как шестнадцатеричную строку прямо в правило. Это избавляет от накладных расходов на хеширование и использует встроенную эффективность YARA без лишней нагрузки на CPU.
Единый стиль оформления YARA-правил нужен не для красоты — он напрямую влияет на то, насколько быстро аналитик поймет, что именно обнаружило правило, и насколько легко команда сможет поддерживать общий набор сигнатур. Ключевые вещи, которые, на мой взгляд, стоит забрать из гайда:
осмысленное имя от общего к частному;
осмысленное имя от общего к частному;
обязательные поля метаданных;
обязательные поля метаданных;
три уровня специфичности строк ($x*/$s*/$a*);
три уровня специфичности строк ($x*/$s*/$a*);
фильтры ложных срабатываний ($fp*).
фильтры ложных срабатываний ($fp*).