├── lib
├── __init__.py
├── connectionPool.py
├── report.py
├── cmdline.py
└── common.py
├── report
└── .gitignore
├── targets
└── .gitignore
├── crawler_logs
└── .gitignore
├── scripts
├── __init__.py
├── web_is_admin.py
├── webserver_nginx_apache_map_root.py
├── webserver_nginx_apache_map_home.py
├── webserver_java_web_inf.py
├── web_wordpress_backup_file.py
├── common_sensitive_folders.py
├── web_outlook_web_app.py
├── web_zabbix_jsrpc_sqli.py
├── web_discuz_backup_file.py
├── web_struts_s0245_remote_code_execution.py
├── common_scan_by_hostname_or_folder.py
└── common_log_files.py
├── rules
├── disabled
│ └── .gitignore
├── other.txt
├── pocs.txt
├── 5.possible_flash_xss.txt
├── black.list
├── white.list
├── 4.web_editors.txt
├── 3.phpinfo_and_test.txt
├── 1.common_set.txt
└── 2.backup_files.txt
├── requirements.txt
├── dict
├── linux_root.txt
├── linux_home.txt
└── java_web_inf.txt
├── .gitignore
├── README.md
├── LICENSE
└── BBScan.py
/lib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/report/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/targets/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/crawler_logs/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scripts/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rules/disabled/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rules/other.txt:
--------------------------------------------------------------------------------
1 | /file:///etc/passwd {tag="root:x:"} {root_only}
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | BeautifulSoup4>=4.3.2
2 | py2-ipaddress>=3.4.1
3 | dnspython>=1.15.0
4 | urllib3
5 | pymongo
6 | requests
--------------------------------------------------------------------------------
/dict/linux_root.txt:
--------------------------------------------------------------------------------
1 | /etc/passwd {tag="root:x:"}
2 | /proc/meminfo {tag="MemTotal"} {status=200} {root_only}
3 | /etc/profile {tag="/etc/profile.d/*.sh"} {status=200} {root_only}
--------------------------------------------------------------------------------
/rules/pocs.txt:
--------------------------------------------------------------------------------
1 | /javax.faces.resource.../WEB-INF/web.xml.jsf {status=200} {type="xml"} {tag="= 0:
10 | save_user_script_result(self, self.index_status, self.base_url + '/',
11 | 'Admin Site Found')
12 | break
13 |
--------------------------------------------------------------------------------
/scripts/webserver_nginx_apache_map_root.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | '''
3 | 在配置 Nginx 和 Apache 的时候错误的将目录映射到了 /
4 | 主要检测 /etc/passwd
5 | '''
6 |
7 | from lib.common import save_user_script_result
8 |
9 |
10 | def do_check(self, prefix):
11 | if prefix != "/": return
12 | status, headers, html_doc =self._http_request('//etc/passwd')
13 | cur_content_type = headers.get('content-type', '')
14 | if html_doc.find("root:x:") >= 0:
15 | rules = self._load_rules("./dict/linux_root.txt")
16 | for rule in rules:
17 | full_url = prefix + rule[0]
18 | self._enqueue_request(prefix, full_url, rule)
--------------------------------------------------------------------------------
/scripts/webserver_nginx_apache_map_home.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | '''
3 | 在配置 Nginx 和 Apache 的时候错误的将目录映射到了 home
4 | 主要检测bash_history
5 | '''
6 |
7 | from lib.common import save_user_script_result
8 |
9 |
10 | def do_check(self, prefix):
11 | if prefix != "/": return
12 | status, headers, html_doc =self._http_request('/.bash_history')
13 | cur_content_type = headers.get('content-type', '')
14 | if status == 206 and cur_content_type.find("application/octet-stream") >= 0:
15 | rules = self._load_rules("./dict/linux_home.txt")
16 | for rule in rules:
17 | full_url = prefix + rule[0]
18 | self._enqueue_request(prefix, full_url, rule)
--------------------------------------------------------------------------------
/rules/5.possible_flash_xss.txt:
--------------------------------------------------------------------------------
1 | /ZeroClipboard.swf {status=206} {type="flash"}
2 | /zeroclipboard.swf {status=206} {type="flash"}
3 | /swfupload.swf {status=206} {type="flash"}
4 | /swfupload/swfupload.swf {status=206} {type="flash"}
5 | /open-flash-chart.swf {status=206} {type="flash"}
6 | /uploadify.swf {status=206} {type="flash"}
7 | /flowplayer.swf {status=206} {type="flash"}
8 | /Jplayer.swf {status=206} {type="flash"}
9 | /extjs/resources/charts.swf {status=206} {type="flash"}
--------------------------------------------------------------------------------
/scripts/webserver_java_web_inf.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | '''
3 | Nginx 在解析静态文件时,把 web-inf 目录映射进去,若没有做 nginx 相关安全配置或由
4 | 于 nginx 自身缺陷影响,将导致通过 nginx 访问到 tomcat 的 web-inf 目录。
5 | '''
6 |
7 | from lib.common import save_user_script_result
8 |
9 |
10 | def do_check(self, prefix):
11 | if prefix != "/": return
12 | #if self.lang != 'java': return
13 | rules = self._load_rules("./dict/java_web_inf.txt")
14 |
15 | rule = rules[0]
16 | full_url = prefix.rstrip('/') + rule[0]
17 | url_description = {'prefix': prefix, 'full_url': full_url}
18 | item = (url_description, rule[1], rule[2], rule[3], rule[4], rule[5], rule[6], rule[7])
19 | valid_item, status, headers, html_doc = self.apply_rules(item)
20 |
21 | if valid_item:
22 | for rule in rules:
23 | full_url = prefix + rule[0]
24 | self._enqueue_request(prefix, full_url, rule)
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | *.egg-info/
22 | .installed.cfg
23 | *.egg
24 | .idea/
25 |
26 | # PyInstaller
27 | # Usually these files are written by a python script from a template
28 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
29 | *.manifest
30 | *.spec
31 |
32 | # Installer logs
33 | pip-log.txt
34 | pip-delete-this-directory.txt
35 |
36 | # Unit test / coverage reports
37 | htmlcov/
38 | .tox/
39 | .coverage
40 | .coverage.*
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 | *,cover
45 |
46 | # Translations
47 | *.mo
48 | *.pot
49 |
50 | # Django stuff:
51 | *.log
52 |
53 | # Sphinx documentation
54 | docs/_build/
55 |
56 | # PyBuilder
57 | target/
58 | *.html
59 |
--------------------------------------------------------------------------------
/rules/black.list:
--------------------------------------------------------------------------------
1 | # text to exclude in html doc
2 | # regex can be used
3 | # 匹配的条目将被丢弃
4 |
5 |
6 | {text="/404/search_children.js"}
7 |
8 | {text="qzone.qq.com/gy/404/data.js"}
9 |
10 | {text="访问的页面不存在"}
11 |
12 | {text="404 Not Found"}
13 |
14 | {text="
The server encountered an internal error or"}
15 |
16 | {text="http://www.qq.com/babygohome/?pgv_ref=404"}
17 |
18 | {text="
410 Gone
"}
19 |
20 | {regex_text="controller.*not found"}
21 |
22 | {text="404 Page Not Found"}
23 |
24 | {text="You do not have permission to get URL"}
25 |
26 | {text="403 Forbidden"}
27 |
28 | {text="Whoops, looks like something went wrong.
"}
29 |
30 | {text="invalid service url:"}
31 |
32 | {text="You don't have permission to access this page"}
33 |
34 | {text="当前页面不存在或已删除"}
35 |
36 | {text="No direct script access allowed"}
37 |
38 | {text="args not correct"}
39 |
40 | {text="Controller Not Found"}
41 |
42 | {text="url error"}
43 |
44 | {text="Bad Request"}
45 |
46 | {text="http://appmedia.qq.com/media/flcdn/404.png"}
47 |
--------------------------------------------------------------------------------
/rules/white.list:
--------------------------------------------------------------------------------
1 | # text to search in doc
2 | # regex can be used
3 |
4 | # 匹配的条目将被立即标记命中
5 |
6 |
7 | {text="Index of"}
8 |
9 | {text="phpMyAdmin"}
10 |
11 | {text="allow_url_fopen"}
12 |
13 | {text="MemAdmin"}
14 |
15 | {text="This is the default start page for the Resin server"}
16 |
17 | # {text="Apache Tomcat"}
18 |
19 | {text="request_uri"}
20 |
21 | {text="Login to Cacti"}
22 |
23 | {text="Zabbix"}
24 |
25 | {text="Dashboard [Jenkins]"}
26 |
27 | {text="Graphite Browser"}
28 |
29 | {text="http://www.atlassian.com/software/jira"}
30 |
31 | # {regex_text=" on line "}
44 |
45 | {text="The proxy server could not handle the request"}
46 |
47 | {regex_text=".*后台.*"}
48 |
49 | {regex_text=".*管理.*"}
50 |
51 |
--------------------------------------------------------------------------------
/rules/4.web_editors.txt:
--------------------------------------------------------------------------------
1 |
2 | # Web Editors
3 |
4 |
5 | /fckeditor/_samples/default.html {tag="FCKeditor"} {type="html"}
6 | /ckeditor/samples/ {tag="CKEditor Samples"}
7 | /editor/ckeditor/samples/ {tag="CKEditor Samples"}
8 | /ckeditor/samples/sample_posteddata.php {tag="http://ckeditor.com"} {lang="php"}
9 | /editor/ckeditor/samples/sample_posteddata.php {tag="http://ckeditor.com"} {lang="php"}
10 | /fck/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.php {status=200} {type="html"} {tag="init_spell()"} {lang="php"}
11 | /fckeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellcheckder.php {status=200} {type="html"} {tag="init_spell()"} {lang="php"}
12 |
13 |
14 | # ueditor SSRF
15 |
16 | /ueditor/ueditor.config.js {status=200} {tag="window.UEDITOR_HOME_URL"}
17 | /ueditor/php/getRemoteImage.php {tag="'tip':'"} {status=200} {lang="php"}
18 |
19 |
--------------------------------------------------------------------------------
/dict/linux_home.txt:
--------------------------------------------------------------------------------
1 | /.bash_history {status=206} {type="application/octet-stream"} {root_only}
2 | /.rediscli_history {status=206} {type="application/octet-stream"} {root_only}
3 | /.bashrc {status=206} {type="application/octet-stream"} {root_only}
4 | /.bash_profile {status=206} {type="application/octet-stream"} {root_only}
5 | /.bash_logout {status=206} {type="application/octet-stream"} {root_only}
6 | /.vimrc {status=206} {type="application/octet-stream"} {root_only}
7 | /.mysql_history {status=206} {type="application/octet-stream"} {root_only}
8 |
9 | # SSH
10 | /.ssh/known_hosts {status=206} {type="application/octet-stream"} {root_only}
11 | /.ssh/id_rsa {status=200} {tag="PRIVATE KEY-"} {root_only}
12 | /id_rsa {status=200} {tag="PRIVATE KEY-"} {root_only}
13 | /.ssh/id_rsa.pub {status=200} {tag="ssh-rsa"} {root_only}
14 | /.ssh/id_dsa {status=200} {tag="PRIVATE KEY-"} {root_only}
15 | /id_dsa {status=200} {tag="PRIVATE KEY-"} {root_only}
16 | /.ssh/id_dsa.pub {status=200} {tag="ssh-dss"} {root_only}
17 | /.ssh/authorized_keys {status=200} {tag="ssh-rsa"} {root_only}
18 |
--------------------------------------------------------------------------------
/scripts/web_wordpress_backup_file.py:
--------------------------------------------------------------------------------
1 | # Wordpress
2 | # /wp-config.php.inc {status=200} {tag="= 0:
16 | url_lst = ['/wp-config.php.inc',
17 | '/wp-config.inc',
18 | '/wp-config.bak',
19 | '/wp-config.php~',
20 | '/.wp-config.php.swp',
21 | '/wp-config.php.bak']
22 | for _url in url_lst:
23 | status, headers, html_doc = self._http_request(_url)
24 | print _url
25 | if status == 200 or status == 206:
26 | if html_doc.find('= 0:
27 | save_user_script_result(self, status, self.base_url + _url, 'WordPress Backup File Found')
28 |
--------------------------------------------------------------------------------
/lib/connectionPool.py:
--------------------------------------------------------------------------------
1 | import urllib3
2 | import socket
3 | import struct
4 | import logging
5 | from urllib3.packages.six.moves.queue import Empty
6 |
7 |
8 | urllib3.disable_warnings()
9 | logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.CRITICAL)
10 |
11 |
12 | class HTTPConnPool(urllib3.HTTPConnectionPool):
13 | def close(self):
14 | """
15 | Close all pooled connections and disable the pool.
16 | """
17 | # Disable access to the pool
18 | old_pool, self.pool = self.pool, None
19 |
20 | try:
21 | while True:
22 | conn = old_pool.get(block=False)
23 | if conn:
24 | conn.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
25 | conn.close()
26 | except Empty:
27 | pass
28 |
29 |
30 | class HTTPSConnPool(urllib3.HTTPSConnectionPool):
31 | def close(self):
32 | """
33 | Close all pooled connections and disable the pool.
34 | """
35 | # Disable access to the pool
36 | old_pool, self.pool = self.pool, None
37 |
38 | try:
39 | while True:
40 | conn = old_pool.get(block=False)
41 | if conn:
42 | conn.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
43 | conn.close()
44 | except Empty:
45 | pass
--------------------------------------------------------------------------------
/scripts/common_sensitive_folders.py:
--------------------------------------------------------------------------------
1 |
2 | from lib.common import save_user_script_result
3 |
4 | folders = """
5 | /admin
6 | /output
7 | /tmp
8 | /temp
9 | /test
10 | /conf
11 | /config
12 | /db
13 | /database
14 | /install
15 | /open-flash-chart
16 | /jPlayer
17 | /jwplayer
18 | /extjs
19 | /boss
20 | /ckeditor
21 | /cgi-bin
22 | /.ssh
23 | /ckfinder
24 | /.git
25 | /.svn
26 | /editor
27 | /bak
28 | /fck
29 | /.idea
30 | /swfupload
31 | /kibana
32 | /monitor
33 | /htmedit
34 | /htmleditor
35 | /ueditor
36 | /resin-doc
37 | /resin-admin
38 | /tomcat
39 | /zabbix
40 | /WEB-INF
41 | /WEB-INF/classes
42 | /manage
43 | /manager
44 | /test
45 | /temp
46 | /tmp
47 | /cgi-bin
48 | /deploy
49 | /backup
50 | """
51 |
52 |
53 | def do_check(self, url):
54 | if url != '/' or not self.conn_pool or self._404_status == 301:
55 | return
56 |
57 |
58 | _folders = folders.split()
59 |
60 | for _url in _folders:
61 | status, headers, html_doc = self._http_request(_url)
62 |
63 | if status in (301, 302):
64 | location = headers.get('location', '')
65 | if location.startswith(self.base_url + _url + '/') or location.startswith(_url + '/'):
66 | save_user_script_result(self, status, self.base_url + _url,
67 | 'Possible Sensitive Folder Found')
68 |
69 | if status == 206 and self._404_status != 206:
70 | save_user_script_result(self, status, self.base_url + _url,
71 | 'Possible Sensitive File Found')
72 |
73 |
--------------------------------------------------------------------------------
/scripts/web_outlook_web_app.py:
--------------------------------------------------------------------------------
1 | # Exchange Outlook Web APP
2 | # /owa/ {status=302} {tag="/owa/auth/logon.aspx"}
3 |
4 | import httplib
5 | from lib.common import save_user_script_result
6 |
7 |
8 | def do_check(self, url):
9 | if url == '/' and self.conn_pool and self.server == 'iis':
10 | if self.index_status == 302 and self.index_headers.get('location', '').lower() == 'https://%s/owa' % self.host:
11 | save_user_script_result(self, 302, 'https://%s' % self.host, 'OutLook Web APP Found')
12 | return
13 |
14 | status, headers, html_doc = self._http_request('/ews/')
15 |
16 | if status == 302:
17 | redirect_url = headers.get('location', '')
18 | if redirect_url == 'https://%shttp://%s/ews/' % (self.host, self.host):
19 | save_user_script_result(self, 302, 'https://%s' % self.host, 'OutLook Web APP Found')
20 | return
21 | if redirect_url == 'https://%s/ews/' % self.host:
22 | try:
23 | conn = httplib.HTTPSConnection(self.host)
24 | conn.request('HEAD', '/ews')
25 | if conn.getresponse().status == 401:
26 | save_user_script_result(self, 401, redirect_url, 'OutLook Web APP Found')
27 | conn.close()
28 | except:
29 | pass
30 | return
31 |
32 | elif status == 401:
33 | if headers.get('Server', '').find('Microsoft-IIS') >= 0:
34 | save_user_script_result(self, 401, self.base_url + '/ews/', 'OutLook Web APP Found')
35 | return
36 |
--------------------------------------------------------------------------------
/rules/3.phpinfo_and_test.txt:
--------------------------------------------------------------------------------
1 |
2 | /phpinfo.php {tag="allow_url_fopen"} {status=200} {type="html"} {lang="php"}
3 | /info.php {tag="allow_url_fopen"} {status=200} {type="html"} {lang="php"}
4 | /pi.php {tag="allow_url_fopen"} {status=200} {type="html"} {lang="php"}
5 | /i.php {status=200} {type="html"} {lang="php"}
6 | /php.php {status=200} {type="html"} {lang="php"}
7 | /mysql.php {status=200} {type="html"} {lang="php"}
8 | /sql.php {status=200} {type="html"} {lang="php"}
9 | /shell.php {status=200} {type="html"} {lang="php"}
10 | /apc.php {status=200} {tag="APC INFO"} {lang="php"}
11 |
12 |
13 | /test.php {status=200} {type="html"} {lang="php"}
14 | /test2.php {status=200} {type="html"} {lang="php"}
15 | /test.html {status=200} {type="html"}
16 | /test2.html {status=200} {type="html"}
17 | /test.txt {status=200} {type="text/plain"}
18 | /test2.txt {status=200} {type="text/plain"}
19 | /debug.php {status=200} {type="html"} {lang="php"}
20 | /a.php {status=200} {type="html"} {lang="php"}
21 | /b.php {status=200} {type="html"} {lang="php"}
22 | /t.php {status=200} {type="html"} {lang="php"}
23 |
24 | /x.php {status=200} {type="html"} {lang="php"}
25 | /1.php {status=200} {type="html"} {lang="php"}
26 |
27 |
28 | # Test CGI {tag="SERVER_NAME"}
29 | #/test.cgi {status=200} {type="html"} {root_only}
30 | #/test-cgi {status=200} {type="html"} {root_only}
31 | #/cgi-bin/test-cgi {status=200} {type="html"} {root_only}
32 |
33 |
--------------------------------------------------------------------------------
/scripts/web_zabbix_jsrpc_sqli.py:
--------------------------------------------------------------------------------
1 | # Wordpress
2 | # /wp-config.php.inc {status=200} {tag="
8 |
9 | BBScan Report
10 |
11 |
22 |
23 |
24 | Please consider to contribute some rules to make BBScan more efficient. BBScan v 1.3
25 | Current Scan finished in ${cost_min} min ${cost_seconds} seconds.
26 | ${content}
27 |
28 |