URL发现数据清洗

  • A+
所属分类:安全文章

0x00 前言

URL发现数据清洗


首先,解释一下什么是URL发现数据。

目前很多甲方公司都已经部署或者正在部署全流量分析平台,用于安全威胁识别,用户行为分析(UBA)、资产发现等等。在资产发现的过程中,我们有效地发现了许多未经过正常流程上线的域名,同时也发现了很多URL,这就是我们所说的URL发现数据。


做渗透测试的小伙们都知道,在渗透测试的过程中很多测试方法都是通过修改参数值进行的,而爬虫和手工进行页面上的点击都不能触发所有的请求(例如一些需要特定条件的触发),同时我们也没有一个统一的API网关,来十分全面的收集到所有URL,所以我们希望能使用URL发现对测试URL目标做一定地补充。

但是问题来了。

由于URL发现数据存在很多静态页面伪静态页面以及百科类的URL,因其数量庞大,占了很大一部分比重,而且人工过滤十分耗时,无法方便地进行后续的安全测试。

为了更加有效地对收集到的URL数据进行安全分析和测试,本文尝试结合字典树聚类和机器学习两种方法对URL发现数据进行清洗

首先,来贴上我们本次实现的结果统计:

 1URL清洗 - 不包含参数值 只关心除参数值之外的URI
2现在只有返回200的数据, 其他的尚未收集,如果收集的话可以扩充字典,提高精度
3共有记录390462条,去重后减少约50% = 208554
4经过聚类清洗减少到32502 ,清洗约90%冗余数据 ,共耗时:331.672039032秒
5经过机器学习识别减少7375 - > 25024
6清洗后URL数量剩余:6.408%
7共清洗冗余数据:93.591%
8聚合规则共:323条
9REG LEN : 323
10TOTAL DATA COUNT : 390462 ,AFTER SOLVE :208554 , CLUSTERED:176052 , UNCLUSTERED:32502
11Time escape : 331.672039032

URL发现数据清洗

0x01  数据预处理

下面是通过全流量镜像收集的URL发现数据 ,时间为2018-02 , 共计390462条.

从MySql数据库中Dump出来指定字段后使用Python进行预处理,处理后结果格式如下:

 1http://yqr.xxx.com/views/k/
2http://yqr.xxx.com/views/jsreadme
3http://yqr.xxx.com/views/jzzjj
4http://b1-uat.yyy.com/validationCode/createValidationCode4ForgetPassword.do
5http://b1-uat.yyy.com/ef_pub_product/forwardHomePage.do
6http://uuu.ccc.com/giftApply/37398
7http://uuu.ccc.com/giftApply/37398
8http://uuu.ccc.com/giftApply/93122
9http://uuu.ccc.com/giftApply/93122
10http://uuu.ccc.com/giftApply/47931
11http://uuu.ccc.com/giftApply/47442
12http://uuu.ccc.com/giftApply/47442
13http://uuu.ccc.com/giftApply/47931
14http://www.ddd.cn/database.tar
15http://www.ddd.cn/.stats/awstats.pl
16http://www.ddd.cn/www_db.tar.gz
17http://www.ddd.cn/www-dump.zip
18http://www.ddd.cn/backup-www.zip
19http://www.ddd.cn/www_database.tar.gz
20http://www.ddd.cn/backupwww.zip
21http://www.ddd.cn/www-backup.tar.gz
22http://www.ddd.cn/www-db.zip

处理后将URI部分提取,通过正则表达式匹配URL中的host部分删除掉,加上表头,作为机器学习的训练数据

正则表达式(网上找的):

其实不用正则也行,匹配到http://后面的第一个/就可以,这里直接使用编辑器通过正则表达式直接匹配删了。

