1.概述

这篇文章首先介绍了文本相似度的概念,接下来阐述了如何计算文本相似度,主要包括两个步骤:首先将文本映射为数值向量,然后计算这些向量的相似度。文章以词袋模型和余弦相似度为例,详细说明了构建文本相似度算法的过程,并提供了实验性原理开发的例子,建议在实践中可以利用第三方库来构建检测脚本或程序。

2.文本相似度概念

文本相似度,顾名思义,是用来衡量文本之间的相似程度的一个指标,相似度越高,文本就越相似。有很多业务场景应用到了文本相似度,例如信息检索,会计算与搜索文本相似的资源作为返回;文档检测,我们所熟悉的论文查重就是利用的这种相似度技术;还有本篇文章下面要介绍的sql注入,越权,路径枚举等较小的安全场景也可以应用这种技术构建解决方案。

3.实现方法

如何计算文本相似度?具体的算法有很多,各有优劣,不过大致的思路是一致的,下面先捋一下思路。

相似度是一个量化的指标,量化涉及到的就是数值计算,那么首先要做的就是将文本映射成某种数值表示,这时候就从计算文本之间的相似度转换为计算一堆数值之间的相似度,而数值就可以带入各种公式或者算法去计算,从而返回一个相似度指标。简要的说,分为两个步骤,第一步,将文本映射为某种数值表示,一般是向量,第二步,计算向量相似度,返回数值指标。第一步常用的算法有词袋模型,TF-IDF模型,Word2Vec等,第二步的算法有余弦相似度,欧式距离等基于距离的算法和神经网络计算相似度。

本篇文章以较简单的词袋模型和余弦相似度为例构建文本相似度算法,其目的是实验性原理开发,实践中我们可以使用第三方库方便快捷的开发检测脚本或程序。

词袋模型是一种将文本转化为数值向量的方法,假定现在有两段文本。

文本一:【今天又是安服仔打工的一天】

文本二:【今天你挣到可以退休的钱了吗】

首先词袋模型的做法是分词,然后将不重复的所有词语放在一个地方,词袋模型的这个名字也是源于此,形象的说就是所有词都在一个袋子里,上面的两段文本就是从这个词袋里拿出的词构建的。

用Python实现的话,分词本篇文章是使用的jieba分词库,构造这个不重复的词表可以使用python的set集合,其实分词还有一个步骤去除停用词,不过本篇文章省略了这一步,因为考虑到去掉的停用词可能会影响检测结果的准确性。

代码如下。

Word_bag方法代码如下。

效果如下。

构建好词表后,接下来就要把两段文本转换为数值向量表示。原理是统计词频,其方法是构建一个与词表相同长度的全零向量,然后遍历前面已经分好词的文本,词每出现一次,就在对应的位置上把向量加一,如下。

效果如下。

这样两段文本就转换成了相应的数值向量,从上面的代码和向量就可以看到,如果词表中的词语在文本中出现,就在对应的位置上加一,这样就统计到了全部的词语和与其出现的次数,如果这两段文本出现的全部词语是一样的,出现的次数也是一样的,那这两段文本就可以认为是高度相似的。这种方法的缺点是只计算了词频,但没有考虑先后顺序,下文再阐述先后顺序是否会对几个安全场景的检测结果有影响。
最后一步就是计算向量的余弦相似度了,如下。

效果如下。

至此,就是使用词袋模型和余弦相似度计算文本相似度的整个过程。

4.文本相似度应用于sql注入

Sql注入是我们很熟悉的一种漏洞类型,通常我们构造正确和错误的sql语法然后通过返回响应的变化来判断是否存在注入。我们期望使用正确的语法和使用错误的语法其页面的返回是不同的,这样我们就可以通过比较使用正确语法返回的页面和使用错误语法返回的页面之间的相似程度来推断是否存在sql注入。而文本相似度可以用来计算页面的相似程度, 通过设置不同的相似度阈值,甚至可以调整感知页面变化的敏感度,对于不易察觉的sql盲注,高敏感度可以很容易的感知页面的细微变化。

使用相似度检测注入首先要预处理一下返回的响应,这里关注两种类型,一种是接口,通常这种响应是文本类型,或者是json,或者是xml等等,为了尽量不改变页面的内容,提高准确性,正如前面所说,省略了去除停用词的步骤,仅去掉标点符号等一些不含实质内容的特殊符号,如下。

