《Web安全之机器学习入门》笔记:第十二章 12.3 隐式马尔可夫算法识别XSS攻击(一)

本文阅读 6 分钟

本小节通过使用隐式马尔可夫来识别XSS攻击,通过xss白名训练马尔可夫模型,然后用马尔可夫模型测试xss攻击黑名单,这样。单示例隐式马尔可夫在网络安全中的使用。

对于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。

img

        正常http请求中参数的取值范围是固定的,比如userid字段有字母和特殊字符'-_'组成。以uid为例,可以对其泛化通过三阶段HMM来判断其观察序列,隐藏序列即在S1-S4之间循环转化,这个概率成为转移概率矩阵。同时四个状态都有确定的概率,以观察序列中的A、C、N、T共4个状态展现,这个概率即为发射概率矩阵。HMM建模就是通过学习样本,生成两个矩阵的过程。

img

  

训练样本即为白名单,可以选择'./good-xss-10000.txt'文件即可,我们来看一下白样本的内容:

img

源码如下 

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)

这里选择三阶段HMM训练白样本

remodel = hmm.GaussianHMM(n_components=3, covariance_type="full", n_iter=100)
    remodel.fit(X,X_lens)

(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’,打开文件查看下具体内容

img

为了避免中文等字符的干扰,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),也没有写明白,实验结果与书上也不一致。

(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

运行结果如下:

img

        这是因为绝大多数白样本在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

从图示来看,黑样本和没过滤掉的白样本特征也差不太多 

img

(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

img

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

img

本小节由于实验效果不好,作者配套源码也比较乱,资料不多,调试代码很久,主要是HMM识别效果不算上佳,但是这本书的读书目的也不是为了学完后效果一定很好。尤其是数据集、源码与书也不太一致,学习到基本的方用法即可。

后续如果有深刻的理解,再补充修改这篇笔记。

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/mooyuan/article/details/122768148
-- 展开阅读全文 --
Web安全—逻辑越权漏洞(BAC)
« 上一篇 03-13
Redis底层数据结构--简单动态字符串
下一篇 » 04-10

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复