本小节通过使用隐式马尔可夫来识别XSS攻击,通过xss白名训练马尔可夫模型,然后用马尔可夫模型测试xss攻击黑名单,这样。单示例隐式马尔可夫在网络安全中的使用。
1、参数建模
对于XSS攻击,首先我们需要对数据进行泛化,比如:
- [a-zA-Z]泛化为A
- [0-9]泛化为N
- [-_]泛化为C
- 其他字符泛化为T
根据如上原则admin123泛化为AAAAANNN,root泛化为AAAA。
如下图所示,url参数wjq_2014为例,对应泛化为AAACNNNN,隐藏序列为S1 S1 S1 S2 S3 S3 S3 S3。
正常http请求中参数的取值范围是固定的,比如userid字段有字母和特殊字符'-_'组成。以uid为例,可以对其泛化通过三阶段HMM来判断其观察序列,隐藏序列即在S1-S4之间循环转化,这个概率成为转移概率矩阵。同时四个状态都有确定的概率,以观察序列中的A、C、N、T共4个状态展现,这个概率即为发射概率矩阵。HMM建模就是通过学习样本,生成两个矩阵的过程。
2、数据处理与特征提取
训练样本即为白名单,可以选择'./good-xss-10000.txt'文件即可,我们来看一下白样本的内容:
源码如下
X = [[0]]
X_lens = [1]
with open(filename, encoding='utf-8') as f:
for line in f:
line=line.strip('\n')
line=parse.unquote(line)
vers = etl(line)
X=np.concatenate( [X,vers])
X_lens.append(len(vers))
对于样本,首先我们根据第一点提到的泛化规则,需要对数据进行泛化,这里选取的原则如下:
- [a-zA-Z]泛化为A
- [0-9]泛化为N
- [-_]泛化为C
- 其他字符泛化为T
代码如下所示
def etl(str):
vers=[]
for i, c in enumerate(str):
c=c.lower()
if ord(c) >= ord('a') and ord(c) <= ord('z'):
vers.append([ord('A')])
elif ord(c) >= ord('0') and ord(c) <= ord('9'):
vers.append([ord('N')])
elif c in SEN:
vers.append([ord('C')])
else:
vers.append([ord('T')])
return np.array(vers)
3、训练模型
这里选择三阶段HMM训练白样本
remodel = hmm.GaussianHMM(n_components=3, covariance_type="full", n_iter=100)
remodel.fit(X,X_lens)
4、模型验证
(1)白样本测试
这里基于白样本特征,采用与训练相同的方法
x = []
y = []
with open(filename, encoding='utf-8') as f:
for line in f:
line=line.strip('\n')
line=parse.unquote(line)
vers = etl(line)
pro = remodel.score(vers)
x.append(len(vers))
y.append(pro)
return x,y
(2)黑样本测试
接下来看一下黑样本’xss-10000.txt’,打开文件查看下具体内容
为了避免中文等字符的干扰,ASCII大于127或者小于32的可以不处理直接跳过
SEN=['<','>',',',':','\'','/',';','"','{','}','(',')']
def ischeck(str):
if re.match(r'^(http)',str):
return False
for i, c in enumerate(str):
# 排除中文干扰 只处理127以内的字符
if ord(c) > 127 or ord(c) < 31:
return False
if c in SEN:
return True
return False
从每条黑样本中提取url参数,通过python接口解决url编码、参数抽取等问题,验证代码如下:
x = []
y = []
with open(filename, encoding='utf-8') as f:
for line in f:
# 切割参数
result = parse.urlparse(line)
# url解码
query = parse.unquote(result.query)
params = parse.parse_qsl(query, True)
for k, v in params:
if ischeck(v) and len(v) >=N :
vers = etl(v)
pro = remodel.score(vers)
x.append(len(vers))
y.append(pro)
return x,y
5、基础函数完整代码
#-*- coding:utf-8 –*-
from urllib import parse
import re
from hmmlearn import hmm
import numpy as np
import matplotlib.pyplot as plt
import joblib
#处理参数值的最小长度
MIN_LEN=6
#其他字符
SEN=['<','>',',',':','\'','/',';','"','{','}','(',')']
def ischeck(str):
if re.match(r'^(http)',str):
return False
for i, c in enumerate(str):
# 排除中文干扰 只处理127以内的字符
if ord(c) > 127 or ord(c) < 31:
return False
if c in SEN:
return True
return False
def etl(str):
vers=[]
for i, c in enumerate(str):
c=c.lower()
if ord(c) >= ord('a') and ord(c) <= ord('z'):
vers.append([ord('A')])
elif ord(c) >= ord('0') and ord(c) <= ord('9'):
vers.append([ord('N')])
elif c in SEN:
vers.append([ord('C')])
else:
vers.append([ord('T')])
#print(vers)
return np.array(vers)
def train(filename):
X = [[0]]
X_lens = [1]
with open(filename, encoding='utf-8') as f:
for line in f:
line=line.strip('\n')
line=parse.unquote(line)
vers = etl(line)
X=np.concatenate( [X,vers])
X_lens.append(len(vers))
remodel = hmm.GaussianHMM(n_components=3, covariance_type="full", n_iter=100)
remodel.fit(X,X_lens)
joblib.dump(remodel, "xss-train1.pkl")
return remodel
def test_normal(filename):
remodel = joblib.load("xss-train1.pkl")
x = []
y = []
with open(filename, encoding='utf-8') as f:
for line in f:
line=line.strip('\n')
line=parse.unquote(line)
vers = etl(line)
pro = remodel.score(vers)
x.append(len(vers))
y.append(pro)
return x,y
def test(filename):
remodel = joblib.load("xss-train1.pkl")
x = []
y = []
with open(filename, encoding='utf-8') as f:
for line in f:
# 切割参数
result = parse.urlparse(line)
# url解码
query = parse.unquote(result.query)
params = parse.parse_qsl(query, True)
for k, v in params:
#print('k:',k,'v:',v,'line:',line)
if ischeck(v) and len(v) >=MIN_LEN :
vers = etl(v)
pro = remodel.score(vers)
x.append(len(vers))
y.append(pro)
return x,y
6、测试对比
作者配套源码乱七八糟,很多内容与书上写的内容都不一样,包括函数调用参数也没有指明(比如训练样本是哪个文件、测试样本是哪个文件),这部分包括得分pro与阈值T的值设定(书上说10,但是源码-200),也没有写明白,实验结果与书上也不一致。
(1)黑样本测试方法
这里我使用实验数据,图示对比使用test函数进行对比 测试均使用黑样本测试函数,代码如下
if __name__ == '__main__':
train('./good-xss-10000.txt')
x1,y1=test('./good-xss-10000.txt')
x2,y2=test('./xss-10000.txt')
fig,ax=plt.subplots()
ax.set_xlabel('Line Length')
ax.set_ylabel('HMM Score')
ax.scatter(x2, y2, color='g', label="xss",marker='v')
ax.scatter(x1, y1, color='r', label="good",marker='*')
ax.legend(loc='best')
plt.show()
白样本10000,黑样本10000个
11 10100
运行结果如下:
这是因为绝大多数白样本在parse.parse_qsl函数和ischeck(v) and len(v) >=MIN_LEN
处理后,得到的k和v基本上都是[],这种黑白样本之间的差别,使得大多数白样本使用test函数验证时,基本上都被过滤掉了,一万个就剩下11个左右,约剩下千分之一。
刚刚使用的是各10000个黑白样本进行测试,这里选择各200000个黑白样本再次测试。
if __name__ == '__main__':
train('./good-xss-10000.txt')
x1,y1=test('../data/good-xss-200000.txt')
x2,y2=test('../data/xss-200000.txt')
print(len(y1), len(y2))
fig,ax=plt.subplots()
ax.set_xlabel('Line Length')
ax.set_ylabel('HMM Score')
ax.scatter(x2, y2, color='g', label="xss",marker='v')
ax.scatter(x1, y1, color='r', label="good",marker='*')
ax.legend(loc='best')
plt.show()
如下所示,这20w的白样本和20w的黑样本测试结果中,白样本没过滤的仅128个,比例约千分之一。而黑样本20w也被过滤剩下14752个了,这也侧面说明这种方式不太靠谱。
128 14752
从图示来看,黑样本和没过滤掉的白样本特征也差不太多
(2)白样本测试对比
这里我使用实验数据,图示对比使用test_normal函数进行对比 测试均使用和训练相同的白样本测试函数,代码如下
f __name__ == '__main__':
train('./good-xss-10000.txt')
x1,y1=test_normal('./good-xss-10000.txt')
x2,y2=test_normal('./xss-10000.txt')
fig,ax=plt.subplots()
ax.set_xlabel('Line Length')
ax.set_ylabel('HMM Score')
ax.scatter(x2, y2, color='g', label="xss",marker='v')
ax.scatter(x1, y1, color='r', label="good",marker='*')
ax.legend(loc='best')
plt.show()
白样本10000,黑样本10000个
10000 10000
8.黑白样本测试对比3
if __name__ == '__main__':
train('./good-xss-10000.txt')
x1,y1=test_normal('./good-xss-10000.txt')
x2,y2=test('./xss-10000.txt')
fig,ax=plt.subplots()
ax.set_xlabel('Line Length')
ax.set_ylabel('HMM Score')
ax.scatter(x2, y2, color='g', label="xss",marker='v')
ax.scatter(x1, y1, color='r', label="good",marker='*')
ax.legend(loc='best')
plt.show()
白样本10000,黑样本10100个
10000 10100
本小节由于实验效果不好,作者配套源码也比较乱,资料不多,调试代码很久,主要是HMM识别效果不算上佳,但是这本书的读书目的也不是为了学完后效果一定很好。尤其是数据集、源码与书也不太一致,学习到基本的方用法即可。
后续如果有深刻的理解,再补充修改这篇笔记。