还有一种类型是返回html页面,同样的,将html标签等不含实质内容的符号过滤掉,如下。

这里只针对简单的html页面,如果响应是需要js动态处理变化的,这种方法大概会失效。

处理完页面后,就可以将处理好的文本带入到前面的相似度计算方法中,通过设定的相似度阈值来判断是否存在注入。

前面所说的这种计算文本相似度的方法的缺点是没有考虑词语的先后顺序,那么是否会影响检测结果呢?那就要考虑一下我们所期望的使用不同的语法所返回的两个不同的页面其不同点是否是出现的词语是完全相同的,仅是顺序不同,实际场景中,这种情况大概是不存在的,下面的两个漏洞类型同样如此,所以先后顺序在这些场景中影响应该是很小的,当然,不排除没考虑到别的情况,大家可以思考一下。

5.文本相似度应用于越权测试

越权可以分为未授权,垂直越权和水平越权三种类型,通常我们会修改请求中的权限标识符或者资源描述符,来观察是否会成功访问没有权限的资源,如果成功访问,那么返回的页面应该与权限者返回的页面是高度相似甚至是完全相同的。基于这种场景,文本相似度就可以应用到这里,与sql注入不同,越权是关注页面之间的相同点,而sql注入是关注不同点。

这里举一个应用于Burp插件中半自动扫描越权的例子。半自动指的是需要手动添加要测试的url,因为是假定前文所描述的简单场景,没有添加其它场景的处理,比如有些Url是公共资源,如果全自动,这些Url必定会返回高相似度的指标,产生很多误报,因此这个例子也是一个最基础的原型。

核心逻辑有两个,一个是手动添加Url时对url的处理,如下。

代码很简单,首先是用正则先排除js等静态资源,然后判断是否是重复url,毕竟是手动添加,最后便是存储url的操作,是放在一个hashmap中,其中path作为键名,请求主体作为键值。下面同样是用hashmap存储响应,在这里,响应存储了两种,一种是第一次发送的原始请求返回的响应,第二种如上文代码再次发送了相同的请求返回的响应。

这里的处理是考虑这样一种情况,比如增删操作,第一次原始请求删除操作返回成功响应,第二次请求会返回无数据之类的请求,在进行越权测试时,大概是属于二次请求,这时候如果拿第一次原始请求的响应作为比较,必然会返回较低的相似度指标从而产生误报,所以这里使用原始请求进行了第二次请求,然后分别与这两个响应进行相似度测试作为综合考量。

通过上面的操作就可以获取到准备要进行越权测试的url列表,接下来就是要替换权限标识符进行批量检测,这里选择权限标识符而不是资源标识符是因为考虑到旧的权限有可能失效的问题,所以我们拿新的权限来访问这些资源,避免产生不必要的错误,核心逻辑如下。

如代码所示,基本逻辑就是循环取出前文存储的请求,然后替换权限标识符,之后分别进行越权和未授权检查,检测逻辑如下。

这里的检测较为简单,就是直接进行相似度测试,前面的话多加了一步处理,如果返回500之类的错误,多加一次请求,减少误报,用处不大,在这里作为一个优化原型。

6.文本相似度应用于路径枚举

路径枚举同样是大家熟知的一种测试方法,在路径枚举中,文本相似度计算主要是用来解决这样一个问题,在响应状态码为200的响应中,不一定是有效响应的页面,很多404的页面也是响应状态码为200,其404的信息放在了响应正文中。

这种情况就可以使用相似度计算,比如一个方案就是随机访问一个不存在的页面,作为比较对象,然后对返回200响应的页面先与该页面进行相似度计算,如果高度相似则判断为误报,这种方式在自动化路径枚举中可以有效降低误报率。

原型代码举例如下。

其实还有一种场景简单说一下,就不单独分出标题了,就是黑灰产用于验证码识别的一种相似度计算应用,在黑灰产对抗图片验证码的方案中有一种就是通过对图片中的图标与目标图标进行相似度检测从而确定坐标,然后使用自动化工具模拟人类点击从而通过验证,如下这种例子。

以上就是对相似度计算原理的概述和应用于安全测试场景的几个例子,都是原型应用,希望能作为优化测试的一些小技巧。