Розпізнавання DGA доменів. А що якщо нейронні мережі?


Всім привіт!
Сьогодні ми поговоримо про розпізнавання доменів, згенерованих за допомогою алгоритмів генерації доменних імен. Подивимося на існуючі методи, а також запропонуємо свій, на основі рекурентних нейронних мереж. Цікаво? Ласкаво просимо під кат.
Алгоритми Генерації Доменних Імен (DGA) являють собою алгоритми, використовувані шкідливим програмним забезпеченням (malware) для генерації великої кількості псевдовипадкових доменних імен, які дозволять встановити з'єднання з керуючим командним центром. Тим самим, вони забезпечують потужний шар захисту інфраструктури для шкідливих програм. На перший погляд, концепція створення великої кількості доменних імен для встановлення зв'язку не видається складною, але методи, використовувані для створення довільних рядків, часто ховаються за різними верствами обфускации. Це робиться для ускладнення процесу зворотної розробки і отримання моделі функціонування того чи іншого сімейства алгоритмів.
Наприклад, одним з перших випадків був комп'ютерний черв'як Conficker у 2008 році. Сьогодні нараховуються подібних шкідливих програм десятки, кожна з яких являє серйозну загрозу. Крім цього, алгоритми вдосконалюються, їх виявлення стає складніше.
Загальний принцип роботи
Малюнок роботи
У загальному випадку шкідливого файлу необхідний seed для ініціалізації генератора псевдовипадкових чисел (ДПРЧ). Як seed може виступати будь-який параметр, який буде відомий шкідливого файлу і власнику ботнету. У нашому випадку — це значення поточної дати і часу, взяті з ресурсу cnn.com. Використовуючи однакові вектори ініціалізації, шкідливий файл і власник ботнету отримують ідентичні таблиці доменних імен. Після цього власникові ботнету достатньо зареєструвати лише один домен для того, щоб шкідливий файл, рекурсивно посилаючи запити до DNS-сервера, отримав IP-адресу керуючого сервера для подальшої установки з ним з'єднання та отримання команд.
Розпізнавання
В даний час існує безліч робіт, пов'язаних c аналізом алгоритмів генерації доменних імен. Деякі з них використовують методи Машинного навчання. В основному, застосовуються усім відомі моделі, які можна зустріти в середовищі обробки і аналізу природних мов, — моделі n-gramm, TF-IDF і ін
Однак постає питання навчальної вибірки. Наша вибірка складатиметься з 2 класів. Перший — Legit, був узятий зі списку Alexa Top Million. Другий — DGA, був складений шляхом зворотної розробки алгоритмів генерації шкідливих доменних імен, взятих з примірників шкідливих програм, існуючих в мережі Інтернет, і доступний в репозиторії (https://github.com/andrewaeva/DGA).
Для початку ми спробували підхід, описаний хлопцями з Сlicksecurity. Ними пропонується використання наступного списку параметрів: довжина, ентропія, модель TF-IDF з виділенням N-gram. Першим параметром виступає довжина доменного імені. Другий параметр — ентропія. Далі, була розглянута модель N-gram. Кожен n-gram (від 3 до 5) був представлений як вектор в n-мірному просторі, і відстань між ними було розраховане за допомогою скалярного добутку цих векторів. Це досить просто реалізується за допомогою бібліотеки Scikit Learn.
Шматочок коду на Python
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer

alexa_vc = CountVectorizer(analyzer='char', ngram_range=(3, 5), min_df=1e-4, max_df=1.0)
counts_matrix = alexa_vc.fit_transform(dataframe_dict['alexa']['domain'])
alexa_counts = np.log10(counts_matrix.sum(axis=0).getA1())

dict_vc = CountVectorizer(analyzer='char', ngram_range=(3, 5), min_df=1e-5, max_df=1.0)
counts_matrix = dict_vc.fit_transform(word_dataframe['word'])
dict_counts = np.log10(counts_matrix.sum(axis=0).getA1())

all_domains['alexa_grams'] = alexa_counts * alexa_vc.transform(all_domains['domain']).T
all_domains['word_grams'] = dict_counts * dict_vc.transform(all_domains['domain']).T
all_domains['diff'] = all_domains['alexa_grams'] - all_domains['word_grams']

В результаті ми отримали додатково 3 параметри: Alexa gram — косинусне відстань до словника, що складається з доменів Alexa Top Million Word gram — косинусне відстань до спеціально складеного словника, що складається з найуживаніших слів і фраз, і параметр diff = alexa gram — word gram.
Для кожного з параметрів ми побудували наочні графіки. Не забудьте подивитися :)
Графіки під спойлером




