Galaxy Lab

focus on information security

机器学习实践-DGA检测

什么是DGA

DGA(Domain Generate Algorithm域名生成算法)是一种使用时间,字典,硬编码的常量利用一定的算法生成的域名。DGA生成的域名具有为随机性,用于中心结构的僵尸网络中与C&C服务器的连接,以逃避域名黑名单检测技术。

DGA域名使用的大致流程如下:首先攻击者通过运行算法生成DGA域名,然后随机选择其中的少量域名进行注册,并将域名绑定到C&C服务器。受害者的机器被植入恶意程序后运行DGA算法生成域名,并检测域名是否可以连接,如果不能连接就尝试下一个域名,如果可以连接就选取该域名作为该恶意程序的控制端服务器域名。

DGA域名特点主要有以下几点:1)DGA域名大部分都没有注册,因为注册域名需要一定的成本,而且从DGA域名的使用流程来说只需要注册一个域名就可以达到目的。2)DGA域名具有伪随机性,与正常的域名具有一定的差别,一般正常的域名在拼写上符合一定的规律。3)DGA域名一般长度较长,长度短的域名往往已经被注册了。

DGA域名举例:

CryptoLocker(ryhqdtlbwacoikd.net)

Gameover(ocdjtg1pm9csac7cbzpx7r75a.com)

Necurs(mjlglqmatygruta.pw)

Wannacry(iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com)

xshell(xmponmzmxkxkh.com)

ccleaner(ab3d685a0c37.com)

pykspa_v2_fake(sgsqoqmyceqm.org)

基于DGA域名的特点,虽然采用传统的域名黑名单也可以检测一些已知的DGA域名,但是对于一些未知DGA域名就束手无策了。我们还可以通过域名长度,是否注册,以及注册的时间和注册信息进行判断,但是这种方式需要借助第三方信誉系统,而且效果往往也不是很理想。由于DGA域名和正常域名存在一定的差异,以及存在大量的正负样本,所以可以尝试使用机器学习来进行DGA域名的检测。

机器学习二分类算法的衡量指标

1)混淆矩阵

说到二分类问题的性能指标,我们首先会想到混淆矩阵,下面为混淆矩阵的示意图

  • FP表示预测为YES实际为NO的数量
  • TP表示预测为YES实际为YES的数量
  • FN表示预测为NO实际为YES的数量
  • TN表示预测为NO实际为NO的数量
  • Y’=TP+FP 表示预测为YES的数量
  • N’=FN+TN表示预测为NO的数量
  • Y=TP+FN表示实际为YES的数量
  • N=FP+TN表示实际为NO的数量

有了混淆矩阵我们可以很方便的计算下面的准确率,精确率,召回率等指标

2)准确率

准确率是使用最普遍和直观的指标,意义是预测准确样本的占所有样本的比例,可表示为(TP+TN)/(Y+N)

3)精确率

精确率也叫查准率,意义是所有预测正确的正类占预测为正类的比例,可表示为P=TP/Y’

4)召回率

召回率也叫查全率,意义是所有预测正确的正类占实际为正类的比例,可表示为R=TP/Y

5)特异度

特异度也称真阴性率,意义是所有预测正确的负类占实际为负的样本负类的比例,可表示为TN/N

6)误报率

误报率意义是所有预测错误的正类占实际为负类的比例,可表示为FP/N

7)漏报率

漏报率意义是所有预测错误的负类占实际为正类的比例,可表示为FN/Y

8)综合评价指标(F-Measure又称为F-Score)

精确率和召回率时常会产生矛盾的情况,需要综合考量他们,于是就有了F-Measure(又称为F-Score)。F-Measure是Precision和Recall加权调和平均F=(a*a+1)P*R/a*a*(P+R)

当参数α=1时,就是最常见的F1,也即F1=2P*R/(P+R)。可知F1综合了P和R的结果,当F1较高时则能说明试验方法比较有效。

9)ROC曲线

ROC曲线的全称叫做Receiver Operating Characteristic,常常被用来判别一个分类器的好坏程度。如下图所示:

