Обнаруживаем атаки с помощью ML

от автора

Использование искусственного интеллекта позволяет существенно увеличить эффективность работы различных средств обеспечения кибербезопасности.

Сегодня мы попробуем решить задачу обнаружения использования вредоносного программного обеспечения злоумышленником. Для этого мы сначала еще немного поговорим об информационной безопасности, а потом уже перейдем к реализации на Python.

Вредоносное программное обеспечение может использоваться хакерами для: кражи данных, шифрования файлов с целью получения выкупа, обхода средств защиты, нарушения контроля доступа и многого другого.

Собственно, вредоносное ПО это не только пресловутые вирусы, но и различные хакерские инструменты, например знаменитый Metasploit Framework, который хотя и предназначен для проведения тестирований на проникновение белыми хакерами, тем не менее активно используется и обычными взломщиками.

Прикладная задача

Мы будем говорить об обнаружении вторжений и подозрительных аномалий трафика с помощью дерева решений. Нашей основной целью будет провести различие между хорошими и плохими сетевыми подключениями на основании результатов анализа представленного дампа трафика. Данные представляют собой дамп TCP-сессий, который был получен из локальной вычислительной сети реальной организации и в них имеется множество следов различных вредоносных активностей. 

Так на примере ниже представлен фрагмент трафика, собранного при сканировании вредоносом сети с зараженной машины:

Напомним, что для обучения нашей системы нам необходимы обучающий и тестовый наборы данных. Обучающий набор данных имеет размер около четырех гигабайт и состоит из сжатого дампа TCP-трафика за семь недель. Этот набор данных содержит около пяти миллионов сетевых подключений. В качестве тестовых данных был использован тот же TCP-трафик за две недели в объеме около двух миллионов подключений.

Собранные для нашего тестового примера данные снабжены метками, которые помогают определить принадлежность к той или иной вредоносной или другой активности. Вот список этих меток:

back,buffer_overflow,ftp_write,guess_passwd,imap,ipsweep,land,loadmodule,multihop,neptune,nmap,normal,perl,phf,pod,portsweep,rootkit,satan,smurf,spy,teardrop,warezclient,warezmaster.

Для анализа этих данных мы будем использовать уже знакомые нам пакеты Python numpy, pandas, предназначенные для машинного обучения. Также, для визуализации результатов мы используем пакет matplotlib:

from time import time

import numpy as np

import matplotlib.pyplot as plt

import pandas as pd

from sklearn.model_selection import cross_val_score

Для реализации изолирующего леса, о котором подробнее мы поговорим чуть позже, мы используем пакет sklearn.ensemble:

from sklearn.ensemble import IsolationForest

 Для оценки точности мы будем использовать характеристики ROC и AUC. ROC-кривая (англ. receiver operating characteristic, рабочая характеристика приёмника) — график, отображающий соотношение между долей объектов от общего количества носителей заданного признака, верно классифицированных как несущие данный признак, и долей объектов от общего количества объектов, не несущих признака, ошибочно классифицированных как несущие признак. AUC или area under curve — это просто площадь под кривой ROC.

И еще одно понятие KDD (обнаружение знаний в базах данных) — это процесс обнаружения полезных знаний и идей из больших и сложных наборов данных. Процесс KDD включает в себя ряд методов и методологий, включая предварительную обработку данных, преобразование данных, интеллектуальный анализ данных, оценку шаблонов и представление знаний. KDD и интеллектуальный анализ данных — это тесно связанные процессы, при этом интеллектуальный анализ данных является ключевым компонентом и подмножеством процесса KDD.

В рамках тех действий, которые мы будем выполнять дальше, мы будем использовать некоторые методы из состава KDD для работы с данными. 

Для начала мы импортируем нужные пакеты и загружаем данные:

from sklearn.metrics import roc_curve, auc

from sklearn.datasets import fetch_kddcup99

%matplotlib inline

dataset = fetch_kddcup99(subset=None, shuffle=True, percent10=True)

X = dataset.data

y = dataset.target

TCP-соединения, которые мы собрали в дампе, обладают рядом характеристик, такими как: тип протокола, продолжительность сессии, количество переданных байт, количество ошибочных пакетов и другими.

На основании этих характеристик мы можем в нашем коде подготовить массив, содержащий соответствующие параметры:

feature_cols = ['duration', 'protocol_type', 'service', 'flag', 'src_bytes', 'dst_bytes', 'land', 'wrong_fragment', 'urgent', 'hot', 'num_failed_logins', 'logged_in', 'num_compromised', 'root_shell',

'su_attempted', 'num_root', 'num_file_creations', 'num_shells', 'num_access_files', 'num_outbound_cmds', 'is_host_login', 'is_guest_login', 'count', 'srv_count', 'serror_rate', 'srv_serrer_rate', 'rerror_rate', 'srv_rerror_rate', 'same_srv_rate', 'diff_srv_rate', 'srv_diff_host_rate',

'dst_host_count', 'dst_host_srv_count', 'dst_host_same_srv_rate', 'dst_host_diff_srv_rate', 'dst_host_same_src_port_rate', 'dst_host_srv_diff_host_rate', 'dst_host_serror_rate', 'dst_host_srv_serror_rate', 'dst_host_rerror_rate', 'dst_host_srv_rerror_rate']

X = pd.DataFrame(X, columns = feature_cols)

y = pd.Series(y)

X.head()

Представленный выше код отобразит первые несколько строк таблицы со всеми названиями столбцов. Затем мы преобразуем столбцы в числа с плавающей запятой для эффективной обработки:

for col in X.columns:

try:

X[col] = X[col].astype(float)

except ValueError:

pass

Мы преобразуем категориальные переменные (переменные, которые называют категории, по которым распределяются данные) в фиктивные или индикаторные:

X = pd.get_dummies(X, prefix=['protocol_type_', 'service_', 'flag_'], drop_first=True)

X.head()

Теперь мы сгенерируем значения:

y.value_counts()

В результате мы получаем статистику по обнаружению различных видов подозрительных активностей. Как видно, здесь есть пакеты, связанные с использованием различных хакерских инструментов (smurf, neptune, satan), эксплуатации уязвимостей (buffer_overflow), сканирования сетевых ресурсов (nmap) и прочее:

smurf. 280790

neptune. 107201

normal. 97278

back. 2203

satan. 1589

ipsweep. 1247

portsweep. 1040

warezclient. 1020

teardrop. 979

pod. 264

nmap. 231

guess_passwd. 53

buffer_overflow. 30

land. 21

warezmaster. 20

imap. 12

rootkit. 10

loadmodule. 9

ftp_write. 8

multihop. 7

phf. 4

perl. 3

spy. 2

dtype: int64

Далее, мы строим для всех данных дерево классификации с максимальной глубиной, равной 7, следующим образом:

from sklearn.tree import DecisionTreeClassifier, export_graphviz

treeclf = DecisionTreeClassifier(max_depth=7)

scores = cross_val_score(treeclf, X, y, scoring='accuracy', cv=5)

print np.mean(scores)

treeclf.fit(X, y)

Получаем следующий результат предсказательной модели

0.9955204407492013

Как видно, мы получили довольно высокую результативность 99,55%. Это означает, что наша модель достаточно хорошо обучена и в принципе может успешно выявлять аномалии в собранных дампах трафика.

Дерево решений

Мы используем деревья решений для классификации данных на вредоносные и не вредоносные категории. Прежде чем мы углубимся в принципы работы данного алгоритма, мы рассмотрим теорию, лежащую в его основе.  Деревья решений — это подходы к принятию решений, которые в основном используются для классификации проблем. Они имеют дело с категориальными и некатегориальными переменными, где дифференциатор делит переменную на несколько однородных подмножеств. Деревья принятия решений основаны на линейных правилах принятия решений, где результатом является содержимое конечного узла:

В зависимости от типов целевых переменных, представленных в дереве решений, их можно разделить на две основные категории:

  • С категориальной переменной

  • С непрерывной переменной

Дерево решений с категориальными переменными. Переменная дерева решений может быть категориальной, то есть ответом может быть «да» или «нет». Типичный пример: Кандидат сдает экзамен, ДА или НЕТ.

Деревья решений, в которых целевая переменная является непрерывной, называются деревом решений с непрерывной переменной. Значение непрерывной переменной является периодическим. Например: время завершения работы с компьютером равно 1,333333333333333.

Коэффициент Джини

И еще одно понятие, пришедшее в машинное обучение из экономики — коэффициент Джини — это статистический показатель неравномерности распределения данных.

Следующий код показывает коэффициент Джини:

DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=7,

max_features=None, max_leaf_nodes=None,

min_impurity_decrease=0.0, min_impurity_split=None,

min_samples_leaf=1, min_samples_split=2,

min_weight_fraction_leaf=0.0, presort=False, random_state=None,

splitter='best')

Мы можем визуализировать результат с помощью функции graphiz:

export_graphviz(treeclf, out_file='tree_kdd.dot', feature_names=X.columns)

Конвертируем в png:

pd.DataFrame({'feature':X.columns, 'importance':treeclf.feature_importances_}).sort_values('importance', ascending=False).head(10)

Ниже мы видим результат:

Мы получили результат с помощью дерева решений, однако для эффективной работы этого будет явно недостаточно и здесь нам на помощь могут прийти случайные леса.

Случайные леса — это методы коллективного обучения, которые используются для целей классификации или регрессии. Они состоят из нескольких деревьев решений, которые объединяются для принятия единогласного решения или классификации. Случайные леса лучше, чем обычные деревья решений, потому что они не приводят к переобучению данных: Проще говоря, они дают более высокую точность.

Воспользуемся методом случайного леса для нашей классификации:

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier()

scores = cross_val_score(rf, X, y, scoring='accuracy', cv=5)

print np.mean(scores)

rf.fit(X, y)

Результат будет иметь следующий вид:

Out[39]:

0.9997307783262454

Таким образом, метод случайного леса выдал нам более точный результат, чем простое дерево принятия решений: 

RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',

max_depth=None, max_features='auto', max_leaf_nodes=None,

min_impurity_decrease=0.0, min_impurity_split=None,

min_samples_leaf=1, min_samples_split=2,

min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,

oob_score=False, random_state=None, verbose=0,

warm_start=False)

pd.DataFrame({'feature':X.columns,

'importance':rf.feature_importances_}).sort_values('importance',

ascending=False).head(10)

Таким образом, используя метод случайного леса, мы можем существенно улучшить точность наших предсказаний, тем самым эффективнее выявляя вредоносную активность в общем потоке трафика. Например, для трафика с признаками buffer_overflow мы получим точность обнаружения 

0.9985207412289135, что позволяет практически безошибочно выявлять данную вредоносную активность.

Заключение

В этой статье, как и в предыдущей у нас тоже было много теории машинного обучения, которая необходима для эффективного моделирования. Мы также рассмотрели различные типы деревьев принятия решений, в том числе деревья принятия решений с категориальными и непрерывными переменными и метод случайных лесов. Стоит отметить, что представленный в статье материал это лишь небольшая часть методов машинного обучения, которые могут использоваться для задач информационной безопасности.


ссылка на оригинал статьи https://habr.com/ru/articles/824074/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *