机器学习与网络安全

机器学习是我大二的时候做的比较多的项目,但是之前对安全这一快做的不多,也从来没有结合过的例子。但是在较为前沿的安全研究中,利用机器学习对攻击的识别是非常热门的,比如我司的🔗XSSChop就是利用语义识别来对XSS攻击作拦截的。最近工作较为稳定一点,也不用天天刷SRC求生活了,我也想把之前的机器学习捡起来,和安全结合学习。

支持向量机

什么是支持向量机

支持向量机(support vector machines)是一种二分类模型,它的目的是寻找一个超平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解。由简至繁的模型包括:

  • 当训练样本线性可分时,通过硬间隔最大化,学习一个线性可分支持向量机;
  • 当训练样本近似线性可分时,通过软间隔最大化,学习一个线性支持向量机;
  • 当训练样本线性不可分时,通过核技巧和软间隔最大化,学习一个非线性支持向量机;

简单来说,支持向量机就是一种分类的模型,用于将样本一分为二,其中分割方式可是线性、和非线性(可理解为空间分割)
假设在一个二维线性可分的数据集中,图一A所示,我们要找到一个超平面把两组数据分开,这时,我们认为线性回归的直线或逻辑回归的直线也能够做这个分类,这条直线可以是图一B中的直线,也可以是图一C中的直线,或者图一D中的直线,但哪条直线才最好呢,也就是说哪条直线能够达到最好的泛化能力呢?那就是一个能使两类之间的空间大小最大的一个超平面。
这个超平面在二维平面上看到的就是一条直线,在三维空间中就是一个平面…,因此,我们把这个划分数据的决策边界统称为超平面。离这个超平面最近的点就叫做支持向量,点到超平面的距离叫间隔。支持向量机就是要使超平面和支持向量之间的间隔尽可能的大,这样超平面才可以将两类样本准确的分开,而保证间隔尽可能的大就是保证我们的分类器误差尽可能的小,尽可能的健壮
1

图一

支持向量机在XSS识别中的应用

机器学习的实际应用,一般是

blob.jpg

数据清洗与抽取特征