上图引自(http://alexkong.net/2013/06/introduction-to-auc-and-roc/)

横坐标可表示为FPR=FP/N,也就是误报率

纵坐标可表示为TPR=TP/Y,也就是查全率

一些分类器得到的结果往往不是0,1这样的标签,如神经网络得到诸如0.5,0.8这样的分类结果。这时,我们人为取一个阈值比方说0.6,那么小于0.6的归为0类,大于等于0.6的归为1类,可以得到一个分类结果。同样,这个阈值我们可以取0.1或0.2等等。取不同的阈值,最后得到的分类情况也就不同。通过遍历不同的阈值,分别计算FPR和TPR,作为一个点。将这些点连接起来就组成了ROC曲线。ROC曲线越靠近左上角,分类器效果越好。

10)AUC

AUC全称Area Under ROC Curve。是一种用来度量分类模型好坏的一个标准,ROC虽然直观,但是不够量化。AUC是对ROC的量化,其值为ROC曲线所覆盖的区域面积,显然,AUC越大,分类器分类效果越好。

AUC = 1,是完美分类器,但是绝大多数预测的场合,不存在完美分类器。

0.5 < AUC < 1,优于随机猜测。这个分类器(模型)有预测价值。

AUC = 0.5,等同于随机猜测,模型没有意义。

AUC < 0.5,比随机猜测还差;但只要反着预测,就优于随机猜测。

训练数据

DGA样本数据,我们采用已知DGA生成算法生成的数据加上360 netlab开放的黑样本数据。360 netlab黑样本数据的下载链接:http://data.netlab.360.com/feeds/dga/dga.txt。

正常域名采用长度小于5的字符串组合以及alexa发布的排名前100万的网站域名,其下载地址为http://s3.amazonaws.com/alexa-static/top-1m.csv.zip

另外,在进行模型训练时,尽量保证正常域名数量和DGA域名数量在一个量级。

DGA分类识别-朴素贝叶斯

特征提取

从正常域名拼写符合一定拼写规律,DGA域名伪随机性来看,可以使用N-GRAM模型进行建模,这里我们采用2-GRAM,首先将域名当作一个字符串,去除顶级域然后进行建模。那pingan.com举例:去除顶级域后得到字符串pingan,进行2-gram处理得到字符串列表’pi’  ‘in’ ’ng’ ‘ga’ ‘an’。我们可以使用python库sklearn提供的CountVectorizer函数直接进行处理即可:

from sklearn.feature_extraction.text import CountVectorizer
ngram_vectorizer = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32)

模型工具

使用python库sklearn提供的MultinomialNB

代码片段:

from sklearn.naive_bayes import MultinomialNB

代码流程介绍:

1)将数据集划分训练集和测试集

x_train, x_test, y_train, y_test = train_test_split(X , y, test_size=0.2)

2)将数据集进行2-GRAM处理,并进行数据拟合和标准化。

count_vec = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32)
x_train = count_vec.fit_transform(x_train)
x_test = count_vec.transform(x_test)

3)生成朴素贝叶斯算法模型,并在训练集上训练,获得模型数据

mtnb = MultinomialNB()
mtnb.fit(x_train,y_train)

4)使用模型在测试集上进行预测,并打印各项分类性能指标

y_pred=mtnb.predict(x_test)
print(classification_report(y_test, y_pred)) //打印精确度,召回率 F1-score等指标
print metrics.confusion_matrix(y_test, y_pred) //打印混淆矩阵
t_probs = mtnb.predict_proba(x_test)[:,1]
t_auc = sklearn.metrics.roc_auc_score(y_test, t_probs)
print('MultinomialNB: auc = %f ' % ( t_auc)) //打印AUC

测试结果

如下图所示:精确度77%,召回率76%  f1-Score 76% , AUC 87%,效果不是很理想。

DGA分类识别-XGBoost

特征提取

同样采用2-GRAM进行处理

模型工具

使用python的xgboost库,代码片段:

import xgboost as xgb

代码流程介绍

1)将数据集划分训练集和测试集

x_train, x_test, y_train, y_test = train_test_split(X , y, test_size=0.2)

2)将数据集进行2-GRAM处理,并进行数据拟合和标准化。

count_vec = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32)
x_train = count_vec.fit_transform(x_train)
x_test = count_vec.transform(x_test)

3)生成朴素贝叶斯算法模型,并在训练集上训练,获得模型数据

model = xgb.XGBClassifier()
model.fit(x_train, y_train)

4)使用模型在测试集上进行预测,并打印各项分类性能指标

