Вредоносное ПО — одна из самых распространенных угроз ИБ. Существуют разные методы защиты от атак с применением ВПО, но чаще всего выделяют два подхода — статический и динамический (поведенческий) анализ.
В первом случае задача сводится к поиску шаблонов вредоносного содержимого в файле или памяти процесса. Это могут быть строки кода, фрагменты закодированных или сжатых данных, а также последовательности скомпилированного кода. При этом можно искать не только отдельные шаблоны, но и их комбинации с дополнительными условиями: с привязкой к месту нахождения сигнатуры, с проверкой расстояния между ними и т. д.
Во втором случае речь идет об анализе поведения программы. Можно запустить ее в режиме эмуляции, который предполагает безопасное интерпретирование действий без причинения вреда операционной системе. Либо в виртуализированном окружении (песочнице). Этот подход подразумевает честное выполнение действий в системе с последующей фиксацией вызовов. Степень подробности логирования — это своего рода баланс между глубиной наблюдения и производительностью анализирующей системы. На выходе мы получаем журнал действий программы в ОС — трассу поведения, которую можно анализировать.
В сравнении со статическим анализом у динамического подхода есть важное преимущество: несмотря на все попытки злоумышленника запутать программный код и скрыть свои намерения, вредоносное воздействие будет зафиксировано. Сведение задачи обнаружения вредоносного ПО к анализу действий программы позволяет выдвинуть гипотезу об устойчивости продвинутого алгоритма обнаружения вредоноса. А воспроизводимость поведения благодаря одному и тому же изначальному состоянию среды (слепок состояния виртуальной машины) упрощает задачу классификации легитимных и вредоносных действий.
В основе поведенческого анализа зачастую лежат наборы правил: экспертные заключения переносятся в сигнатуры, с помощью которых инструмент детекта делает выводы о вредоносности ПО. Этот подход хорошо себя показывает, но не исключает следующую проблему: решение будет фиксировать только те атаки, которые строго соответствуют заданным правилам. Аналогичные сложности возникают и в случае изменения или модернизации ВПО. Решить проблему можно двумя способами: задать более мягкие критерии срабатывания (написать более общее правило) либо сформировать множество правил под каждое вредоносное ПО. В первом сценарии есть риск получить массу ложных срабатываний, а для реализации второго требуется много времени, что может негативно повлиять на скорость выхода обновлений.
Как результат, появляется потребность в распространении имеющихся знаний на новые случаи, которые мы еще не покрыли правилами, но можем предположить, что это ВПО, на основе известных признаков. Здесь нам на помощь приходят ML-алгоритмы. При корректном обучении ML-модели имеют обобщающую способность. Проще говоря, модель не просто запоминает предложенные примеры, а может обрабатывать новые кейсы с учетом закономерностей из обучающей выборки.
Чтобы ML-модель работала корректно, во время ее обучения важно учитывать два фактора:
- Набор признаков должен быть как можно более полным (чтобы модель увидела как можно больше закономерностей и лучше распространяла знания на новые примеры), но не избыточным (чтобы не хранить и не обрабатывать бесполезные признаки).
- Набор данных должен быть репрезентативным, сбалансированным и регулярно обновляемым.
Мы решили провести исследование: сформировать набор признаков, обучить модель и проверить, можно ли доверять выводам ML-решения о вредоносном характере файлов и программ.
Экспертные знания в ML-модели
В контексте анализа вредоносного ПО исходные данные — это сами файлы (.exe, .pdf, .msi, .zip и др.), а промежуточные — созданные ими вспомогательные процессы. Эти процессы производят системные вызовы, последовательности которых (трассы) нужно преобразовать в набор признаков и проанализировать. При этом выводы о вредоносности с экспертной точки зрения можно делать на основе отдельных вызовов, их последовательностей, а также сочетаний первых и вторых.
Мы начали составление датасета с выбора признаков, которые, по мнению специалистов, являются значимыми в контексте обнаружения ВПО. Все эти признаки можно свести к виду n-грамм по системным вызовам. Затем мы оценили, какие из них вносят наибольший вклад в процесс обнаружения, отбросили лишние и получили итоговую версию датасета.
Исходные данные:
{"count":1,"PID":"764","Method":"NtQuerySystemInformation","unixtime":"1639557419.628073","TID":"788","plugin":"syscall","PPID":"416","Others":"REST: ,Module=\"nt\",vCPU=1,CR3=0x174DB000,Syscall=51,NArgs=4,SystemInformationClass=0x53,SystemInformation=0x23BAD0,SystemInformationLength=0x10,ReturnLength=0x0","ProcessName":"windows\\system32\\svchost.exe"}
{"Key":"\\registry\\machine","GraphKey":"\\REGISTRY\\MACHINE","count":1,"plugin":"regmon","Method":"NtQueryKey","unixtime":"1639557419.752278","TID":"3420","ProcessName":"users\\john\\desktop\\e95b20e76110cb9e3ecf0410441e40fd.exe","PPID":"1324","PID":"616"}
{"count":1,"PID":"616","Method":"NtQueryKey","unixtime":"1639557419.752278","TID":"3420","plugin":"syscall","PPID":"1324","Others":"REST: ,Module=\"nt\",vCPU=0,CR3=0x4B7BF000,Syscall=19,NArgs=5,KeyHandle=0x1F8,KeyInformationClass=0x7,KeyInformation=0x20CD88,Length=0x4,ResultLength=0x20CD98","ProcessName":"users\\john\\desktop\\e95b20e76110cb9e3ecf0410441e40fd.exe"}
Промежуточные данные (последовательности):
syscall_NtQuerySystemInformation*regmon_NtQueryKey*syscall_NtQueryKey
Вектор признаков:
... | syscall_NtQuerySystemInformation*regmon_NtQueryKey | regmon_NtQueryKey*syscall_NtQueryKey | syscall_NtQuerySystemInformation*syscall_NtQueryKey | ... |
... | 1 | 1 | 0 | ... |
Накапливаем данные
Мы уже упоминали, что основные требования к данным для обучения ML-модели — это репрезентативность, сбалансированность и регулярная обновляемость. Рассмотрим эти характеристики в контексте поведенческого анализа вредоносных файлов:
- Репрезентативность. Данные должны иметь распределение по признакам, близкое к распределению в реальности.
- Сбалансированность. Исходные данные для тренировки модели поступают в нее с разметкой «легитимные» или «вредоносные». Количество примеров обоих классов должно быть примерно равным.
- Регулярная обновляемость. Во многом этот пункт связан с репрезентативностью данных. Поскольку тренды в области вредоносных файлов постоянно меняются, необходимо регулярно актуализировать решение.
С учетом перечисленных требований мы выстроили следующий процесс накопления данных:
- Данные делятся на два типа — основной поток и эталонные примеры. Последние проверяются экспертами вручную, поэтому корректность их разметки гарантирована. Они нужны для валидации модели и управления тренировочной выборкой. Основной поток размечается правилами и автоматизированными проверками. Это необходимо для обогащения выборки всевозможными реальными примерами.
- Все эталоны сразу добавляются в обучающую выборку.
- Также в обучающую выборку добавляется некоторый изначальный набор данных из потока (чтобы собрать необходимый для обучения объем информации). Под необходимым объемом в данном случае подразумевается такое количество данных, с которым тренировочная выборка становится достаточно полной (разнообразной) и репрезентативной. Поскольку эталонные примеры проверяются вручную, собрать выборку из нескольких десятков тысяч таких кейсов невозможно — добирать разнообразие приходится с помощью потока.
- Периодически модель тестируется на новых данных из потока.
- Точность модели в первую очередь гарантируется для эталонных примеров. Соответственно, в случае противоречий предпочтение отдается эталонным данным.
Со временем мы собрали достаточно много данных из потока, настал момент отказаться от автоматизированного накопления на основе ошибок в пользу более контролируемой обучающей выборки:
- Фиксируем накопленную обучающую выборку.
- Данные из потока используем только для тестирования модели (ни один экземпляр не добавляется в обучающую выборку).
- Обновление обучающей выборки возможно только в случае обновления набора эталонных примеров.
Каких результатов мы добились:
- Убедились, что обученная зафиксированная модель достаточно устойчива к «дрифту» данных.
- Контролируем каждый новый пример, который добавляем в обучающую выборку (эталонные примеры проверяются вручную).
- Можем отслеживать изменения и гарантировать точность работы модели на эталонном наборе данных.
С процессом накопления данных разобрались, но дальше возникает закономерный вопрос: почему мы уверены, что каждое обновление модели ее улучшает? Ответом на него является все та же эталонная выборка. Примеры из нее всегда проверяются и размечаются вручную, поэтому мы считаем ее корректной. Соответственно, при каждом обновлении мы в первую очередь проверяем, что модель показывает 100-процентную точность именно на эталонной выборке. А затем смотрим на результаты тестирования in the wild, которые также должны показывать, что эффективность решения повышается.
Положительный результат достигается за счет очистки обучающей выборки от противоречащих эталонных данных. Это примеры из потока, которые достаточно близки по векторному расстоянию к трассам из эталонной выборки, но при этом имеют противоположную метку. Наши эксперименты показали, что такие примеры являются выбросами даже с точки зрения данных из потока, так как после удаления их из обучающей выборки с целью повышения точности на эталонной выборке возрастала и точность на потоке.
В чем хороша ML-модель
ML-модель отлично проявила себя в сочетании с поведенческими детектами в виде корреляций. Подчеркнем, что именно в сочетании: обобщающая способность модели хороша в случаях, когда необходимо расширить решение детектом схожих инцидентов. Однако она не так эффективна при детектах в рамках четкого понимания правил и критериев вредоносности ПО.
Примеры, где ML-подход показал себя с хорошей стороны:
- Аномальные цепочки подпроцессов. Само по себе большое количество ветвистых цепочек — вещь легитимная. При этом модель замечает аномалии в количестве узлов, степени вложенности, повторяемости/неповторяемости конкретных имен процессов и др. Человек вряд ли до такого додумается.
- Нестандартные значения параметров вызовов по умолчанию. В большинстве случаев аналитиков интересуют значимые параметры функций, в которых они ищут что-либо вредоносное. На остальные параметры (грубо говоря, значения по умолчанию) они не особо обращают внимание. Но в определенный момент вместо пяти значений по умолчанию их может стать шесть. Не факт, что аналитик это заметит, а модель сразу зафиксирует.
- Нетипичные последовательности вызовов функций. Тот случай, когда функции не делают ничего вредоносного ни по отдельности, ни в совокупности, но их последовательность не встречается в легитимном ПО. Аналитику потребуется гигантский опыт, чтобы заметить подобную закономерность. А модель заметит, причем не одну, нестандартно решая задачу классификации по признаку, который даже не закладывался как показатель вредоносности.
И напоследок — несколько примеров, где сигнатурный поведенческий анализ оказался более эффективным:
- Использование одного конкретного компонента одним вызовом для вредоносного действия. Система по-разному использует сотни объектов. Уловить использование одного объекта на фоне миллиона других едва ли удастся — гранулярность аномалии все же низковата.
- Проактивный детект по модели угроз. Предположим: мы решили, что определенное действие с определенным объектом в системе в принципе недопустимо — даже если это единичный случай. С первого раза модель может не понять, что это значимое явление. Соответственно, возникает вероятность ошибки или неуверенного решения на этапе классификации.
- Обфускация последовательности действий. Например, нам известно, что программа должна выполнить три-четыре действия в определенном порядке, чтобы модель классифицировала ее поведение как вредоносное. Что будет между ними — неважно. Если накидать между тремя-четырьмя ключевыми действиями несколько «мусорных», это собьет модель и она примет неверное решение. При этом размерность числа признаков не позволяет учитывать такие запутывания с помощью хранения всех комбинаций последовательностей вызовов.