这里数据我是用的《Web安全机器学习入门》(文末有github链接)中所提供的日志数据,数据概况如下
2
这里面已经帮助我们做好了数据清洗工作,以及是完整的访问uri情况了,但是我这边根据我自己的工作经历,还需要对访问日志数据做一部分的清洗工作,如下:

  • 删除访问日志中的%0A、%0D、+ 这三个是url编码后的 “\r”,”\n”,” “,在HTML的dom元素中会无视这几个字符,将"javascr\nipt:alert(1)"识别成"javascript:alert(1)"
  • 提取参数,对参数做编码测试,若参数是存在编码情况则返回解码后的参数(这里处理参数包括标准参数?parma=xxxxparma和xxxx,在url路径中的参数,由于此处只有url数据暂时只取这两个,其实http head也是可以三方恶意传参,后续自己收集数据再改进模型),这里将参数全部解码成功后,以” “作为占位符形成新的uri 例如/think/index.php?s=a&c=aaa 处理后=>think index.php s a c aaa
  • 去除""混淆,在实际上绕过WAF的情况下,我们经常会用到window["al"+'ert']这种方式来绕过字符限制,所以这里我对需要处理的参数做了正则匹配,用于去除混淆,将window["al"+'ert'] 转化成=》 window["alert']用于后续检测

其次是特征的提取,在书本里写的对web日志特征抽取是这几个:
3
但是我这里做了稍微的修改,后续会对书本的例子作比较,我这里使用的特征是:
4
我提取的参数是 最长参数长度,敏感字符数,敏感关键字一数,敏感关键字二数,和参数混淆程度,其中敏感关键词一就是dom事件参数,例如onload,onerror等,敏感关键字二是一些敏感函数,参数混淆程度是指参数中加了%0A"\"+''"、base64等编码混淆的一个度量数据

import re
from urllib import unquote

# 正则
Base64_pattern = ""
dom_spcstr_pattern = re.compile("(%0A)|(%0D)")
char_conf_pattern = re.compile("['\"]\+[\"']")
space_pattern = re.compile("\+|(%20)|\n|\r")
evil_char_pattern = re.compile("[<>,\'\"/]")
with open("dom.txt", "r") as f:
    dom = f.readlines()
dom = "(" + ")|(".join(dom).replace("\r\n", "") + ")"
dom += "|(<script)|(src=)|(javascript:)"
evil_word_pattern1 = re.compile(dom)
evil_word_pattern2 = re.compile("(javascript:)|(eval)|\
    (fetch)|($.get)|($.post)|(xmlhttprequest)|(cookie)\
        |(document)|(src=)")

matrix = []
is_xss = []

# 去除编码混淆
def trychangecode(lists, count):
    pass


# 数据清洗与规整,将数据转化成矩阵形式
def data_clean(filename, outfile, outcount, xss=1):
    pass


def get_len(uri):
    return max([len(i) for i in uri.split(" ")])

def get_url_count(uri):
    pass

def get_evil_char(uri):
    return len(evil_char_pattern.findall(uri))

def get_evil_word1(uri):
    return len(evil_word_pattern1.findall(uri,re.IGNORECASE))

def get_evil_word2(uri):
    return len(evil_word_pattern2.findall(uri,re.IGNORECASE))
"""
得到的matrix和is_xss就是特征矩阵以及结果向量
"""

模型构造

python中利用svm,可以直接用python中机器学习的库sklearn来跑:

import pandas as pd
from sklearn import datasets, metrics, model_selection, svm
from sklearn.externals import joblib
from sklearn.metrics import classification_report

x_train, x_test, y_train, y_test = model_selection.train_test_split(matrix, is_xss, test_size=0.4, random_state=0)
clf = svm.SVC(kernel='rbf', C=3).fit(x_train, y_train)
y_pred = clf.predict(x_test)
do_metrics(y_test, y_pred)
joblib.dump(clf,"xss-svm-module.m")

效果如下:
blob.jpg
比书上给的github示例要差上一点
blob.jpg
接下来是真实数据测试

真实数据测试:

这里我拿了我之前服务器上的日志数据进行测试,放在公网上,肯定是存在被被人扫描器扫到的情况,而且加上我自己的流量绝对是真实流量,所以很有参考下,不过因为日志不全,只能测试GET请求,
blob.jpg
大概只有33729多条数据

def test():
    clf = joblib.load("xss-svm-module.m")
    filename_log = "data/load_data.txt"
    global matrix
    matrix = []
    data_clean(filename_log , 0)
    matrix = pd.DataFrame(matrix[::-1])
    matrix = matrix[[i for i in range(5)]]
    output = clf.predict(matrix)
    with open(filename_log) as f:
        data = pd.DataFrame(f.readlines())
    data_is_xss = data[output==1]
    data_is_xss.to_csv("result/xss.txt")
    data_not_xss = data[output==0]
    data_not_xss.to_csv("result/nor.txt")

运行很快,我里面实际上只有100条是xss攻击的请求,但是这里check出来了300条数据
blob.jpg
手动check了一遍,发现误报挺严重的,但是并没有漏报
blob.jpg
blob.jpg
误报请求大多是我自己程序业务上(XSS平台,会通过get方法携带一些cookie数据回来)和一些扫描器请求的十分长参数的请求,很大程度上可能是因为参数最长参数长度参数引起的,这是一个需要改进的参数。
对github示例预测,一共有接近400条数据被拦截
blob.jpg
手动check了一下,发现存在有漏报的情况,并且拦截了很多莫名其妙的请求(扫描器请求)
blob.jpg

漏报

blob.jpg

构造数据测试

TODO:

  • 自己构造大量富文本请求与长参数请求,再次训练模型

感想

总体来说,我觉得SVM这种纯二分类算法不是太适合来做XSS、等漏洞的识别,二分表示着他是属于非黑即白,但是在真实情况下,我们并不是真正意义上的非黑即白,在真实的甲方业务体系之中,很多情况是需要在基于业务完整情况下保证安全,所以在很多时候,对低威胁的请求(在SVM分类里容易和正常请求混淆)会采取业务优先的放行策略,而不是一律拦下,SVM的XSS识别可能会保证漏报率较低,但是在业务上,误报率太高是不会允许的。

代码:
🔗https://github.com/Artemis1029/sklearn_in_security
参考资料:
🔗https://www.cnblogs.com/vipyoumay/p/7560061.html
🔗《Web安全机器学习入门》

One thought on “机器学习与网络安全”

  1. Pingback: buy viagra

发表评论