Web安全之机器学习入门读书笔记——K近邻算法

网络空间安全和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))







  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值