java爬虫去重_QtWebKit爬虫与去重规则

原标题:QtWebKit爬虫与去重规则

*本文作者:DX安全团队—-0d9y,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

一个漏扫程序想要检测更多的漏洞,那么他配备的爬虫一定得给力,能够爬取很多其他爬虫爬不到的链接,就有可能比其他漏扫程序检测出更多的漏洞。

基于Qtwebkit的爬虫

记得刚刚入Python爬虫的坑的时候,用的是urllib2配合thread做的一个简单的爬虫,后来学会request后就取代了urllib2,这种爬虫很简单,很高效,不过爬取不了一个页面很多的链接,比较死板,不能解析js,很多ajax链接或者通过js动态生成的网站就不容易通过程序自动去抓取,需要通过人工去重新写一个爬虫去适配抓取想要抓取的内容,所以这种爬虫肯定是不能拿来做漏扫程序的。作为一个漏扫程序,能解析js是很有必要的。阅读了解一些资料后,发现Qtwebkit很适合我们的漏扫程序,当然,谷歌出的HeadlessChrome也是可以的,等有空打算再研究。Qtwebkit是安装了Qt后内置的一个封装好的浏览器内核,先上一个官方的文档http://doc.qt.io/qt-5,我们可以通过它实现抓起ajax的链接,或者某个事件后生成的链接,js动态生成的链接等。

QtWebKit作为爬虫重点的两个类

想要利用QtWebKit,首先得明白Qtwebkit运行的机制。其中QWebView和QNetworkAccessManager类尤其重要。QWebView 相当于一个简易的浏览器空间,每次打开Url前要对浏览器进行操作或者打开Url后要对浏览器进行操作都需要重构这个类里面的函数,QNetworkAccessManager是一个网络请求管理类,将我们的浏览器和其绑定后,我们的所有浏览器执行的请求都会被这里类管理,譬如我们需要改造请求头或者获取相应头等操作都需要用到这个类,下面我们详细讲解一下这两个类。

QNetworkAccessManager 类

正如上面所说,要想用这个类首先需要让webkit内核绑定这个类,然后才可以实现请求的监控拦截,和右键审核元素里的网络监控类似。Python绑定代码为page.setNetworkAccessManager(manager)其中page是QWebPage()构建的类。

其中进行拦截监控的主要是通过重写QNetworkAccessManager类中的createRequest函数,这个函数原本的功能是创建一个网络请求,我们可以在创建请求前先记录请求的Url、请求的方式以及请求的数据。不过由于在createRequest函数的时候,还没有获取相应头,如果要特定记录返回状态码为200的链接的话就需要另外一个函数——–_finished函数,_finished函数是每一次请求结束的时候调用,传入一个已经具有相应信息的类作为参数,可以通过获取status后进行判断。判断status的进行记录,可以后期拿来检测js404漏洞等,还有返回header也需要记录,可以用来进行检测URL重定向等漏洞。

url = str(reply.url().toString())

#获得返回状态

status =reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)

status ,ok = status.toInt() Qwebview 类

这个类主要获取是QtWebKit进行解析js后获取生成的源文件还有在解析前或者解析成功后执行一些js代码等。不过这个类没有自带的网站访问超时时间,一旦对方响应的时间比较慢,会进入漫长的等待。所以要重写load函数在其中加上qtimer进行定时。load函数是每一次Qwebview进行加载页面之前就会执行的一个函数。

defload( self,url):

self.loop = QEventLoop()

timer = QTimer()

timer.setSingleShot( True)

timer.timeout.connect( self._ loadFinished)

timer.start( self.timeout * 1000)

super(Browser ,self).load(url)

self.loop.exec_()

iftimer.isActive():

timer.stop()

else:

print "Request time out:"+ str(url.url().toString())

我们可以在类的定义处加入

self.loadFinished.connect(self._loadFinished)

self.loadStarted.connect(self._loadStarted),

代表QWebKit在load成功后就会执行_loadStatred函数。在成功渲染后会执行_loadFinished函数。再load成功QWebKit渲染前,我们把confirm、alert、prompt等可能会导致有窗口弹出导致js代码不继续运行的函数给替换掉。

def_loadStarted( self):

frame = self.page().mainFrame()

frame.evaluateJava( "window.alert=function(){}")

frame.evaluateJava( "window.confirm=function(){returntrue}")

frame.evaluateJava( "window.prompt =function(){return 0}")

frame.evaluateJava( "window.open =function(){}")

然后再渲染后写_loadFinshed函数遍历所有的事件,达到抓取一些触发事件后才发起或者生成的链接。首先把a标签里的href改成onclick然后再利用js触发on事件,这样不会跳转而且可以出发href里面的js代码。之后遍历特定可能存在生成或者触发链接的事件后,通过QNetworkAccessManager类获取事件后发起的网络请求链接存到已爬取的列表。至于源代码中删除的则是通过把渲染后生成的源代码传入parse函数进行处理,抓取a链接进入等待爬起的列表,抓取表单中的action和input的数据,存入已经爬起的表单列表,后续可以进行csrf测试。