1((([A-Za-z]{3,9}:(?://)?)(?:[-;:&=+$,w][email protected])?[A-Za-z0-9.-]+(:[0-9]+)?|(?:ww‌w.|[-;:&=+$,w][email protected])[A-Za-z0-9.-]+)((?:/[+~%/.w-_]*)???(?:[-+=&;%@.w_]*)#?‌(?:[w]*))?)

匹配结果:

URL发现数据清洗

处理后结果格式如下:

1uri|
2application/login
3api/marketQuartz/tradeMatch.ajax
4api/marketQuartz/createMarketJob.ajax
5api/sso/bank/bankQuery.ajax

uri后面的|是自定义的分隔符,csv格式里的分隔符默认为,(逗号)但是URL里出现很多,,这里是为了避免冲突。

训练数据(少得可怜):

正样本为从聚合后数据中手工随机挑选具有代表性的数据,约800

负样本为Github上自寻的XSS+SQLi Payload和敏感目录字典,约300

到这里,数据就准备好了。
下面,我们开始科普阶段。

URL发现数据清洗

0x02 字典树与聚类

字典树:
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

URL发现数据清洗

同时,Trie树可以利用字符串的公共前缀来节约存储空间

例如:{he,her,his,hi}四个单词可以表示为:

URL发现数据清洗

其中每个蓝色的节点都可以表示一个单词,只使用5个节点的空间即可表示4个单词共10个字母。

而我们这里使用了一个字典树的变形,使用了一个树形结构,将/,=,?作为节点的分割符,然后将同一深度的节点进行规则匹配。如下图所示,将article/123,article/456,article/789使用正则表达式d+匹配。

URL发现数据清洗

举例说明,

我们现在有URL数据如下:

 1'http://example.com/cat/sports',
2'http://example.com/cat/tech',
3'http://example.com/cat/life',
4'http://example.com/cat/politics',
5'http://example.com/article/123/tag1',
6'http://example.com/article/456/tag2',
7'http://example.com/article/789/tag3',
8'http://example.com/article/?id=1',
9'http://example.com/article/?id=2',
10'http://example.com/article/?id=3',

执行聚类过程后:

 1REGEX: http://example.com/cat/([^/]+)
2URLS:
3    http://example.com/cat/sports
4    http://example.com/cat/tech
5    http://example.com/cat/life
6    http://example.com/cat/politics
7REGEX: http://example.com/tag/(d+)/([^/]+)
8URLS:
9    http://example.com/tag/123/tag1
10    http://example.com/tag/456/tag2
11    http://example.com/tag/789/tag3
12REGEX: http://example.com/article/??id=(d+)
13URLS:
14    http://example.com/article/?id=1
15    http://example.com/article/?id=2
16    http://example.com/article/?id=3
17UNCLUSTERED URLS:
18    http://example.com
19    http://example.com/about
20    http://example.com/contact
21

我们可以在聚类的过程中设置一个阈值,例如,阈值为2,代表匹配大于2个才进行聚类,来调整聚类的精度。这里的实现源码在参考资料最后一个,原项目已经三年没更新了,我这里Fork了一份,并解决了一下中文编码问题。

在测试数据中执行的部分结果:

1MATCHED URLS:
2963
3REGEX: http://yqr.yiqifin.com/views/([^/]+)/([^/]+)
4MATCHED URLS:
55844
6REGEX: http://www.vooc.com.cn/course/(d+)/reviews/

生成的正则表达式会在平台展示,然后人工进行规则审计,下面是我们的平台部分测试结果截图:

URL发现数据清洗

至此已经未清洗URL数量为32502条,下面我们使用机器学习的方法来继续清洗(这里我们参考Web攻击检测的方法,使用TF-IDF+决策树,详情可跳转到最后的参考资料).
首先,又是一段科普:

URL发现数据清洗

0x03 使用TF-IDF进行特征提取


TF-IDF(term frequency–inverse document frequency)是一种用于资讯检索与资讯探勘的常用加权技术。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜寻引擎应用,作为文件与用户查询之间相关程度的度量或评级。

TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上是:TF * IDF,TF词频(Term Frequency),IDF反文档频率(Inverse Document Frequency)。

TF(t) = Number of times term t appears in a document / Total number of terms in the document

URL发现数据清洗

TF表示词条在文档d中出现的频率(另一说:TF词频(Term Frequency)指的是某一个给定的词语在该文件中出现的次数)。
以上式子中 n_{i,j} 是该词 t_{i}  在文件d_{j}中的出现次数,而分母则是在文件d_{j}中所有字词的出现次数之和。

URL发现数据清洗

|D|:语料库中的文件总数

|{ j: t_{i} in d_{j}}| :包含词语 t_{i} 的文件数目(即 n_{i,j} neq 0的文件数目)如果该词语不在语料库中,就会导致被除数为零,因此一般情况下使用1 + |{j : t_{i} in d_{j}}|

IDF的主要思想是:如果包含词条t的文档越少,也就是n越小,IDF越大(见后续公式),则说明词条t具有很好的类别区分能力。如果某一类文档C中包含词条t的文档数为m,而其它类包含t的文档总数为k,显然所有包含t的文档数n=m+k,当m大的时候,n也大,按照IDF公式得到的IDF的值会小,就说明该词条t类别区分能力不强。(另一说:IDF反文档频率(Inverse Document Frequency)是指果包含词条的文档越少,IDF越大,则说明词条具有很好的类别区分能力。)

URL发现数据清洗

某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

代码示例:

 1# coding:utf-8
2
3from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer,TfidfVectorizer
4tv = TfidfVectorizer(analyzer='word',ngram_range={1,3})
5from sklearn.feature_extraction.text import CountVectorizer  
6
7#语料  
8corpus = [  
9    'This is the first document.',  
10    'This is the second second document.',  
11    'And the third one.',  
12    'Is this the first document?',  
13]
14
15#将文本中的词语转换为词频矩阵  
16vectorizer = CountVectorizer()  
17#计算个词语出现的次数  
18X = vectorizer.fit_transform(corpus)  
19#获取词袋中所有文本关键词  
20word = vectorizer.get_feature_names()  
21print word  
22#查看词频结果  
23print X.toarray()  

URL发现数据清洗

0x04 训练和预测代码实现

代码:

 1#coding:utf-8  
2
3import sys  
4reload(sys)  
5sys.setdefaultencoding('utf8')
6import pandas as pd
7import numpy as np
8from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer,TfidfVectorizer
9from sklearn.tree import DecisionTreeClassifier
10from sklearn.externals import joblib
11
12df = pd.read_csv('data/abnormal_request.csv',delimiter='|')
13df['label'] = 1
14df1 = pd.read_csv('data/normal_request.csv',delimiter='|')
15df1['label'] = 0
16df = df.append(df1)
17df = df.drop_duplicates()
18
19tv = TfidfVectorizer(analyzer='word',ngram_range={1,3})
20
21n_grams_tfidf = tv.fit_transform(df['uri'].values.astype('U'))
22
23clf = DecisionTreeClassifier(random_state=0).fit(n_grams_tfidf,df['label'])
24
25joblib.dump(clf,'models/url_clean.pkl')
26joblib.dump(tv,'models/url_clean.m')
27
28tv = joblib.load('models/url_clean.m')
29model = joblib.load('models/url_clean.pkl')
30
31dfd = pd.read_csv('data/uncluster.csv',delimiter='|',error_bad_lines=False)
32
33test_n_grams_tfidf = tv.transform(dfd['uri'].values.astype('U'))
34
35r =  model.predict(test_n_grams_tfidf)
36dfd['label'] = r
37dfd[dfd['label']==1].to_csv('abnormal_url.csv')
38dfd[dfd['label']==0].to_csv('cleaned_url.csv')
39print np.bincount(r)
40print np.unique(r)

执行结果格式如下:

abnormal_url:

110139,"docs/project/21//;9603""():;9877",,1
210140,"docs/project/3//;9652""():;9366",,1
310141,"docs/project/7//;9413""():;9686",,1
410142,docs//1/category/22,,1
510144,"docs/project/7//;9056""():;9545",,1
610145,docs/project/8//[email protected],,1
710147,docs/project/1*232*227*0/category/15,,1
810148,"docs/project/7//;9209""():;9199",,1
910149,docs/project/1*587*582*0/interface/207,,1

cleaned_url:

126,cupid/rest/us.do,,0
227,cupid/rest/v340.do,,0
328,cupid/rest/v430.do,,0
429,cupid/nuggets/login.do,,0
530,cupid/nuggets/logout.do,,0
631,cupid/nuggets/getGraphicCode.do,,0

URL发现数据清洗

0x05 总结和展望

本文介绍的主要还是URL聚类的一个思路,机器学习方法仅作为一个尝试,由于数据清洗和标注人工成本较大,只使用了很小一部分样本进行训练,虽然目测效果还是不错的,为了保持严谨性,后半部分的算法性能结果就先不拿出来献丑了。

大佬们常说不要觉得自己写的很LOW就不敢发出来,总有人在这个领域不熟悉,需要一些科普性的知识或是一些契机,于是,我就发出来了-__-。

URL数据清洗的工作主要在于将大量静态和伪静态的URL进行去重,意义在于将一些暴露在外部的真实API接口提炼出来,用于从外部视角的安全测试,性能测试等.
如果大家还有一些其他的应用场景或者更好的想法,欢迎随时与我们交流,不胜感激!

URL发现数据清洗

0x06 参考资料

  • http://blog.csdn.net/sangyongjia/article/details/52440063

  • http://blog.csdn.net/baimafujinji/article/details/51476117

  • http://jaq.alibaba.com/community/art/show?spm=a313e.7916646.24000001.23.84tYZT&articleid=746

  • https://juejin.im/entry/5a275e2351882503dc53937c

  • https://www.jianshu.com/p/942d1beb7fdd

  • https://github.com/lfzark/urlclustering


URL发现数据清洗

更多动态,请长按下方二维码关注我们

URL发现数据清洗



本文始发于微信公众号(宜信安全应急响应中心):URL发现数据清洗

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: