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

Сегодня мы попробуем решить задачу обнаружения использования вредоносного программного обеспечения злоумышленником. Для этого мы сначала еще немного поговорим об информационной безопасности, а потом уже перейдем к реализации на 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, что позволяет практически безошибочно выявлять данную вредоносную активность.

Заключение

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

Комментарии (7)


  1. CrazyElf
    24.06.2024 13:54

    Так что всё-таки насчёт изолирующего леса? Импортировали - и забыли про него? )


    1. cdnnow-creator
      24.06.2024 13:54

      Изолирующий лес представлен в качестве одного из примеров работы алгоритмов ИИ.


      1. CrazyElf
        24.06.2024 13:54

        Но ведь примера то использования нет. Кроме того, это алгоритм выявления аномалий, а про это у вас тоже в итоге ничего нет.


  1. semipro
    24.06.2024 13:54

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

    Спасибо за статью. А как собирали эти метки? Вручную? Сколько по времени ушло? Сколько людей было вовлечено в этот процесс?


    1. cdnnow-creator
      24.06.2024 13:54
      +1

      Трафик размечался средствами IDS (настроенных, с минимумом ложных срабатываний), так как вручную разметить такой объем трафика очень сложно.


      1. semipro
        24.06.2024 13:54

        А в IDS какая-то своя ML-модель или какой-то rule-based подход?


        1. cdnnow-creator
          24.06.2024 13:54
          +1

          Использовался настроенный Rule Based IDS с настроенными на обнаружение аномалий правилами.