def_loadFinished(self):

self.loop.quit()

frame = self.page().mainFrame()

#事件先按一遍然后到parse处理

frame.evaluateJava('selectdom=document.querySelectorAll("a");for(var i= 0; i< selectdom.length; i ++){if(!selectdom[i].getAttribute("onclick")){selectdom[i].setAttribute("onclick",selectdom[i].getAttribute("href"))}}')

frame.evaluateJava('selectdom=document.querySelectorAll("[]");for(var i= 0; i< selectdom.length; i ++){try{selectdom[i].();}catch(err){continue;}}')

frame.evaluateJava('selectdom=document.querySelectorAll("[onchange]");for(var i= 0; i< selectdom.length; i ++){try{selectdom[i].onchange();}catch(err){continue;}}')

frame.evaluateJava('selectdom=document.querySelectorAll("[onclick]");for(var i= 0; i< selectdom.length; i ++){try{selectdom[i].onclick();}catch(err){continue;}}')

frame.evaluateJava('selectdom=document.querySelectorAll("[onfocus]");for(var i= 0; i< selectdom.length; i ++){try{selectdom[i].onfocus();}catch(err){continue;}}')

frame.evaluateJava('selectdom=document.querySelectorAll("[onmouseout]");for(var i= 0; i< selectdom.length; i ++){try{selectdom[i].onmouseout();}catch(err){continue;}}')

frame.evaluateJava('selectdom=document.querySelectorAll("[onmouseover]");for(var i= 0; i< selectdom.length; i ++){try{selectdom[i].onmouseover();}catch(err){continue;}}')爬虫的去重规则

众所周知,一个爬虫的去重规则越好越不容易爬死,爬取的效率和内容越好。因为很多网站类型其实是一样的,不过Url不一样而已,譬如freebuf中很多文章,只是文字变了,网站是id变了,做漏扫程序其实是不用重复来爬取的,因为他们后台源码都是同一个。目前我的去重处理如下:

首先把一个Url去除netloc部分后进行正则替换,把所有的数字统统都替换成{{{int}}}

之后把query部分取出来,通过{netloc:[{参数 1的名称:[参数1的内容,参数1的内容],参数2的名称:[参数2的内容,]}]}这种结构进行储存,当发现某一个neiloc中

某参数的值种类多达10重之后,这个参数就被认为是无效参数,后续去重时即便这个参数和之前的不同,也会被认定是相同的。

举几个上面规则的例子,譬如某安全网站的的某链接https://www.anquanke.com/post/id/1是一种伪静态,被第一步处理之后就变成了https://www.anquanke.com/post/id/{{{int}}}

第二部后就成了{‘www.anquanke.com/post/id/{{{int}}}’:[]}存入内存。

之后继续遇到https://www.anquanke.com/post/id/1处理后变成https://www.anquanke.com/post/id/{{{int}}}查询到内存中已经有了,于是去除。

再具一个复杂点的例子:譬如百度贴吧的http://tieba.baidu.com/home/main?un=_0d9y&fr=index&red_tag=p280448481

被第一个规则处理后就变成了http://tieba.baidu.com/home/main?un=_{{{int}}}d{{{int}}}y&fr=index&red_tag=p{{{int}}}

之后存入结构变成了{‘tieba.baidu.com/home/main’:[‘un’:[‘{{{int}}}d{{{int}}}y’],’fr’:[‘index’],’red_tag’:[‘p{{{int}}}’]]}

之后如果遇到了http://tieba.baidu.com/home/main?un=_XXXXX&fr=index&red_tag=p280448481

则会继续爬起把原来的结构的参数un中加入_XXXXX值,当爬取发现un参数变化的值高于10个,则下次匹配不会管这个参数。{‘tieba.baidu.com/home/main’:[‘un’:[‘{{{int}}}d{{{int}}}y’,’XXXXX’],’fr’:[‘index’],’red_tag’:[‘p{{{int}}}’]]}

通过这两个规则可以很有效的过滤一些伪静态,或者是一些un这类的用户昵称参数等,达到有效的去重。

另外一些小心得

用Qtwebkit想要更加的高效,可以采取多进程,因为一个page的资源是不可以共享的,所以不可以多线程。在链接爬取的过程中,建议爬取表单可以把提交按钮是什么存起来,后期做csrf会用到,还有把爬到的链接是什么请求,请求的内容,请求后的状态码和Cookie给记录一下,后期检测sql注入,xss,json劫持,404js,重定向等漏洞都是会用到的。还有请求的User-Agent的值最好在QNetworkAccessManager类中进行随机生成,避免一些防爬策略。

*本文作者:DX安全团队—-0d9y,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。 返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值