本小节通过使用隐式马尔可夫来识别XSS攻击,通过xss白名训练马尔可夫模型,然后用马尔可夫模型测试xss攻击黑名单,这样。单示例隐式马尔可夫在网络安全中的使用。
对于XSS攻击,首先我们需要对数据进行泛化,比如:
根据如上原则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建模就是通过学习样本,生成两个矩阵的过程。
训练样本即为白名单,可以选择'./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))
对于样本,首先我们根据第一点提到的泛化规则,需要对数据进行泛化,这里选取的原则如下:
代码如下所示
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)
这里选择三阶段HMM训练白样本
remodel = hmm.GaussianHMM(n_components=3, covariance_type="full", n_iter=100) remodel.fit(X,X_lens)
这里基于白样本特征,采用与训练相同的方法
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
接下来看一下黑样本’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
#-*- 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
作者配套源码乱七八糟,很多内容与书上写的内容都不一样,包括函数调用参数也没有指明(比如训练样本是哪个文件、测试样本是哪个文件),这部分包括得分pro与阈值T的值设定(书上说10,但是源码-200),也没有写明白,实验结果与书上也不一致。
这里我使用实验数据,图示对比使用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
从图示来看,黑样本和没过滤掉的白样本特征也差不太多
这里我使用实验数据,图示对比使用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
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识别效果不算上佳,但是这本书的读书目的也不是为了学完后效果一定很好。尤其是数据集、源码与书也不太一致,学习到基本的方用法即可。
后续如果有深刻的理解,再补充修改这篇笔记。