y_pred=model .predict(x_test)
print(classification_report(y_test, y_pred)) //打印精确度,召回率 F1-score等指标
print metrics.confusion_matrix(y_test, y_pred) //打印混淆矩阵
t_probs = model.predict_proba(x_test)[:,1]
t_auc = sklearn.metrics.roc_auc_score(y_test, t_probs)
print( 'XGBClassifier: auc = %f ' % ( t_auc)) //打印AUC

测试结果

如下图所示:精确度84%,召回率81%  f1-Score 81% , AUC 91.7264%,效果尚可。

DGA分类识别-神经网络多层感知机

特征提取

同样采用2-GRAM进行处理

模型工具

Keras是一个高层级的python神经网络框架,Keras后端可以在Tensorflow或者Theano上运行,Keras 是为支持快速实验而生,具有高度模块化,极简,和可扩充特性。我们选用keras进行神经网络多层感知机模型的测试。

代码流程介绍

1)将数据集进行2-GRAM处理,并进行数据拟合和标准化。然后将数据集划分训练集和测试集

ngram_vectorizer = CountVectorizer(analyzer='char', ngram_range=(2, 2),dtype=np.int32)
count_vec = ngram_vectorizer.fit_transform(X) //数据拟合和标准化
max_features = count_vec.shape[1] //计算输入维度

//划分训练集和测试集

X_train, X_test, y_train, y_test, _, label_test = train_test_split(count_vec, y,labels, test_size=0.2)
X_train, X_holdout, y_train, y_holdout = train_test_split(X_train, y_train, test_size=0.05)

2)利用keras生成神经网络多层感知机模型。这里简要介绍下其中一些概念:
激活函数activation:激活函数的主要作用是提供网络的非线性建模能力,常见的有 sigmoid函数,tanh函数,ReLU函数, ELU函数, PReLU函数,softmax函数等

损失函数loss:就是一个评分函数,用来对模型预测准确性进行打分,模型的训练就是要通过样本将损失函数最小化,常见的有:0-1损失函数 ,平方损失函数,绝对损失函数,对数损失函数等

优化器optimizer:最小化损失函数的求解方法。(常用的有最小二乘法,梯度下降法)

//创建一个Sequential模型

model = Sequential()
//添加一个全连接层,激活函数使用sigmoid,输出维度128
model.add(Dense(128, input_dim=max_features, activation='sigmoid'))
//添加一个Dropout层,用于防止过拟合
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))
//编译模型,损失函数采用对数损失函数,优化器选用adam
model.compile(loss='binary_crossentropy',optimizer='adam')

3)使用模型进行最多50轮训练,并记录训练每轮AUC的值,在相隔两轮的AUC值没有增加时退出训练

for ep in range(50):
    model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=1)
    t_probs = model.predict_proba(X_holdout)
    t_auc = sklearn.metrics.roc_auc_score(y_holdout, t_probs)//计算AUC值
    if t_auc > best_auc:
        best_auc = t_auc
        best_iter = ep
        //打印分类模型指标
        probs = model.predict_proba(X_test)
        print(classification_report(y_test, probs > .5))
        print('mlp: auc = %f ' %confusion_matrix(y_test, probs > .5))
    else:
        if (ep-best_iter) > 2:
            Break

测试结果

如下图所示:精确度99%,召回率99%  f1-Score 99% , AUC 99.9138%,效果很好。

DGA分类识别-循环神经网络单层LSTM

特征提取

简单的将域名转换为整数下标列表,然后使用keras的Embedding嵌入层转换为固定大小的向量

模型工具

我们选用keras进行循环神经网络单层LSTM的测试。

代码流程介绍

1)将数据集转换为整数下标数组的模式,并将数据划分为训练集和测试集

site_chars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
valid_chars = {x:idx+1 for idx, x in enumerate(set(''.join(site_chars)))}
max_features = len(valid_chars) + 1 //计算特征字符长度
maxlen = np.max([len(x) for x in X]) //记录最长的域名长度
X = [[valid_chars[y] for y in x] for x in X] //转换为下标数组
X = sequence.pad_sequences(X, maxlen=maxlen)//进行长度填充

//划分训练集和测试集
X_train, X_test, y_train, y_test, _, label_test = train_test_split(X, y, labels,test_size=0.2)
X_train, X_holdout, y_train, y_holdout = train_test_split(X_train, y_train, test_size=0.05)