Сама класифікація проводилась за принципом 80/20, тобто навчання відбувалося на 80% вихідних даних, а тестування алгоритму — на 20%. Після перевірки якості класифікації були отримані наступні результати:








Алгоритм Точність класифікації Logistic Regression 87% Random Forest 95% Naive Bayes 75% Extra Tree Forest 94,6% Voting Classification 94,7%
Ми подумали, чому досі ніхто не спробував використовувати нейронні мережі? Треба пробувати!
Нейронні мережі
Для вирішення нашої проблеми ми задіяли рекуррентную нейронну мережу. Рекурентні нейронні мережі, головним чином, відрізняються наявністю циклу. Вони дозволяють зберігати і використовувати інформацію, одержану з попередніх кроків нейронної мережі. Кожен домен в нашому випадку розглядається як послідовність символів з фіксованого словника, яка подається на вхід рекурентної нейронної мережі. Навчання такої нейронної мережі здійснюється методом зворотного поширення помилки таким чином, щоб максимізувати ймовірність правильного вибору відповідного класу.

Рекурентна нейронна мережа і Yandex.ru
Така архітектура нейронної мережі може аналізувати інформацію, подану раніше для аналізу даних в даний момент часу. Однак, на практиці виявляється, що якщо розрив між минулого інформацією і справжньою досить великий, то цей зв'язок втрачається, і подібна мережа нездатна її обробляти. Рішення цієї проблеми було знайдено в 1997 році вченими Hochreiter & Schmidhuber. У своїй роботі вони запропонували нову модель рекурентної нейронної мережі, а саме Long short-therm memory. В даний час дана модель широко використовується для вирішення різних класів завдань, таких як: розпізнавання мови, обробка природних мов та ін. LSTM складається з ряду постійно пов'язаних підмереж, відомих як блоки пам'яті. Замість одного шару нейронної мережі, в даній моделі використовується 4 шари, взаємодіючих особливим чином. У своїй моделі ми будемо використовувати різновид моделі LSTM, а саме Gated Recurrent Unit (GRU). Детальніше про LSTM і GRU можна почитати в чудовій статті (http://colah.github.io/posts/2015-08-Understanding-LSTMs/), а ми рушимо далі.
Для реалізації нашої моделі будемо використовувати Python і всіма улюблені бібліотеки Theano (https://pypi.python.org/pypi/Theano) і Lasagne (https://pypi.python.org/pypi/Lasagne/0.1).
Завантажимо наші дані в пам'ять (так, ми ліниві) і проведемо предобработку:
Прихований текст
import numpy as np
import pandas as pd
import theano
import theano.tensor as T
import lasagne

dataset = pd.read_csv('/home/andrw/dataset_all_2class.csv', sep = ',')
dataset.head()
chars = dataset['domain'].tolist()
chars = ".join(chars)
chars = list(set(chars)) 

print chars
# ['-', '.', '1', '0', '3', '2', '5', '4', '7', '6', '9', '8', '_', 'a', 'c', 'b', 'e', 'd', 'g', 'f', 'i', 'h', 'k', 'j', 'm', 'l', 'o', 'n', 'q', 'p', 's', 'r', 'u', 't', 'w', 'v', 'y', 'x', 'z']

classes = dataset['class'].tolist()
classes = list(set(classes))

print classes
#['dga', 'legit']

Тепер можна закодувати наш домен в послідовність і сформуємо масиви X, y, маску M. Навіщо маска потрібна? Все просто, Карл! Домени-то різної довжини.
Прихований текст
char_to_ix = { ch:i for i,ch in enumerate(chars) }
ix_to_char = { i:ch for i,ch in enumerate(chars) }
class_to_y = { cl:i for i,cl in enumerate(classes) }

NUM_VOCAB = len(chars)
NUM_CLASS = len(classes)
NUM_CHARS = 75

N = len(dataset.index)
X = np.zeros((N, NUM_CHARS)).astype('int32')
M = np.zeros((N, NUM_CHARS)).astype('float32')
Y = np.zeros(N).astype('int32')

for i, r in dataset.iterrows():
inputs = [char_to_ix[ch] for ch in r['domain']]
length = len(inputs)
X[i,:length] = np.array(inputs)
M[i,:length] = np.ones(length)
Y[i] = class_to_y[r['class']]

Сформуємо тренувальну і тестову вибірку:
Прихований текст
rand_indx = np.random.randint(N, size=N)
X = X[rand_indx,:]
M = M[rand_indx,:]
Y = Y[rand_indx]

Ntrain = int(N * 0.75)
Ntest = N - Ntrain

Xtrain = X[:Ntrain,:]
Mtrain = M[:Ntrain,:]
Ytrain = Y[:Ntrain]

Xtest = X[Ntrain:,:]
Mtest = M[Ntrain:,:]
Ytest = Y[Ntrain:]

Тепер все готово, щоб описати архітектуру нашої мережі, яка виглядає, як показано на рисунку. Для класифікації ми будемо передавати стан останнього прихованого шару в Softmax шар, вихід якого ми можемо інтерпретувати як ймовірності приналежності домену до одного з класів (шкідливих або легітимних).

Прихований текст
BATCH_SIZE = 100
NUM_UNITS_ENC = 128

x_sym = T. imatrix()
y_sym = T. ivector()
xmask_sym = T. matrix()

Tdata = np.random.randint(0,10,size=(BATCH_SIZE, NUM_CHARS)).astype('int32')
Tmask = np.ones((BATCH_SIZE, NUM_CHARS)).astype('float32')
l_in = lasagne.layers.InputLayer((None, None))
l_emb = lasagne.layers.EmbeddingLayer(l_in, NUM_VOCAB, NUM_VOCAB, name='Embedding')
l_mask_enc = lasagne.layers.InputLayer((None, None))
l_enc = lasagne.layers.GRULayer(l_emb, num_units=NUM_UNITS_ENC, name='GRUEncoder', mask_input=l_mask_enc)
l_last_hid = lasagne.layers.SliceLayer(l_enc, indices=-1, axis=1, name='LastState')
l_softmax = lasagne.layers.DenseLayer(l_last_hid, num_units=NUM_CLASS, nonlinearity=lasagne.nonlinearities.softmax, name='SoftmaxOutput')

output_train = lasagne.layers.get_output(l_softmax, inputs={l_in: x_sym, l_mask_enc: xmask_sym}, deterministic=False)

total_cost = T. nnet.categorical_crossentropy(output_train, y_sym.flatten())
mean_cost = T. mean(total_cost)

#accuracy function
argmax = T. argmax(output_train, axis=-1)
eq = T. eq(argmax,y_sym)
acc = T. mean(eq)

all_parameters = lasagne.layers.get_all_params([l_softmax], trainable=True)

all_grads = T. grad(mean_cost, all_parameters)
all_grads_clip = [T. clip(g,-1,1) for g in all_grads]
all_grads_norm = lasagne.updates.total_norm_constraint(all_grads_clip, 1)

updates = lasagne.updates.adam(all_grads_norm, all_parameters, learning_rate=0.005)
train_func_a = theano.function([x_sym, y_sym, xmask_sym], mean_cost, updates=updates)
test_func_a = theano.function([x_sym, y_sym, xmask_sym], acc

Навчаємо нашу модель, розбивши вибірку на батчи по 100 доменних імен. В результаті, отримуємо наступний графік:

На закінчення можна сказати, що отримана модель анітрохи не поступається алгоритмом Random Forest, а навіть перевершує його. Крім цього, нашу модель можна вдосконалювати і далі, наприклад, додавши в неї реверсивний прохід по доменному імені або включивши в модель механізм уваги (Attention LSTM). Ну а для теми машинного навчання у інформаційної безпеки все тільки починається :)

References

Абакумов Андрій, Digital Security

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.