网络空间安全和AI几乎是当下最热的两门话题了,而AI安全人才是少之又少,抱着这个想法和自己的兴趣,最近在读兜哥出的一本书:《Web安全之机器学习入门》。这几天会边读边写笔记,由于兜哥的代码都是用python2.7写的,个人比较喜欢python3.6,在写笔记的过程中可能与兜哥的源代码不符,顺便纠正一下书中的错误(试某个代码的时候一直跑错,到兜哥的GitHub上发现大家都说兜哥的代码的确写错了)。建议各位同好的朋友们去买兜哥的正版图书哈~我的笔记会省略兜哥讲到的很多姿势。以下是兜哥这本书的京东地址:https://item.jd.com/12158965.html
本文所有代码以及数据都可以在我的GitHub上下载:Capter5
K近邻算法算是最简单的机器学习算法了,也是比较重要的算法,简单易懂,书里介绍的相对简略,想更了解这个算法的朋友可以到这位大佬这里学习一下:Python3《机器学习实战》学习笔记(一):k-近邻算法(史诗级干货长文
使用K近邻算法检测异常操作(一)
黑客入侵Web服务器以后,通常会通过系统漏洞进一步提权,获得root权限。所以我们可以利用搜集来的Linux服务器的bash操作日志,通过训练识别出特定用户的操作习惯,然后进一步识别出异常操作行为。兜哥这里写的代码文件名是5-2.py,于是我这里写S的是test5_2.py。
首先是对训练数据的处理,训练数据中包括50个用户的操作日志,每个日志包含15000条操作命令,其中前5000条都是正常操作,后面的10000条日志中随机包含有异常操作。为了方便分析,数据集每100条操作作为一个操作系列,每个操作序列只要有1条异常数据就认为这个操作序列异常。
1、数据搜集和清洗
一共50个User,其中label.txt记录每个User文件操作序列是否为异常,用于测试和训练,tmp.py是我自己写的脚本……
然后我们需要逐行读取操作命令,并且每100个命令组成一个操作序列,保存在list中:
def load_user_cmd(filename):
cmd_list = [] #存储所有操作序列
dist_max = [] #存储用户使用频率最高的50个命令
dist_min = [] #存储用户使用频率最低的50个命令
dist = [] #存储所有命令
with open(filename) as f:
i = 0
x = [] #存储每个操作序列
for line in f:
line = line.strip('\n') #去掉空行
x.append(line) #组合成操作序列
dist.append(line) #添加操作命令
i+=1
if i==100:
cmd_list.append(x) #每计数100个添加操作序列
x = [] #然后将操作序列清空
i = 0
我们需要统计使用频率最高的50个命令和最低的50个命令,以判断用户的操作习惯,所以略微修改一下读入的函数:
def load_user_cmd(filename):
cmd_list = [] #存储所有操作序列
dist_max = [] #存储用户使用频率最高的50个命令
dist_min = [] #存储用户使用频率最低的50个命令
dist = [] #存储所有命令
with open(filename) as f:
i = 0
x = [] #存储每个操作序列
for line in f:
line = line.strip('\n') #去掉空行
x.append(line) #组合成操作序列
dist.append(line) #添加操作命令
i+=1
if i==100:
cmd_list.append(x) #每计数100个添加操作序列
x = [] #然后将操作序列清空
i = 0
fdist = sorted(FreqDist(dist).items(),key = operator.itemgetter(1),reverse = True) #获得操作命令使用频率并排序
dist_max = set([item[0] for item in fdist[:50]]) #取出前50个操作命令的指令
dist_min = set([item[0] for item in fdist[-50:]]) #取出前50个操作命令的指令
return cmd_list,dist_max,dist_min
使用FreqDist和operator.itemgetter之前记得首先要import一下:
from nltk.probability import FreqDist
import operator
fdist = FreqDist(dist).keys()
dist_max=set(fdist[0:50])
dist_min = set(fdist[-50:])
return cmd_list,dist_max,dist_min
然而这样获取的keys()是随机的,不是按照使用频率排序的,故后面都会出错(尽管我用兜哥的源代码仍然跑的概率和书上不同,改对后也不同)。set其实没有必要用,因为keys一定不会重复的,但是这样用也没有问题,向兜哥致敬吧,所以我没有把set去掉。
数据搜集和清洗的操作完成了,接下来将数据
特征化。
2、特征化
(1)获得每个操作序列去重后的指令个数:
f1=len(set(cmd_block))
(2)获得每个操作序列使用前10以及后10的命令(这里依旧和兜哥的源代码不同):
fdist = sorted(FreqDist(cmd_block).items(),key = operator.itemgetter(1),reverse = True)
f2 = [item[0] for item in fdist[:10]]
f3 = [item[0] for item in fdist[-10:]]
KNN只能以标量作为输入参数,所以需要将f2和f3表量化,最简单的方式就是和统计的最频繁使用的前50个命令以及最不频繁的50个命令计算重合度:
f2 = len(set(f2) & set(dist_max))
f3 = len(set(f3) & set(dist_min))
最终,特征化函数为:
def get_user_cmd_feature(user_cmd_list,dist_max,dist_min):
user_cmd_feature = []
for cmd_block in user_cmd_list:
f1 = len(set(cmd_block))
fdist = sorted(FreqDist(cmd_block).items(),key = operator.itemgetter(1),reverse = True)
f2 = [item[0] for item in fdist[:10]]
f3 = [item[0] for item in fdist[-10:]]
f2 = len(set(f2) & set(dist_max))
f3 = len(set(f3) & set(dist_min))
x = [f1,f2,f3]
user_cmd_feature.append(x)
return user_cmd_feature
3、训练数据
完成了数据读取和特征化后,我们即将开始训练数据,但在此之前,我们还需要把标志操作序列是否异常的标签读进来,提供监督学习:
def get_label(filename,index=0): #读取标签,index+1即用户编号
x=[]
with open(filename) as f:
for line in f:
line = line.strip('\n')
x.append(int(line.split()[index]))
return x
加载user3的数据,然后进行测试和训练,前N个用作训练,其余的用作测试:
#读取用户操作序列,并做数据清洗
user_cmd_list,user_cmd_dist_max,user_cmd_dist_min = load_user_cmd('../Capter5/MasqueradeDat/User3')
#将数据特征化
user_cmd_feature = get_user_cmd_feature(user_cmd_list,user_cmd_dist_max,user_cmd_dist_min)
#获得操作序列的标签
labels = get_label('../Capter5/MasqueradeDat/label.txt',2)
#label.txt中只有后100个序列的标签,前50个都是正常用户的操作序列
y = [0]*50 + labels
#x代表特征化的数据,这里取了前N个数据
x_train = user_cmd_feature[0:N]
#y代表数据的标签,这里取了前N个数据的标签
y_train = y[0:N]
#N个以后的都用作测试
x_test = user_cmd_feature[N:150]
y_test = y[N:150]
接下来要调用skearn的库来操作,首先需要import一下:
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
接下来调用KNN函数进行训练:
neigh = KNeighborsClassifier(n_neighbors = 3)
neigh.fit(x_train,y_train)
然后预测结果,并获得 准确率:
y_predict = neigh.predict(x_test)
score = np.mean(y_test == y_predict)*100
print(score)
书上说是用前120个操作序列进行训练,后30进行测试,准确率是80%,然而无论我用书上的代码还是兜哥GitHub上的源代码跑出的准确率都是100%……包括我自己修正后的代码也是100,不过将训练的数据减小到50个,我修正后的代码跑出的准确率为89%:
89.0
***Repl Closed***
OK,将所有代码整合在一起,如下:
#coding:utf-8
'''
@DateTime: 2018-01-24 14:36:02
@Version: 1.0
@Author: Unname_Bao
'''
from nltk.probability import FreqDist
import operator
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
N = 50
def load_user_cmd(filename):
cmd_list = [] #存储所有操作序列
dist_max = [] #存储用户使用频率最高的50个命令
dist_min = [] #存储用户使用频率最低的50个命令
dist = [] #存储所有命令
with open(filename) as f:
i = 0
x = [] #存储每个操作序列
for line in f:
line = line.strip('\n') #去掉空行
x.append(line) #组合成操作序列
dist.append(line) #添加操作命令
i+=1
if i==100:
cmd_list.append(x) #每计数100个添加操作序列
x = [] #然后将操作序列清空
i = 0
fdist = sorted(FreqDist(dist).items(),key = operator.itemgetter(1),reverse = True) #获得操作命令使用频率并排序
dist_max = set([item[0] for item in fdist[:50]]) #取出前50个操作命令的指令
dist_min = set([item[0] for item in fdist[-50:]]) #取出前50个操作命令的指令
return cmd_list,dist_max,dist_min
def get_user_cmd_feature(user_cmd_list,dist_max,dist_min):
user_cmd_feature = []
for cmd_block in user_cmd_list:
f1 = len(set(cmd_block))
fdist = sorted(FreqDist(cmd_block).items(),key = operator.itemgetter(1),reverse = True)
f2 = [item[0] for item in fdist[:10]]
f3 = [item[0] for item in fdist[-10:]]
f2 = len(set(f2) & set(dist_max))
f3 = len(set(f3) & set(dist_min))
x = [f1,f2,f3]
user_cmd_feature.append(x)
return user_cmd_feature
def get_label(filename,index=0): #读取标签,index+1即用户编号
x=[]
with open(filename) as f:
for line in f:
line = line.strip('\n')
x.append(int(line.split()[index]))
return x
if __name__ == '__main__':
#读取用户操作序列,并做数据清洗
user_cmd_list,user_cmd_dist_max,user_cmd_dist_min = load_user_cmd('../Capter5/MasqueradeDat/User3')
#将数据特征化
user_cmd_feature = get_user_cmd_feature(user_cmd_list,user_cmd_dist_max,user_cmd_dist_min)
#获得操作序列的标签
labels = get_label('../Capter5/MasqueradeDat/label.txt',2)
#label.txt中只有后100个序列的标签,前50个都是正常用户的操作序列
y = [0]*50 + labels
#x代表特征化的数据,这里取了前N个数据
x_train = user_cmd_feature[0:N]
#y代表数据的标签,这里取了前N个数据的标签
y_train = y[0:N]
#N个以后的都用作测试
x_test = user_cmd_feature[N:150]
y_test = y[N:150]
neigh = KNeighborsClassifier(n_neighbors = 3)
neigh.fit(x_train,y_train)
y_predict = neigh.predict(x_test)
score = np.mean(y_test == y_predict)*100
print(score)
使用K近邻算法检测异常操作(二)
之前的方法只比较了最频繁和最不频繁的操作命令,这次我们尝试一下全量比较。1、数据搜集和数据清洗
由于这次是全量化处理,所以不需要再统计频率了,函数修改成以下:def load_user_cmd_new(filename):
cmd_list = [] #存储所有操作序列
dist = [] #存储所有命令
with open(filename) as f:
i = 0
x = [] #存储每个操作序列
for line in f:
line = line.strip('\n') #去掉空行
dist.append(line) #添加操作命令
i+=1
if i==100:
cmd_list.append(x) #每计数100个添加操作序列
x = [] #然后将操作序列清空
i = 0
return cmd_list,list(set(dist))
2、特征化
特征化也很简单,对于出现过的命令置为1即可:
def get_user_cmd_feature_new(user_cmd_list,dist):
user_cmd_feature = []
for cmd_block in user_cmd_list:
v = [0]*len(dist) #v为向量,初始全为0
for i in range(len(dist)):
if dist[i] in cmd_block:
v[i] = 1 #一旦使用过某序号的命令,置为1
user_cmd_feature.append(v)
return user_cmd_feature
3、训练数据及验证
和之前类似,这次使用交叉验证,10次随机取样和验证,提高验证可信度。
#读取用户操作序列,并做数据清洗
user_cmd_list,dist = load_user_cmd_new('../Capter5/MasqueradeDat/User3')
#将数据特征化
user_cmd_feature = get_user_cmd_feature_new(user_cmd_list,dist)
#获得操作序列的标签
labels = get_label('../Capter5/MasqueradeDat/label.txt',2)
#label.txt中只有后100个序列的标签,前50个都是正常用户的操作序列
y = [0]*50 + labels
neigh = KNeighborsClassifier(n_neighbors = 3)
#交叉验证,10次随机取样,n_jobs=-1表示使用全部CPU运行
print(model_selection.cross_val_score(neigh,user_cmd_feature,y,n_jobs=-1,cv=10))
最终代码为:
#coding:utf-8
'''
@DateTime: 2018-01-25 16:22:47
@Version: 1.0
@Author: Unname_Bao
'''
from nltk.probability import FreqDist
import operator
from sklearn.neighbors import KNeighborsClassifier
from sklearn import model_selection
import numpy as np
def load_user_cmd_new(filename):
cmd_list = [] #存储所有操作序列
dist = [] #存储所有命令
with open(filename) as f:
i = 0
x = [] #存储每个操作序列
for line in f:
line = line.strip('\n') #去掉空行
dist.append(line) #添加操作命令
i+=1
if i==100:
cmd_list.append(x) #每计数100个添加操作序列
x = [] #然后将操作序列清空
i = 0
return cmd_list,list(set(dist))
def get_user_cmd_feature_new(user_cmd_list,dist):
user_cmd_feature = []
for cmd_block in user_cmd_list:
v = [0]*len(dist) #v为向量,初始全为0
for i in range(len(dist)):
if dist[i] in cmd_block:
v[i] = 1 #一旦使用过某序号的命令,置为1
user_cmd_feature.append(v)
return user_cmd_feature
def get_label(filename,index=0): #读取标签,index+1即用户编号
x=[]
with open(filename) as f:
for line in f:
line = line.strip('\n')
x.append(int(line.split()[index]))
return x
if __name__ == '__main__':
#读取用户操作序列,并做数据清洗
user_cmd_list,dist = load_user_cmd_new('../Capter5/MasqueradeDat/User3')
#将数据特征化
user_cmd_feature = get_user_cmd_feature_new(user_cmd_list,dist)
#获得操作序列的标签
labels = get_label('../Capter5/MasqueradeDat/label.txt',2)
#label.txt中只有后100个序列的标签,前50个都是正常用户的操作序列
y = [0]*50 + labels
neigh = KNeighborsClassifier(n_neighbors = 3)
#交叉验证,10次随机取样,n_jobs=-1表示使用全部CPU运行
print(model_selection.cross_val_score(neigh,user_cmd_feature,y,n_jobs=-1,cv=10))