2)利用keras生成循环神经网络单层LSTM模型:

//创建一个Sequential模型
model = Sequential()

//添加一个嵌入层,输出向量大小256,嵌入层是将正整数(下标)转换为具有固定大小的向量,
//其本质就是word2vec,底层实现是2-gram(词频)+神经网络
model.add(Embedding(max_features, output_dim=256, input_length=maxlen))

//添加长短期记忆网络LSTM,从样本中学习特征,这个是核心层
model.add(LSTM(128))

//添加Dropout层防止过拟合
model.add(Dropout(0.5))

//添加全连接层,激活函数使用'sigmoid'
model.add(Dense(1, activation='sigmoid'))

//编译模型,损失函数采用对数损失函数,优化器选用rmsprop,
//该优化器通常是面对递归神经网络时的一个良好选择
model.compile(loss='binary_crossentropy',optimizer='rmsprop')

3)使用模型进行最多25轮训练,并记录训练每轮AUC的值,在相隔两轮的AUC值没有增加时退出训练

for ep in range(25):
    model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=1)
    t_probs = model.predict_proba(X_holdout)
    t_auc = sklearn.metrics.roc_auc_score(y_holdout, t_probs)//计算AUC值
        if t_auc > best_auc:
            best_auc = t_auc
            best_iter = ep
            //打印分类模型指标
            probs = model.predict_proba(X_test)
            print(classification_report(y_test, probs > .5))
            print('Single LSTM: auc = %f ' %confusion_matrix(y_test, probs > .5))
        else:
            if (ep-best_iter) > 2:
                Break

测试结果

如下图所示:精确度99%,召回率99%  f1-Score 99% , AUC 99.9233%,效果很好。

DGA分类识别-循环神经网络多层LSTM

特征提取和模型选择同单层LSTM

代码流程介绍

1)将数据集转换为整数下标数组的模式,并将数据划分为训练集和测试集

site_chars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
valid_chars = {x:idx+1 for idx, x in enumerate(set(''.join(site_chars)))}
max_features = len(valid_chars) + 1 //计算特征字符长度
maxlen = np.max([len(x) for x in X]) //记录最长的域名长度
X = [[valid_chars[y] for y in x] for x in X] //转换为下标数组
X = sequence.pad_sequences(X, maxlen=maxlen)//进行长度填充

//划分训练集和测试集
X_train, X_test, y_train, y_test, _, label_test = train_test_split(X, y, labels,test_size=0.2)
X_train, X_holdout, y_train, y_holdout = train_test_split(X_train, y_train, test_size=0.05)

2)利用keras生成循环神经网络多层LSTM模型:

//创建一个Sequential模型
model = Sequential()

//添加一个嵌入层,输出向量大小256,嵌入层是将正整数(下标)转换为具有固定大小的向量,
//其本质就是word2vec,底层实现是2-gram(词频)+神经网络
model.add(Embedding(max_features, output_dim=256, input_length=maxlen))

//添加长短期记忆网络LSTM,从样本中学习特征,这个是核心层
model.add(LSTM(128, return_sequences=True))  //返回维度128的向量序列
model.add(LSTM(64, return_sequences=True))   //返回维度64的向量序列
model.add(LSTM(64)) //返回维度64的单个向量

//添加全连接层,激活函数使用'sigmoid'
model.add(Dense(1, activation='sigmoid'))

//编译模型,损失函数采用对数损失函数,优化器选用rmsprop,
//该优化器通常是面对递归神经网络时的一个良好选择
model.compile(loss='binary_crossentropy',optimizer='rmsprop')

4)使用模型进行最多25轮训练,并记录训练每轮AUC的值,在相隔两轮的AUC值没有增加时退出训练

for ep in range(25):
    model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=1)
    t_probs = model.predict_proba(X_holdout)
    t_auc = sklearn.metrics.roc_auc_score(y_holdout, t_probs)//计算AUC值
    if t_auc > best_auc:
        best_auc = t_auc
        best_iter = ep

        //打印分类模型指标
        probs = model.predict_proba(X_test)
        print(classification_report(y_test, probs > .5))
        print('Single LSTM: auc = %f ' %confusion_matrix(y_test, probs > .5))
    else:
        if (ep-best_iter) > 2:
            Break

测试结果

如下图所示:精确度99%,召回率99%  f1-Score 99% , AUC 99.9476%,效果很好。