├── README.md
├── build.sh
├── config
├── Config.yml
├── pc_ua.json
├── phantomjs_liunx_x64
└── phantomjs_windows.exe
├── imgs
├── five.png
├── four.png
├── help.png
├── logo.jpg
├── one.png
├── threes.png
└── two.png
├── lib
├── Controller.py
├── WebpackFind.py
├── __init__.py
├── common
│ ├── Printlog.py
│ ├── __init__.py
│ ├── cmdline.py
│ ├── downloadjs.py
│ ├── fileoperation.py
│ ├── regular.py
│ ├── toupdate.py
│ ├── urlrequest.py
│ └── utils.py
└── vuln
│ ├── __init__.py
│ └── unauthorized.py
├── main.py
├── requirements.txt
└── version.txt
/README.md:
--------------------------------------------------------------------------------
1 | # Webpackfind-信息收集工具
2 |
3 | 郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担。
4 |
5 | ## 0x01 介绍
6 |
7 | 作者:[小洲](https://github.com/xz-zone)
8 |
9 | 团队:[横戈安全团队](imgs/logo.jpg),未来一段时间将陆续开源工具,欢迎关注微信公众号:
10 |
11 | 
12 |
13 | 定位:协助红队人员快速的信息收集,测绘目标资产,寻找薄弱点。
14 |
15 | 语言:python3开发
16 |
17 | 功能:一条龙服务,只需要输入根域名即可一键化收集敏感信息。具体案例见下文。
18 |
19 | 调用:
20 | * 脚本借用了JSFinderjs内容提取脚本,感谢Threezh1作者。
21 | * 脚本借用了HaE内容提取脚本,感谢gh0stkey作者。
22 |
23 | 支持环境:Ubuntu、Centos、Windows。
24 |
25 |
26 | ## 0x02 安装
27 |
28 | 为了避免踩坑,建议安装在如下环境中
29 |
30 | * 当前用户对该目录有写权限,不然扫描结果无法生成。root权限即可
31 | * Python环境必须是3.7以上,因为使用了异步。建议VPS环境是ubuntu20,默认是python3.8。安装模块的时候切记不要走豆瓣的源
32 |
33 | ```
34 | Ubuntu 运行环境
35 | chmod 777 build.sh
36 | ./build.sh
37 |
38 | Windows模块安装
39 | python3 -m pip install -r requirements.txt
40 | ```
41 | 
42 |
43 | `python3 webpackfind.py`
44 |
45 | 
46 |
47 | ## 0x03 效果展示
48 |
49 | 自动爬取网站
50 |
51 | 
52 |
53 | 自动爬取本地文件
54 |
55 | 
56 |
57 | 读取txt循环读取url
58 |
59 | 
60 |
61 | ## 0x04 使用方法
62 |
63 | | 语法 | 功能 |
64 | | :------------------------------------------------------- | :-------------------------------------------- |
65 | | python3 webpackfind.py -u http://domain.com | 自动化遍历URL中里面js |
66 | | python3 webpackfind.py -j ./js/test.com/ | 遍历本地目录中的js文件并且格式化js文件、提取信息泄露 |
67 | | python3 webpackfind.py -f ./url.txt | 读取本地url文件批量遍历url中js |
68 | | python3 webpackfind.py -c "name=test;" -u http://domain.com | 设置自定义Cookie,场景:需要登录才能爬取js信息 |
69 | | python3 webpackfind.py -d "Authorization: bearer 123456\nContent-Type: application/json\n" -u http://domain.com | 设置自定义headers |
70 | | python3 webpackfind.py -update 1 | 检查本地`webpackfind.py`是否与github.com是否一致 |
71 |
72 | ## 0x05 版本更新
73 |
74 | 2021-08-17 初始版本提交
75 |
76 | 2021-08-18 版本整体修改为面向对象编程,自动识别是否存在其他子域名
77 | 
78 |
79 | 2021-08-19 修复build.sh 不能执行问题
80 |
81 | 2021-08-23 修复了manifest js无法读取遍历BUG,感谢 chhyx2 师傅提交的bug。
82 |
83 | 2021-08-24 优化细节,解决windows生成目录问题。
84 |
85 | 2021-08-26 优化细节,解决获取js路径问题,添加读取文本url地址进行扫描js。
86 |
87 | 2021-09-02 优化细节,解决PhantomJS给拦截问题。
88 |
89 | 2021-09-03 优化细节,解决提取域名正则问题。
90 |
91 | 2021-09-07 优化细节,解决目录名称更好查找,添加自动判断是否更新。
92 |
93 | 2021-09-22 优化细节,解决首页存在webpackjs代码问题。
94 |
95 | 2022-07-21 优化细节,解决本地js代码文件未格式化问题,优化加更新参数才检查更新。
96 |
97 | 2022-08-22 重构核心代码,兼容更多webpack版本问题。
98 |
99 | 2022-08-23 优化细节,优化批量扫描,解决写入路径问题,增加Cookies认证遍历读取。
100 |
101 | 2022-08-24 优化phantomjs文件过大问题,优化提示文字,增加协议识别模块。
102 |
103 | 2022-08-25 优化细节,增加批量扫描单线程改多线程。
104 |
105 | 2022-08-27 优化线程池问题,增加“HaE”规则库。
106 |
107 | 2022-08-30 增加进度条,自动化识别路径Url。
108 |
109 | 2022-08-31 优化线程池问题,压缩运行时间。
110 |
111 | 2022-09-01 优化整体压缩美化代码。
112 |
113 | 2022-09-06 优化匹配正则,输出文件。
114 |
115 | 2022-09-09 增加自定义herder头部。
116 |
117 | 2022-09-19 优化匹配正则。
118 |
119 | 2022-09-23 优化细节。
120 |
121 | 2022-09-28 优化细节。
122 |
123 | 2022-11-28 优化细节,匹配更多api接口。
124 |
125 | ## 0x06 反馈
126 |
127 | Webpackfind 是一个免费且开源的项目,我们欢迎任何人为其开发和进步贡献力量。
128 |
129 | * 在使用过程中出现任何问题,可以通过 issues 来反馈。
130 | * Bug 的修复可以直接提交 Pull Request 到 dev 分支。
131 | * 如果是增加新的功能特性,请先创建一个 issue 并做简单描述以及大致的实现方法,提议被采纳后,就可以创建一个实现新特性的 Pull Request。
132 | * 欢迎对说明文档做出改善,帮助更多的人使用 Webpackfind。
133 | * 贡献代码请提交 PR 至 dev 分支,master 分支仅用于发布稳定可用版本。
134 |
135 | *提醒:和项目相关的问题最好在 issues 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。*
136 |
137 | ## Stargazers over time
138 |
139 | [](https://starchart.cc/xz-zone/Webpackfind)
140 |
141 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | apt install libfontconfig1
3 | apt install python3 -y
4 | apt install python3-pip --fix-missing -y
5 | apt install python3-setuptools -y
6 | python3 -m pip install --upgrade pip
7 | python3 -m pip install -r requirements.txt
--------------------------------------------------------------------------------
/config/Config.yml:
--------------------------------------------------------------------------------
1 | rules:
2 | - rule:
3 | - color: green
4 | engine: dfa
5 | loaded: true
6 | name: Shiro
7 | regex: (=deleteMe|rememberMe=)
8 | scope: any
9 | sensitive: true
10 | - color: green
11 | engine: dfa
12 | loaded: true
13 | name: JSON Web Token
14 | regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
15 | scope: any
16 | sensitive: true
17 | - color: green
18 | engine: dfa
19 | loaded: true
20 | name: Swagger UI
21 | regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi))
22 | scope: response body
23 | sensitive: true
24 | - color: green
25 | engine: dfa
26 | loaded: true
27 | name: Ueditor
28 | regex: (ueditor\.(config|all)\.js)
29 | scope: response body
30 | sensitive: true
31 | type: Fingerprint
32 | - rule:
33 | - color: yellow
34 | engine: nfa
35 | loaded: true
36 | name: Email
37 | regex: (([a-z0-9][_|\.])*[a-z0-9]+@([a-z0-9][-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,}))
38 | scope: response
39 | sensitive: false
40 | - color: orange
41 | engine: nfa
42 | loaded: true
43 | name: Chinese IDCard
44 | regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]'
45 | scope: response body
46 | sensitive: true
47 | - color: orange
48 | engine: nfa
49 | loaded: true
50 | name: Chinese Mobile Number
51 | regex: '[^\w]((?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]'
52 | scope: response body
53 | sensitive: true
54 | - color: cyan
55 | engine: nfa
56 | loaded: true
57 | name: Internal IP Address
58 | regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))'
59 | scope: response
60 | sensitive: true
61 | - color: green
62 | engine: nfa
63 | loaded: true
64 | name: MAC Address
65 | regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}))
66 | scope: response
67 | sensitive: true
68 | - color: orange
69 | engine: nfa
70 | loaded: false
71 | name: Chinese Bank Card ID
72 | regex: '[^0-9]([1-9]\d{12,18})[^0-9]'
73 | scope: response
74 | sensitive: true
75 | type: Basic Information
76 | - rule:
77 | - color: cyan
78 | engine: dfa
79 | loaded: true
80 | name: RCE Paramters
81 | regex: ((cmd=)|(exec=)|(command=)|(execute=)|(ping=)|(query=)|(jump=)|(code=)|(reg=)|(do=)|(func=)|(arg=)|(option=)|(load=)|(process=)|(step=)|(read=)|(function=)|(feature=)|(exe=)|(module=)|(payload=)|(run=)|(daemon=)|(upload=)|(dir=)|(download=)|(log=)|(ip=)|(cli=))
82 | scope: request
83 | sensitive: true
84 | - color: yellow
85 | engine: dfa
86 | loaded: true
87 | name: Java Deserialization
88 | regex: (javax\.faces\.ViewState)
89 | scope: response
90 | sensitive: true
91 | - color: cyan
92 | engine: dfa
93 | loaded: true
94 | name: Debug Logic Parameters
95 | regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=))
96 | scope: request
97 | sensitive: true
98 | - color: cyan
99 | engine: nfa
100 | loaded: true
101 | name: URL As A Value
102 | regex: (=(https?://.*|https?%3(a|A)%2(f|F)%2(f|F).*))
103 | scope: request
104 | sensitive: true
105 | - color: yellow
106 | engine: dfa
107 | loaded: true
108 | name: Upload Form
109 | regex: (type=\"file\")
110 | scope: response body
111 | sensitive: true
112 | - color: cyan
113 | engine: nfa
114 | loaded: true
115 | name: DoS Paramters
116 | regex: ((size=)|(page=)|(num=))
117 | scope: request
118 | sensitive: false
119 | - color: yellow
120 | engine: nfa
121 | loaded: true
122 | name: JSONP Response
123 | regex: (^(\w.*?)\()
124 | scope: response body
125 | sensitive: false
126 | type: Maybe Vulnerability
127 | - rule:
128 | - color: yellow
129 | engine: dfa
130 | loaded: true
131 | name: OSS
132 | regex: ([A|a]ccess[K|k]ey[I|i][d|D]|[A|a]ccess[K|k]ey[S|s]ecret)
133 | scope: any
134 | sensitive: true
135 | - color: green
136 | engine: nfa
137 | loaded: true
138 | name: Amazon AWS URL
139 | regex: (((([a-zA-Z0-9._-]+\.s3|s3)(\.|\-)+[a-zA-Z0-9._-]+|[a-zA-Z0-9._-]+\.s3|s3)\.amazonaws\.com)|(s3:\/\/[a-zA-Z0-9-\.\_]+)|(s3.console.aws.amazon.com\/s3\/buckets\/[a-zA-Z0-9-\.\_]+)|(amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|(ec2-[0-9-]+.cd-[a-z0-9-]+.compute.amazonaws.com)|(us[_-]?east[_-]?1[_-]?elb[_-]?amazonaws[_-]?com))
140 | scope: response body
141 | sensitive: true
142 | - color: green
143 | engine: nfa
144 | loaded: true
145 | name: Amazon AWS Region
146 | regex: ((us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\d)
147 | scope: response body
148 | sensitive: true
149 | - color: blue
150 | engine: nfa
151 | loaded: true
152 | name: SSH Private Key
153 | regex: ([-]+BEGIN [^\s]+ PRIVATE KEY[-])
154 | scope: response body
155 | sensitive: true
156 | - color: green
157 | engine: nfa
158 | loaded: true
159 | name: Windows File/Dir Path
160 | regex: '[^\w](([a-zA-Z]:\\(?:\w+\\?)*)|([a-zA-Z]:\\(?:\w+\\)*\w+\.\w+))'
161 | scope: response
162 | sensitive: true
163 | - color: yellow
164 | engine: nfa
165 | loaded: true
166 | name: Password Field
167 | regex: ((|'|")([p](ass|wd|asswd|assword))(|'|")(:|=)( |)('|")(.*?)('|")(|,))
168 | scope: response body
169 | sensitive: false
170 | - color: green
171 | engine: nfa
172 | loaded: true
173 | name: Username Field
174 | regex: ((|'|")([u](ser|name|ame|sername))(|'|")(:|=)( |)('|")(.*?)('|")(|,))
175 | scope: response body
176 | sensitive: false
177 | - color: green
178 | engine: dfa
179 | loaded: true
180 | name: WeCom Key
181 | regex: ([c|C]or[p|P]id|[c|C]orp[s|S]ecret)
182 | scope: response body
183 | sensitive: true
184 | - color: yellow
185 | engine: nfa
186 | loaded: true
187 | name: JDBC Connection
188 | regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+)
189 | scope: any
190 | sensitive: false
191 | - color: yellow
192 | engine: nfa
193 | loaded: true
194 | name: Authorization Header
195 | regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
196 | scope: response body
197 | sensitive: false
198 | - color: green
199 | engine: nfa
200 | loaded: true
201 | name: Github Access Token
202 | regex: ([a-z0-9_-]*:[a-z0-9_\-]+@github\.com*)
203 | scope: response body
204 | sensitive: false
205 | - color: cyan
206 | engine: nfa
207 | loaded: true
208 | name: Microsoft Teams Webhook
209 | regex: (https://outlook\.office\.com/webhook/[a-z0-9@-]+/IncomingWebhook/[a-z0-9-]+/[a-z0-9-]+)
210 | scope: response body
211 | sensitive: false
212 | - color: cyan
213 | engine: nfa
214 | loaded: true
215 | name: Zoho Webhook
216 | regex: (https://creator\.zoho\.com/api/[a-z0-9/_.-]+\?authtoken=[a-z0-9]+)
217 | scope: response body
218 | sensitive: false
219 | - color: cyan
220 | engine: nfa
221 | loaded: true
222 | name: Sonarqube Token
223 | regex: (sonar.{0,50}(?:"|\'|`)?[0-9a-f]{40}(?:"|\'|`)?)
224 | scope: response body
225 | sensitive: false
226 | type: Sensitive Information
227 | - rule:
228 | - color: gray
229 | engine: nfa
230 | loaded: true
231 | name: Linkfinder
232 | regex: (?:"|')(((?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|((?:/|\.\./|\./)[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,})|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-/]{1,}/[a-zA-Z0-9_\-/]{3,}(?:[\?|#][^"|']{0,}|))|([a-zA-Z0-9_\-]{1,}\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:[\?|#][^"|']{0,}|)))(?:"|')
233 | scope: response body
234 | sensitive: true
235 | - color: pink
236 | engine: dfa
237 | loaded: true
238 | name: Source Map
239 | regex: (\.js\.map)
240 | scope: response body
241 | sensitive: true
242 | - color: magenta
243 | engine: nfa
244 | loaded: true
245 | name: HTML Notes
246 | regex: ()
247 | scope: response body
248 | sensitive: true
249 | - color: green
250 | engine: dfa
251 | loaded: true
252 | name: Create Script
253 | regex: (createElement\(\"script\"\))
254 | scope: response body
255 | sensitive: true
256 | type: Other
257 |
--------------------------------------------------------------------------------
/config/pc_ua.json:
--------------------------------------------------------------------------------
1 | {"chrome": ["Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2224.3 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", "Mozilla/5.0 (Windows NT 4.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36", "Mozilla/5.0 (X11; OpenBSD i386) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2309.372 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2117.157 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1866.237 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/4E423F", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36 Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.517 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36", "Mozilla/5.0 (X11; CrOS i686 4319.74.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.2 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1467.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1464.0 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1500.55 Safari/537.36", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.90 Safari/537.36", "Mozilla/5.0 (X11; NetBSD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36", "Mozilla/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.14 (KHTML, like Gecko) Chrome/24.0.1292.0 Safari/537.14"], "opera": ["Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16", "Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14", "Mozilla/5.0 (Windows NT 6.0; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 12.14", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0) Opera 12.14", "Opera/12.80 (Windows NT 5.1; U; en) Presto/2.10.289 Version/12.02", "Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00", "Opera/9.80 (Windows NT 5.1; U; zh-sg) Presto/2.9.181 Version/12.00", "Opera/12.0(Windows NT 5.2;U;en)Presto/22.9.168 Version/12.00", "Opera/12.0(Windows NT 5.1;U;en)Presto/22.9.168 Version/12.00", "Mozilla/5.0 (Windows NT 5.1) Gecko/20100101 Firefox/14.0 Opera/12.0", "Opera/9.80 (Windows NT 6.1; WOW64; U; pt) Presto/2.10.229 Version/11.62", "Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.10.229 Version/11.62", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52", "Opera/9.80 (Windows NT 5.1; U; en) Presto/2.9.168 Version/11.51", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; de) Opera 11.51", "Opera/9.80 (X11; Linux x86_64; U; fr) Presto/2.9.168 Version/11.50", "Opera/9.80 (X11; Linux i686; U; hu) Presto/2.9.168 Version/11.50", "Opera/9.80 (X11; Linux i686; U; ru) Presto/2.8.131 Version/11.11", "Opera/9.80 (X11; Linux i686; U; es-ES) Presto/2.8.131 Version/11.11", "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/5.0 Opera 11.11", "Opera/9.80 (X11; Linux x86_64; U; bg) Presto/2.8.131 Version/11.10", "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.8.99 Version/11.10", "Opera/9.80 (Windows NT 5.1; U; zh-tw) Presto/2.8.131 Version/11.10", "Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1", "Opera/9.80 (X11; Linux x86_64; U; Ubuntu/10.10 (maverick); pl) Presto/2.7.62 Version/11.01", "Opera/9.80 (X11; Linux i686; U; ja) Presto/2.7.62 Version/11.01", "Opera/9.80 (X11; Linux i686; U; fr) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 6.1; U; sv) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 6.1; U; en-US) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 6.1; U; cs) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 6.0; U; pl) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 5.2; U; ru) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 5.1; U;) Presto/2.7.62 Version/11.01", "Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.7.62 Version/11.01", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101213 Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01", "Mozilla/5.0 (Windows NT 6.1; U; nl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01", "Mozilla/5.0 (Windows NT 6.1; U; de; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; de) Opera 11.01", "Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00", "Opera/9.80 (X11; Linux i686; U; it) Presto/2.7.62 Version/11.00", "Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.6.37 Version/11.00", "Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.7.62 Version/11.00", "Opera/9.80 (Windows NT 6.1; U; ko) Presto/2.7.62 Version/11.00", "Opera/9.80 (Windows NT 6.1; U; fi) Presto/2.7.62 Version/11.00", "Opera/9.80 (Windows NT 6.1; U; en-GB) Presto/2.7.62 Version/11.00", "Opera/9.80 (Windows NT 6.1 x64; U; en) Presto/2.7.62 Version/11.00", "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.7.39 Version/11.00"], "firefox": ["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0", "Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0", "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/29.0", "Mozilla/5.0 (X11; OpenBSD amd64; rv:28.0) Gecko/20100101 Firefox/28.0", "Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0", "Mozilla/5.0 (Windows NT 6.1; rv:27.3) Gecko/20130101 Firefox/27.3", "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:27.0) Gecko/20121011 Firefox/27.0", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0", "Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0", "Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/23.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20130406 Firefox/23.0", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:23.0) Gecko/20131011 Firefox/23.0", "Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:22.0) Gecko/20130328 Firefox/22.0", "Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20130405 Firefox/22.0", "Mozilla/5.0 (Microsoft Windows NT 6.2.9200.0); rv:22.0) Gecko/20130405 Firefox/22.0", "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/21.0.1", "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:21.0.0) Gecko/20121011 Firefox/21.0.0", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20130514 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.2; rv:21.0) Gecko/20130326 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130401 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130331 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130330 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130401 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130328 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20130401 Firefox/21.0", "Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20130331 Firefox/21.0", "Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (Windows NT 5.0; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0", "Mozilla/5.0 (Windows NT 6.2; Win64; x64;) Gecko/20100101 Firefox/20.0", "Mozilla/5.0 (Windows x86; rv:19.0) Gecko/20100101 Firefox/19.0", "Mozilla/5.0 (Windows NT 6.1; rv:6.0) Gecko/20100101 Firefox/19.0", "Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/18.0.1", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0.6"], "internetexplorer": ["Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko", "Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 7.0; InfoPath.3; .NET CLR 3.1.40767; Trident/6.0; en-IN)", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/4.0; InfoPath.2; SV1; .NET CLR 2.0.50727; WOW64)", "Mozilla/5.0 (compatible; MSIE 10.0; Macintosh; Intel Mac OS X 10_7_3; Trident/6.0)", "Mozilla/4.0 (Compatible; MSIE 8.0; Windows NT 5.2; Trident/6.0)", "Mozilla/4.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)", "Mozilla/1.22 (compatible; MSIE 10.0; Windows 3.1)", "Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; InfoPath.3; MS-RTC LM 8; .NET4.0C; .NET4.0E)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.112)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; Tablet PC 2.0; InfoPath.3; .NET4.0C; .NET4.0E)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; yie8)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET CLR 1.1.4322; .NET4.0C; Tablet PC 2.0)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; FunWebProducts)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/13.0.782.215)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; chromeframe/11.0.696.57)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) chromeframe/10.0.648.205", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.1; SV1; .NET CLR 2.8.52393; WOW64; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; chromeframe/11.0.696.57)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/4.0; GTB7.4; InfoPath.3; SV1; .NET CLR 3.1.76908; WOW64; en-US)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; InfoPath.1; SV1; .NET CLR 3.8.36217; WOW64; en-US)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; .NET CLR 2.7.58687; SLCC2; Media Center PC 5.0; Zune 3.4; Tablet PC 3.6; InfoPath.3)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; SLCC1; .NET CLR 1.1.4322)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.0; Trident/4.0; InfoPath.1; SV1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.0; Trident/4.0; FBSMTWB; .NET CLR 2.0.34861; .NET CLR 3.0.3746.3218; .NET CLR 3.5.33652; msn OptimizedIE8;ENUS)", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8)", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; MS-RTC LM 8", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; Media Center PC 6.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C)", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.3; .NET4.0C; .NET4.0E; .NET CLR 3.5.30729; .NET CLR 3.0.30729; MS-RTC LM 8)", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; InfoPath.2)", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 3.0)"], "safari": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A", "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10", "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; da-dk) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1", "Mozilla/5.0 (Windows; U; Windows NT 6.1; tr-TR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.1; ko-KR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.1; fr-FR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.1; cs-CZ) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.0; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; sv-se) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ko-kr) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ja-jp) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; it-it) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; fr-fr) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; es-es) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-gb) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", "Mozilla/5.0 (Windows; U; Windows NT 6.1; sv-SE) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 6.1; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 6.0; hu-HU) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 6.0; de-DE) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 5.1; it-IT) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/534.16+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; fr-ch) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; de-de) AppleWebKit/534.15+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ar) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Android 2.2; Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4", "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-HK) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 6.0; tr-TR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 6.0; nb-NO) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; zh-cn) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5"]}
--------------------------------------------------------------------------------
/config/phantomjs_liunx_x64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/config/phantomjs_liunx_x64
--------------------------------------------------------------------------------
/config/phantomjs_windows.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/config/phantomjs_windows.exe
--------------------------------------------------------------------------------
/imgs/five.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/five.png
--------------------------------------------------------------------------------
/imgs/four.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/four.png
--------------------------------------------------------------------------------
/imgs/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/help.png
--------------------------------------------------------------------------------
/imgs/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/logo.jpg
--------------------------------------------------------------------------------
/imgs/one.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/one.png
--------------------------------------------------------------------------------
/imgs/threes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/threes.png
--------------------------------------------------------------------------------
/imgs/two.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/imgs/two.png
--------------------------------------------------------------------------------
/lib/Controller.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | from lib.common.utils import Utils
5 | from lib.WebpackFind import webpackfind_class
6 | from lib.common.toupdate import ToUpdate
7 | from lib.common.cmdline import CommandLines
8 | from lib.common.fileoperation import FileOperation
9 | from queue import Queue
10 | from tqdm import *
11 |
12 | class Controller():
13 |
14 | def __init__(self, options):
15 | self.options = options
16 |
17 | def start(self):
18 | # 判断cookie是否为空
19 | if self.options.cookies == None:
20 | self.options.cookies = ""
21 |
22 | # 声明全局调用headers
23 | headers = {"User-Agent": FileOperation().uarand(), "Cookie": self.options.cookies}
24 | if self.options.headers != None:
25 | headers = Utils().handle_headers(headers, self.options.headers)
26 |
27 | # 声明全局uuid
28 | uuid = Utils().random_uuid() + "/"
29 |
30 | if self.options.update != 0:
31 | # 检查更新
32 | ToUpdate().get_version()
33 |
34 | elif self.options.jsfile != None:
35 |
36 | webpackfind_class([], uuid, None, headers).jsfile_main(self.options.jsfile)
37 |
38 | elif self.options.urlfile != None:
39 |
40 | pbar = tqdm(total=1, position=0, desc="完成进度")
41 |
42 | webpackfind_class([], uuid, pbar, headers).main(self.options.urlfile)
43 |
44 | pbar.close()
45 |
46 | elif self.options.file != None:
47 |
48 | try:
49 | # 声明多线程队列
50 | urllist = Queue(-1)
51 | file = open(str(self.options.file), 'r', encoding='utf-8')
52 | filelist = file.readlines()
53 | for i in range(len(filelist)):
54 | urllist.put([filelist[i].strip(), len(filelist)])
55 |
56 | pbar = tqdm(total=len(filelist), position=0, desc="完成进度")
57 | threads = []
58 | for num in range(1, len(filelist) + 1):
59 | t = webpackfind_class(urllist, uuid, pbar, headers)
60 | threads.append(t)
61 | t.start()
62 |
63 | for t in threads:
64 | t.join()
65 |
66 | pbar.close()
67 |
68 | except Exception as e:
69 | tqdm.write(CommandLines().help())
70 | else:
71 | tqdm.write(CommandLines().help())
72 |
73 | # 清空phantomjs日志
74 | FileOperation().rmphantomjslog()
75 |
--------------------------------------------------------------------------------
/lib/WebpackFind.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import os, threading
5 | from lib.common.utils import Utils
6 | from lib.common.fileoperation import FileOperation
7 | from lib.common.downloadjs import DownloadJs
8 | from lib.common.regular import Regular
9 | from lib.vuln.unauthorized import unauthorized
10 | from lib.common.urlrequest import UrlRequest
11 | from lib.common.Printlog import Printlog
12 | from urllib.parse import urlparse
13 | from bs4 import BeautifulSoup
14 |
15 | class webpackfind_class(threading.Thread):
16 |
17 | def __init__(self, urllist=[], uuid="", pbar=None, headers={}):
18 | threading.Thread.__init__(self)
19 | self.urllist = urllist
20 | self.uuid = uuid
21 | self.domain = ""
22 | # 初始化加载规则库
23 | self.rulesJson = FileOperation().getyml()
24 | self.log = None
25 | self.domainlog = ""
26 | self.pbar = pbar
27 | self.headers = headers
28 |
29 | # js读取本地文件
30 | def jsfile_main(self, jsfile=""):
31 | try:
32 | # 清空日志文件
33 | FileOperation().rmlogfile(jsfile)
34 |
35 | self.log = Printlog(jsfile).get_logger()
36 |
37 | # 遍历读取文件目录中的文件
38 | eachinfo = FileOperation().eachFile(jsfile, 1)
39 |
40 | # 遍历读取文件路径
41 | eachfile = FileOperation().eachFile(jsfile, 2)
42 |
43 | # 匹配规则库
44 | Regular().run_Matching_rules(os.path.basename(os.path.realpath(jsfile)), jsfile, eachfile, self.rulesJson)
45 |
46 | # 在所有的urls中提取出目标站的子域名
47 | Regular().find_subdomain(os.path.basename(os.path.realpath(jsfile)), eachinfo, jsfile, os.path.basename(os.path.realpath(jsfile)))
48 |
49 | # 探测未授权接口
50 | unauthorized(os.path.basename(os.path.realpath(jsfile)), self.headers, os.path.basename(os.path.realpath(jsfile)), self.rulesJson, jsfile).run(eachfile)
51 |
52 | self.log.info("{} 【{}】 扫描成功 路径:{}".format(Utils().tellTime(), os.path.basename(os.path.realpath(jsfile)), jsfile))
53 |
54 | except Exception as e:
55 | self.log.error("{} 【错误异常】:{}".format(Utils().tellTime(), str(e)))
56 |
57 | # 获取js文件url
58 | def get_js(self, domain="", path=""):
59 |
60 | domain = UrlRequest(self.headers).Auto_select_protocol(domain)
61 |
62 | self.log.info("{} 【{}】 协议头检测:{}".format(Utils().tellTime(), self.domainlog, str(domain)))
63 |
64 | self.domain = domain
65 |
66 | url = []
67 |
68 | new_domain = ""
69 |
70 | if urlparse(domain).netloc:
71 |
72 | # url自动化遍历读取文件
73 | content, js_array = UrlRequest(self.headers).phantomjs_requests(domain)
74 |
75 | for i in range(len(js_array)):
76 | url.append(js_array[i])
77 |
78 | if content:
79 |
80 | content = BeautifulSoup(content, "html.parser")
81 |
82 | script = content.find_all('script')
83 |
84 | for a in range(len(script)):
85 | if script:
86 | try:
87 | for a in range(len(script)):
88 | if urlparse(script[a].get("src")).netloc != "":
89 | if urlparse(self.domain).netloc != urlparse(script[a].get("src")).netloc:
90 | domain = script[a].get("src")
91 | else:
92 | domain = self.domain
93 | else:
94 | domain = self.domain
95 |
96 | if script[a].get("src") != None and script[a].get("src")[0:2] == "//":
97 | scr_path = os.path.basename(script[a].get("src").replace("./", "/"))
98 | if os.path.basename(script[a].get("src").replace("./", "/")).find("?") != -1:
99 | scr_path = os.path.basename(script[a].get("src").replace("./", "/"))[0:os.path.basename(script[a].get("src").replace("./", "/")).find("?")]
100 | if len(js_array) > 0:
101 | if Utils().is_array_value(js_array, Utils().get_filename(scr_path)) == True:
102 | continue
103 | elif domain == None:
104 | # 读取js文件路径
105 | js_content = FileOperation().resolve_path(str(script[a]))
106 | for i in range(len(js_content)):
107 | url.append(self.domain + "/" + js_content[i])
108 | url = list(set(url))
109 | else:
110 | try:
111 | scheme = "http:"
112 | if urlparse(domain).scheme != "":
113 | scheme = str(urlparse(domain).scheme) + ":"
114 | if urlparse(domain).path:
115 | new_domain = scheme + "//" + urlparse(domain).netloc + "/" + str(urlparse(domain).path).split("/")[1]
116 | else:
117 | new_domain = scheme + "//" + urlparse(domain).netloc
118 | if script[a].get("data-main") != None:
119 | domain_url = new_domain + os.path.normpath(script[a].get("src").replace("./", "/"))
120 | if urlparse(domain).path:
121 | if script[a].get("data-main").find(str(urlparse(new_domain).path).split("/")[1]) != -1:
122 | domain_url = urlparse(new_domain).scheme + "://" + urlparse(new_domain).netloc + os.path.normpath(script[a].get("data-main")).replace("\\", "/").replace("./", "/")
123 | if os.path.normpath(script[a].get("data-main")).replace("\\", "/").replace("./", "/")[:1] != "/":
124 | domain_url = urlparse(new_domain).scheme + "://" + urlparse(new_domain).netloc + "/" + os.path.normpath(script[a].get("data-main")).replace("\\", "/").replace("./", "/")
125 | url.append(domain_url)
126 | elif script[a].get("src") != None:
127 | if "http" in script[a].get("src"):
128 | url.append(script[a].get("src").replace("./", "/"))
129 | elif "runtime." in script[a].get("src") or "app." in script[a].get("src") or "finance." in script[a].get("src") or "vendor." in script[a].get("src") or "manifest." in script[a].get("src"):
130 | if urlparse(os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")).netloc == "":
131 | domain_url = new_domain + os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")
132 | else:
133 | domain_url = new_domain + str(scheme + os.path.normpath(script[a].get("src")).replace("\\","/").replace("./", "/")).replace(new_domain, "")
134 | if urlparse(new_domain).path:
135 | if os.path.normpath(script[a].get("src")).replace("\\","/").replace("./", "/")[:1] != "/":
136 | if urlparse(os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")).netloc == "":
137 | domain_url = urlparse(new_domain).scheme + "://" + urlparse(new_domain).netloc + urlparse(new_domain).path + "/" + os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")
138 | else:
139 | domain_url = urlparse(new_domain).scheme + "://" + urlparse(new_domain).netloc + urlparse(new_domain).path + "/" + str(scheme + os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")).replace(new_domain, "")
140 | else:
141 | if urlparse(os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")).netloc == "":
142 | domain_url = urlparse(new_domain).scheme + "://" + urlparse(new_domain).netloc + urlparse(new_domain).path + os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")
143 | else:
144 | domain_url = urlparse(new_domain).scheme + "://" + urlparse(new_domain).netloc + urlparse(new_domain).path + str(scheme + os.path.normpath(script[a].get("src")).replace("\\", "/").replace("./", "/")).replace(new_domain, "")
145 | content = UrlRequest(self.headers).Extract_html(domain_url)
146 | if content == None:
147 | try:
148 | if domain == None:
149 | domainfname = os.path.join(path, str(urlparse(self.domain).netloc).replace(":", "_") + "_error_js_url_list.txt")
150 | else:
151 | domainfname = os.path.join(path, str(urlparse(domain).netloc).replace(":", "_") + "_error_js_url_list.txt")
152 | FileOperation().save_result(domainfname, domain)
153 | except Exception as e:
154 | self.log.error("{} 【写入文件失败】:{}".format(Utils().tellTime(), str(e)))
155 | continue
156 | else:
157 | # 读取js文件路径
158 | js_content = FileOperation().resolve_path(str(content))
159 | for i in range(len(js_content)):
160 | url.append(new_domain + "/" + js_content[i])
161 | url = list(set(url))
162 | else:
163 | script_text = script[a].get("src").replace("./", "/")
164 | if script_text[0] != "/":
165 | script_text = "/" + script_text
166 | if len(js_array) > 0:
167 | if Utils().is_array_value(js_array, os.path.basename(script[a].get("src").replace("./", "/"))) == True:
168 | continue
169 |
170 | if urlparse(script_text).scheme == "":
171 | if urlparse(script_text).netloc != "":
172 | url.append(scheme + script_text)
173 | else:
174 | url.append(new_domain + script_text)
175 | else:
176 | url.append(new_domain + script_text)
177 | except Exception as e:
178 | self.log.error("{} 【解析js出错】:{}".format(Utils().tellTime(), str(e)))
179 | continue
180 | except Exception as e:
181 | self.log.error("{} 【解析js出错】:{}".format(Utils().tellTime(), str(e)))
182 | pass
183 |
184 | # 解决重复url问题
185 | new_url = []
186 | for u in range(len(url)):
187 | if Utils().White_list_domain(url[u]):
188 | url[u] = url[u].replace("///", "/").replace("\\", "/").replace("./", "/").replace("///", "/")
189 | if url[u].find("?") != -1:
190 | new_url.append(url[u][:url[u].find("?")])
191 | elif url[u].find(";") != -1:
192 | new_url.append(url[u][:url[u].find(";")])
193 | else:
194 | if urlparse(url[u]).netloc == "":
195 | continue
196 | else:
197 | new_url.append(url[u])
198 | url = list(set(new_url))
199 | try:
200 | if domain == None:
201 | domainfname = os.path.join(path, str(urlparse(self.domain).netloc).replace(":", "_") + "_js_url_list.txt")
202 | else:
203 | domainfname = os.path.join(path, str(urlparse(domain).netloc).replace(":", "_") + "_js_url_list.txt")
204 | for u in range(len(url)):
205 | FileOperation().save_result(domainfname, str(url[u]))
206 | return url
207 | except Exception as e:
208 | self.log.error("{} 【写入文件失败】:{}".format(Utils().tellTime(), str(e)))
209 | return url
210 | elif content == None:
211 | try:
212 | fname = os.path.join(path, str(urlparse(new_domain).netloc).replace(":", "_") + "_error_js_url_list.txt")
213 | FileOperation().save_result(fname, domain)
214 | return url
215 | except Exception as e:
216 | self.log.error("{} 【写入文件失败】:{}".format(Utils().tellTime(), str(e)))
217 | return url
218 | else:
219 | self.log.error("{} 【扫描失败】:{}".format(Utils().tellTime(), str(domain)))
220 | return url
221 |
222 | # 入口函数
223 | def main(self, domain=""):
224 | # 做下vue url存在 /#/ 问题
225 | if "/#/" in domain:
226 | domain = domain.split("/#/")[0]
227 |
228 | # 声明保存路径
229 | if urlparse(domain).netloc == "":
230 | path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'tmp', self.uuid, str(domain).replace(":", "_"), '')
231 | self.domainlog = str(domain)
232 | else:
233 | path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'tmp', self.uuid, str(urlparse(domain).netloc).replace(":", "_"), '')
234 | self.domainlog = str(urlparse(domain).netloc)
235 |
236 | FileOperation().mkdir(path)
237 |
238 | self.log = Printlog(path).get_logger()
239 |
240 | self.log.info("{} 【{}】 初始化创建路径:{}".format(Utils().tellTime(), self.domainlog, path))
241 |
242 | # 读取js文件路径
243 | info = self.get_js(domain, path)
244 |
245 | if info:
246 |
247 | self.log.info("{} 【{}】 获取到js路径,数量:{}".format(Utils().tellTime(), self.domainlog, len(info)))
248 |
249 | # 扫描结果存入 result.txt
250 | FileOperation().save_result(os.path.join(path, 'result.txt'), "【URL】:{}".format(str(domain)), "w")
251 |
252 | # 批量下载js
253 | DownloadJs(self.domainlog, info, path, self.headers).run()
254 |
255 | # 遍历读取文件目录中的文件
256 | eachinfo = FileOperation().eachFile(path, 1)
257 |
258 | # 遍历读取文件路径
259 | eachfile = FileOperation().eachFile(path, 2)
260 |
261 | # 匹配规则库
262 | Regular().run_Matching_rules(self.domainlog, path, eachfile, self.rulesJson)
263 |
264 | # 在所有的urls中提取出目标站的子域名
265 | Regular().find_subdomain(self.domainlog, eachinfo, path, domain)
266 |
267 | # 探测未授权接口
268 | unauthorized(self.domainlog, self.headers, domain, self.rulesJson, path).run(eachfile)
269 |
270 | self.log.info("{} 【{}】 扫描成功 路径:{}".format(Utils().tellTime(), self.domainlog, str(path)))
271 |
272 | else:
273 |
274 | self.log.info("{} 【扫描失败】:{}".format(Utils().tellTime(), str(domain)))
275 |
276 | self.pbar.update()
277 |
278 | # 主函数
279 | def run(self):
280 | while not self.urllist.empty():
281 | url, i = self.urllist.get()
282 | self.main(url)
283 | self.urllist.task_done()
284 |
285 |
286 |
287 |
--------------------------------------------------------------------------------
/lib/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/lib/__init__.py
--------------------------------------------------------------------------------
/lib/common/Printlog.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 | import logging, time, os
4 |
5 | class Printlog():
6 | def __init__(self, path="", logger=None):
7 | self.logger = logging.getLogger(logger)
8 | self.logger.setLevel(logging.NOTSET)
9 | self.log_time = time.strftime("%Y-%m-%d %H:%M:%S")
10 | self.path = path
11 |
12 | def info(self, message):
13 | self.fontColor('\033[0;34m%s\033[0m')
14 | self.logger.info(message)
15 |
16 | def set_logger(self):
17 | if not self.logger.handlers:
18 | self.fh = logging.FileHandler(os.path.join(self.path, "run_log.txt"), "w", encoding="utf-8")
19 | self.fh.setLevel(logging.DEBUG)
20 | self.chd = logging.StreamHandler()
21 | self.chd.setLevel(logging.INFO)
22 | self.formatter = logging.Formatter("[%(levelname)s] %(asctime)s %(filename)s -> %(funcName)s line:%(lineno)d: %(message)s\n")
23 | self.formatter_info = logging.Formatter()
24 | self.chd.setFormatter(self.formatter_info)
25 | self.fh.setFormatter(self.formatter)
26 | self.logger.addHandler(self.fh)
27 | self.logger.addHandler(self.chd)
28 |
29 | def get_logger(self):
30 | Printlog.set_logger(self)
31 | return self.logger
32 |
33 | def remove_log_handler(self):
34 | self.logger.removeHandler(self.fh)
35 | self.logger.removeHandler(self.chd)
36 | self.fh.close()
37 | self.chd.close()
38 |
--------------------------------------------------------------------------------
/lib/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/lib/common/__init__.py
--------------------------------------------------------------------------------
/lib/common/cmdline.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 |
4 | import argparse
5 |
6 | class CommandLines():
7 |
8 | # 参数输入
9 | def cmd(self):
10 | parser = argparse.ArgumentParser(description=self.help(), add_help=False)
11 | parser.add_argument("-u", "--urlfile", help="自动化遍历URL中里面js")
12 | parser.add_argument("-j", "--jsfile", help="遍历本地目录中的js文件并且格式化js文件、提取信息泄露")
13 | parser.add_argument("-f", "--file", help="读取本地url文件批量遍历url中js")
14 | parser.add_argument("-c", "--cookies", help="设置自定义Cookie,场景:需要登录才能爬取js信息")
15 | parser.add_argument("-d", "--headers", help="设置自定义headers")
16 | parser.add_argument("-update", "--update", type=int, default=0, help="检查版本更新")
17 | return parser.parse_args()
18 |
19 | # 帮助文档输出
20 | def help(self):
21 | return '''
22 | 例子:
23 | 自动化遍历URL中里面js
24 | python3 webpackfind.py -u http://domain.com
25 |
26 | 遍历本地目录中的js文件并且格式化js文件、提取信息泄露
27 | python3 webpackfind.py -j ./js/test.com/
28 |
29 | 读取本地url文件批量遍历url中js
30 | python3 webpackfind.py -f ./url.txt
31 |
32 | 设置自定义Cookie,场景:需要登录才能爬取js信息
33 | python3 webpackfind.py -c "name=test;" -u http://domain.com
34 |
35 | 设置自定义headers
36 | python3 webpackfind.py -d "Authorization: bearer 123456\nContent-Type: application/json\n" -u http://domain.com
37 |
38 | 检查本地`webpackfind.py`是否与github.com是否一致
39 | python3 webpackfind.py -update 1
40 | '''
--------------------------------------------------------------------------------
/lib/common/downloadjs.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 |
4 | import jsbeautifier
5 | from lib.common.urlrequest import UrlRequest
6 | from lib.common.Printlog import Printlog
7 | from lib.common.utils import Utils
8 | from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED
9 |
10 | class DownloadJs():
11 |
12 | def __init__(self, domainlog="", urllist=[], path="", headers={}):
13 | self.urllist = urllist
14 | self.path = path
15 | self.headers = headers
16 | self.log = Printlog(path).get_logger()
17 | self.downloadpbar = None
18 | self.success = 0
19 | self.error = 0
20 | self.domainlog = domainlog
21 |
22 | # 主函数
23 | def run(self):
24 |
25 | # 创建线程池
26 | pool = ThreadPoolExecutor(20)
27 |
28 | allTask = [pool.submit(self.Download, url) for url in self.urllist]
29 |
30 | # 开启异步线程池
31 | wait(allTask, return_when=ALL_COMPLETED)
32 |
33 | # log日志输出
34 | self.log.info("{} 【{}】 【下载完成】总数:{} 成功:{} 失败:{}".format(Utils().tellTime(), self.domainlog, str(len(self.urllist)), str(self.success), str(self.error)))
35 |
36 | def Download(self, url):
37 |
38 | url = url.replace("//", "/").replace(":/", "://")
39 |
40 | # 请求接口
41 | content = UrlRequest(self.headers).Extract_html(url)
42 |
43 | if content:
44 | self.success = self.success + 1
45 | fname = self.path + "/" + url.split('/')[-1]
46 | try:
47 | fp = open(fname, "at", encoding='utf-8')
48 | fp.write(jsbeautifier.beautify(content))
49 | fp.close()
50 | except Exception as e:
51 | self.log.error("{} 【{}】【写入文件失败】:{}".format(Utils().tellTime(), self.domainlog, str(e)))
52 | return
53 | else:
54 | self.error = self.error + 1
55 | return
--------------------------------------------------------------------------------
/lib/common/fileoperation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import os, json, random, jsbeautifier, yaml, re, js2py
5 | from lib.common.regular import Regular
6 |
7 | class FileOperation():
8 |
9 | # 写入文件
10 | def save_result(self, filename="", content="", jurisdiction="at"):
11 | fp = open(filename, jurisdiction, encoding='utf-8')
12 | fp.write(content + "\n")
13 | fp.close()
14 |
15 | # 创建文件夹
16 | def mkdir(self, path):
17 | path = path.strip()
18 | path = path.rstrip("\\")
19 | isExists = os.path.exists(path)
20 | if not isExists:
21 | os.makedirs(path)
22 | return True
23 | else:
24 | self.del_file(path)
25 | return True
26 |
27 | # 清空文件夹内容
28 | def del_file(self, path):
29 | ls = os.listdir(path)
30 | for i in ls:
31 | c_path = os.path.join(path, i)
32 | if os.path.isdir(c_path):
33 | self.del_file(c_path)
34 | else:
35 | os.remove(c_path)
36 |
37 | # 遍历指定目录,显示目录下的所有文件名
38 | def eachFile(self, filepath="", type=1):
39 | url = []
40 | pathDir = os.listdir(filepath)
41 | for allDir in pathDir:
42 | if os.path.splitext(allDir)[-1] != ".txt":
43 | child = os.path.join('%s%s' % (filepath, allDir))
44 | if type == 1:
45 | info = self.readFile(child)
46 | if info:
47 | for u in range(len(info)):
48 | url.append(info[u])
49 | else:
50 | url.append(child)
51 | return url
52 |
53 | # 遍历指定目录,并格式化js源码
54 | def eachFormatJs(self, filepath):
55 | pathDir = os.listdir(filepath)
56 | for allDir in pathDir:
57 | child = os.path.join('%s%s' % (filepath, allDir))
58 | # 读取文件
59 | fopen = open(child, 'r', encoding='utf-8')
60 | txt = jsbeautifier.beautify(fopen.read())
61 | fopen.close()
62 | # 写入文件
63 | file_object = open(child, 'w', encoding='utf-8')
64 | file_object.write(txt)
65 | file_object.close()
66 |
67 | # 读取文件内容并打印
68 | def readFile(self, filename):
69 | url = []
70 | fopen = open(filename, 'r', encoding='utf-8')
71 | data = Regular().Extract_URL(fopen.read())
72 | self.save_result(os.path.join(os.path.dirname(filename), "result.txt"), "【+】" + filename)
73 | for x in data:
74 | self.save_result(os.path.join(os.path.dirname(filename),"result.txt"), " " + str(x))
75 | url.append(x)
76 | fopen.close()
77 | return url
78 |
79 | # 随机获取ua库
80 | def uarand(self):
81 | ie_type = ["chrome", "opera", "firefox", "internetexplorer", "safari"]
82 | with open(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), 'config', 'pc_ua.json'), 'r', encoding='utf8') as fp:
83 | json_data = json.load(fp)
84 | return json_data[ie_type[random.randint(0, len(ie_type) - 1)]][random.randint(0, len(json_data[ie_type[random.randint(0, len(ie_type) - 1)]]) - 1)]
85 |
86 | # 读取yml规则库
87 | def getyml(self):
88 | rulesJson = []
89 | try:
90 | file = open(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), 'config', 'Config.yml'), 'r', encoding="utf-8")
91 | file_data = file.read()
92 | file.close()
93 | data = yaml.load(file_data, Loader=yaml.FullLoader)
94 | rules = data.get("rules")
95 | # Fingerprint 指纹
96 | # Basic Information 基本信息
97 | # Maybe Vulnerability 可能是脆弱性
98 | # Sensitive Information 敏感信息
99 | # Other 另外
100 | for i in range(len(rules)):
101 | for r in range(len(rules[i]["rule"])):
102 | rulesJson.append({"type_name": rules[i]["type"], "name": rules[i]["rule"][r]["name"], "regex": rules[i]["rule"][r]["regex"]})
103 | return rulesJson
104 | except:
105 | return rulesJson
106 |
107 | # 读取js文件路径
108 | def resolve_path(self, content=""):
109 | r = re.compile(r"\w\.p\+\"(.*?)\.js", re.DOTALL)
110 | result = r.findall(str(content))
111 | urllist = []
112 | for jsCode in result:
113 | if len(jsCode) < 30000:
114 | jsCode = "\"" + jsCode + ".js\""
115 | variable = re.findall(r'\[.*?\]', jsCode)
116 | if "[" and "]" in variable[0]:
117 | variable = variable[0].replace("[", "").replace("]", "")
118 | jsCodeFunc = "function js_compile(%s){js_url=" % (variable) + jsCode + "\nreturn js_url}"
119 | pattern_jscode = re.compile(r"\(\{\}\[(.*?)\]\|\|.\)", re.DOTALL)
120 | flag_code = pattern_jscode.findall(jsCodeFunc)
121 | if flag_code:
122 | jsCodeFunc = jsCodeFunc.replace("({}[%s]||%s)" % (flag_code[0], flag_code[0]), flag_code[0])
123 | pattern1 = re.compile(r"\{(.*?)\:")
124 | pattern2 = re.compile(r"\,(.*?)\:")
125 | nameList1 = pattern1.findall(jsCode)
126 | nameList2 = pattern2.findall(jsCode)
127 | nameList = nameList1 + nameList2
128 | nameList = list(set(nameList))
129 | Func = js2py.eval_js(jsCodeFunc)
130 | for name in nameList:
131 | if "\"" in name:
132 | name = name.replace("\"", "")
133 | content = Func(name)
134 | if "undefined" not in content:
135 | urllist.append(content)
136 | return list(set(urllist))
137 |
138 | # 清空phantomjs日志
139 | def rmphantomjslog(self):
140 | # 清楚日志文件
141 | try:
142 | os.remove(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), 'tmp', 'ghostdriver.log'))
143 | except Exception as e:
144 | pass
145 |
146 | # 清空日志文件
147 | def rmlogfile(self, filepath):
148 | pathDir = os.listdir(filepath)
149 | for allDir in pathDir:
150 | if allDir.endswith(".txt"):
151 | try:
152 | os.remove(os.path.join(filepath, allDir))
153 | except Exception as e:
154 | pass
--------------------------------------------------------------------------------
/lib/common/regular.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import re, os
5 | from lib.common.utils import Utils
6 | from lib.common.Printlog import Printlog
7 | from urllib.parse import urlparse
8 | from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED
9 |
10 | class Regular():
11 |
12 | def __init__(self):
13 | self.log = ""
14 |
15 | # 从js内容提取URL。返回链接列表:js_url[]
16 | def Extract_URL(self, Js_content):
17 | pattern_raw = r"""
18 | (?:"|') # Start newline delimiter
19 | (
20 | ((?:[a-zA-Z]{1,10}://|//) # Match a scheme [a-Z]*1-10 or //
21 | [^"'/]{1,}\. # Match a domainname (any character + dot)
22 | [a-zA-Z]{2,}[^"']{0,}) # The domainextension and/or path
23 | |
24 | ((?:/|\.\./|\./) # Start with /,../,./
25 | [^"'><,;| *()(%%$^/\\\[\]] # Next character can't be...
26 | [^"'><,;|()]{1,}) # Rest of the characters can't be
27 | |
28 | ([a-zA-Z0-9_\-/]{1,}/ # Relative endpoint with /
29 | [a-zA-Z0-9_\-/]{1,} # Resource name
30 | \.(?:[a-zA-Z]{1,4}|action) # Rest + extension (length 1-4 or action)
31 | (?:[\?|#][^"|']{0,}|)) # ? or # mark with parameters
32 | |
33 | ([a-zA-Z0-9_\-/]{1,}/ # REST API (no extension) with /
34 | [a-zA-Z0-9_\-/]{3,} # Proper REST endpoints usually have 3+ chars
35 | (?:[\?|#][^"|']{0,}|)) # ? or # mark with parameters
36 | |
37 | ([a-zA-Z0-9_\-]{1,} # filename
38 | \.(?:php|asp|aspx|jsp|json|
39 | action|html|js|txt|xml) # . + extension
40 | (?:[\?|#][^"|']{0,}|)) # ? or # mark with parameters
41 | )
42 | (?:"|') # End newline delimiter
43 | """
44 | pattern = re.compile(pattern_raw, re.VERBOSE)
45 | result = re.finditer(pattern, str(Js_content))
46 | if result == None:
47 | return None
48 | js_url = []
49 | for match in result:
50 | if match.group() not in js_url:
51 | js_url.append(match.group().strip('"').strip("'"))
52 |
53 | rule = r"['\"]+[0-9a-zA-Z-:\./?=]{4,}['\"]+"
54 | r = re.compile(rule)
55 | result = r.findall(str(Js_content))
56 | for i in result:
57 | if "." in i:
58 | if "/" in i:
59 | js_url.append(i.strip('"').strip("'"))
60 | js_url = Utils().unique(js_url)
61 | return js_url
62 |
63 | # 在所有的urls中提取出目标站的子域名
64 | def find_subdomain(self, domainlog="", urls=[], mainurl="", domain=""):
65 |
66 | self.log = Printlog(mainurl).get_logger()
67 |
68 | self.log.info("{} 【{}】 子域名提取规则模块加载完成".format(Utils().tellTime(), domainlog))
69 |
70 | if urlparse(domain).netloc:
71 | fname = os.path.join(mainurl, str(urlparse(domain).netloc).replace(":", "_") + "_url_list.txt")
72 | else:
73 | fname = os.path.join(mainurl, str(urlparse(domain).path).replace(":", "_") + "_url_list.txt")
74 | pattern = re.compile(
75 | r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|'
76 | r'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|'
77 | r'([a-zA-Z0-9][-_.a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\.'
78 | r'([a-zA-Z]{2,13}|[a-zA-Z0-9-]{2,30}.[a-zA-Z]{2,3})$'
79 | )
80 | miandomain = urlparse(mainurl).netloc
81 | positions = Utils().find_last(domain, ".")
82 | if len(positions) > 1:
83 | miandomain = domain[positions[-2] + 1:].replace("/", "")
84 | subdomains = []
85 | for url in urls:
86 | suburl = urlparse(url)
87 | subdomain = suburl.netloc
88 | if subdomain.strip() == "":
89 | continue
90 | if miandomain in subdomain:
91 | if subdomain not in subdomains:
92 | if Utils().White_list_domain(subdomain):
93 | if pattern.match(subdomain):
94 | if Utils().filter_content(subdomain):
95 | subdomains.append(subdomain)
96 | self.save_result(fname, subdomain)
97 | else:
98 | check_ip = re.compile('(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]):(6[0-5]{2}[0-3][0-5]|[1-5]\d{4}|[1-9]\d{1,3}|[0-9])|(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])')
99 | if check_ip.match(subdomain):
100 | if Utils().filter_content(subdomain):
101 | subdomains.append(subdomain)
102 | self.save_result(fname, subdomain)
103 | subdomains = list(set(subdomains))
104 | new_subdomains = []
105 | for i in range(len(subdomains)):
106 | new_subdomains.append([i, subdomains[i]])
107 |
108 | # 子域名表格方式输出
109 | Utils().gettable(["序号", "子域名"], "扫描完成:{}".format(domain), new_subdomains, os.path.join(mainurl, "table_result.txt"))
110 |
111 | self.log.info("{} 【{}】 子域名提取结果保存路径:{}".format(Utils().tellTime(), domainlog, os.path.join(mainurl, "table_result.txt")))
112 |
113 | self.log.info("{} 【{}】 子域名提取规则模块扫描完成".format(Utils().tellTime(), domainlog))
114 |
115 | return subdomains
116 |
117 | def run_Matching_rules(self, domainlog="", path="", filepath=[], rulesJson=[]):
118 |
119 | self.log = Printlog(path).get_logger()
120 |
121 | self.log.info("{} 【{}】 Hae规则匹配模块加载完成".format(Utils().tellTime(), domainlog))
122 |
123 | # 创建线程池
124 | pool = ThreadPoolExecutor(20)
125 |
126 | allTask = [pool.submit(self.Matching_rules, url, rulesJson) for url in filepath]
127 |
128 | # 开启异步线程池
129 | wait(allTask, return_when=ALL_COMPLETED)
130 |
131 | self.log.info("{} 【{}】 Hae规则匹配结果保存路径:{}".format(Utils().tellTime(), domainlog, os.path.join(path, "table_result.txt")))
132 |
133 | self.log.info("{} 【{}】 Hae规则匹配模块扫描完成".format(Utils().tellTime(), domainlog))
134 |
135 | # 匹配规则库
136 | def Matching_rules(self, filename="", rulesJson=[]):
137 | try:
138 | fopen = open(filename, 'r', encoding='utf-8')
139 | data = str(fopen.read())
140 | fopen.close()
141 | array = []
142 | count = 0
143 | for i in range(len(rulesJson)):
144 | try:
145 | r = re.compile(rulesJson[i]['regex'])
146 | result = r.findall(str(data))
147 | for ib in range(len(result)):
148 | if Utils().filter_content(result[ib][0]):
149 | array.append([rulesJson[i]['type_name'], rulesJson[i]['name'], result[ib][0]])
150 | count = count + 1
151 | except Exception as e:
152 | pass
153 | if count != 0:
154 | self.save_result(os.path.join(os.path.dirname(filename), "/result_rules.txt"), "【+】" + filename)
155 | # 子域名表格方式输出
156 |
157 | Utils().gettable(["模块", "名称", "内容"], "路径:{}".format(filename), array, os.path.join(os.path.dirname(filename), "table_result.txt"))
158 |
159 | except Exception as e:
160 | return
161 |
162 | # 写入文件
163 | def save_result(self, filename="", content="", jurisdiction="at"):
164 | fp = open(filename, jurisdiction, encoding='utf-8')
165 | fp.write(content + "\n")
166 | fp.close()
--------------------------------------------------------------------------------
/lib/common/toupdate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import os, requests
5 | from tqdm import *
6 | from lib.common.fileoperation import FileOperation
7 |
8 | class ToUpdate():
9 |
10 | # 检查版本更新
11 | def get_version(self):
12 | path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'version.txt')
13 | if os.path.exists(path):
14 | try:
15 | with open(path, "rt", encoding="UTF-8") as f:
16 | now_version = f.read().strip()
17 | print("目前版本: \n{}\n".format(now_version))
18 | version_url = "https://raw.githubusercontent.com/xz-zone/Webpackfind/master/version.txt"
19 | res = requests.get(url=version_url, headers={"User-Agent": FileOperation().uarand()}, timeout=10, verify=False)
20 | if res.status_code == 200:
21 | new_version = res.text.strip()
22 | if now_version == new_version:
23 | tqdm.write("目前版本最新")
24 | else:
25 | add_version = str(str(new_version.replace("\r", "").replace("\n", "")).replace(str(now_version.replace("\n", "")), "")).replace("。", "。\n")
26 | if add_version:
27 | print("目前版本最新")
28 | print("更新内容如下:\n{}".format(add_version))
29 | print("目前版本非最新,建议及时更新...\n地址: https://github.com/xz-zone/Webpackfind/\n")
30 | else:
31 | print("获取版本信息失败...")
32 | except Exception as e:
33 | print("获取版本信息失败...")
34 | else:
35 | print("目前版本非最新,建议及时更新...\n地址: https://github.com/xz-zone/Webpackfind/")
--------------------------------------------------------------------------------
/lib/common/urlrequest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import requests, chardet, platform, os
5 | from urllib.parse import urlparse
6 | from lib.common.fileoperation import FileOperation
7 | from tqdm import *
8 | from selenium import webdriver
9 | from selenium.webdriver import DesiredCapabilities
10 | from requests.packages import urllib3
11 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
12 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
13 |
14 | class UrlRequest():
15 |
16 | def __init__(self, headers={}):
17 | urllib3.disable_warnings()
18 | self.headers = headers
19 |
20 | # 提取页面源码,返回值为页面源码
21 | def Extract_html(self, url):
22 | try:
23 | re = requests.get(url, headers=self.headers, timeout=10, verify=False, allow_redirects=False)
24 | if re.status_code == 200:
25 | cont = re.content
26 | charset = chardet.detect(cont)['encoding']
27 | raw = cont.decode(charset)
28 | return raw
29 | else:
30 | return None
31 | except Exception as e:
32 | return None
33 |
34 | # 自动选择协议头
35 | def Auto_select_protocol(self, url):
36 | try:
37 | scheme = urlparse(url).scheme
38 | # 自动判断url地址是否添加http/https协议
39 | if "http" in scheme or "https" in scheme:
40 | return url
41 | else:
42 | if self.Extract_html("http://" + url) == None:
43 | if self.Extract_html("https://" + url) == None:
44 | tqdm.write("【扫描失败】:{}\n".format(str(url)))
45 | return False
46 | else:
47 | url = "https://" + url
48 | else:
49 | url = "http://" + url
50 | return url
51 | except Exception as e:
52 | return False
53 |
54 | # 调用phantomjs模仿浏览器请求
55 | def phantomjs_requests(self, url):
56 | ua = ""
57 | if "user-agent" in self.headers:
58 | ua = self.headers["user-agent"]
59 | if "User-Agent" in self.headers:
60 | ua = self.headers["User-Agent"]
61 | content = ""
62 | js_array = []
63 | sys = platform.system()
64 | if sys == "Windows":
65 | jspath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), 'config', 'phantomjs_windows.exe')
66 | elif sys == "Linux":
67 | jspath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), 'config', 'phantomjs_liunx_x64')
68 | else:
69 | return False
70 | try:
71 | desired_capabilities = DesiredCapabilities.PHANTOMJS.copy()
72 | ua = FileOperation().uarand()
73 | for key, value in self.headers.items():
74 | desired_capabilities['phantomjs.page.customHeaders.{}'.format(key)] = value
75 | desired_capabilities["phantomjs.page.settings.userAgent"] = ua
76 | desired_capabilities["phantomjs.page.settings.resourceTimeout"] = True
77 | desired_capabilities["phantomjs.page.settings.disk-cache"] = True
78 | desired_capabilities["phantomjs.page.settings.loadImages"] = False
79 | driver = webdriver.PhantomJS(executable_path=jspath, desired_capabilities=desired_capabilities, service_args=['--ignore-ssl-errors=true'], service_log_path=os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))), 'tmp', 'ghostdriver.log'))
80 | driver.set_page_load_timeout(10)
81 | driver.set_script_timeout(5)
82 | driver.get(url)
83 | content = driver.page_source
84 | if urlparse(driver.current_url).netloc != urlparse(url).netloc:
85 | content = self.Extract_html(url)
86 | else:
87 | # 解决layui模块加载问题
88 | js = """
89 | var jspath = []
90 | for(var i=0;i<=document.scripts.length;i++){
91 | try {
92 | if(document.scripts[i].src != ""){
93 | jspath.push(document.scripts[i].src)
94 | }
95 | } catch(err){
96 | break;
97 | }
98 | }
99 | return jspath
100 | """
101 | js_array = driver.execute_script(js)
102 | return content, js_array
103 | except Exception as e:
104 | content = self.Extract_html(url)
105 | return content, js_array
--------------------------------------------------------------------------------
/lib/common/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import time
5 | import re
6 | from prettytable import PrettyTable
7 | from uuid import uuid4
8 | from urllib.parse import urlparse
9 |
10 | class Utils():
11 |
12 | def __init__(self):
13 | self.White = ["w3.org", "example.com", "purl.org", "microsoft.com", "openxmlformats.org", "purl.oclc.org", "docs.oasis-open.org", "openoffice.org", "raphaeljs.com", "bing.com", "wallstreetcn.com", "mozilla.org", "mozilla.org"]
14 |
15 | # 使用set对列表去重,并保持列表原来顺序
16 | def unique(self, arr):
17 | arr1 = list(set(arr))
18 | arr1.sort(key=arr.index)
19 | return arr1
20 |
21 | # 白名单判断是否过滤那个域名
22 | def White_list_domain(self, domain=""):
23 | for t in range(len(self.White)):
24 | if self.White[t] in domain:
25 | return False
26 | return True
27 |
28 | # 返回一个列表,内容为目标字符在字符串中的位置 [3, 7, 10]
29 | def find_last(self, string, str):
30 | positions = []
31 | last_position = -1
32 | while True:
33 | position = string.find(str, last_position + 1)
34 | if position == -1: break
35 | last_position = position
36 | positions.append(position)
37 | return positions
38 |
39 | # 随机生成uuid
40 | def random_uuid(self):
41 | # 声明全局uuid
42 | return str(uuid4()).split('-')[-1]
43 |
44 | # 表格输出
45 | def gettable(self, field_names=[], title="", array=[], path=""):
46 | if len(array) != 0:
47 | tb = PrettyTable(align="l", header=True, padding_width=5, field_names=field_names, title=title)
48 | count = 0
49 | for i in range(len(array)):
50 | count = count + 1
51 | tb.add_row(array[i])
52 |
53 | if path != "":
54 | self.save_result(path, tb.get_string())
55 |
56 | # 写入文件
57 | def save_result(self, filename="", content="", jurisdiction="at"):
58 | fp = open(filename, jurisdiction, encoding='utf-8')
59 | fp.write(content + "\n")
60 | fp.close()
61 |
62 | # 时间输出
63 | def tellTime(self):
64 | localtime = "[" + str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))) + "] "
65 | return localtime
66 |
67 | # 判断数组中是否存在某个值
68 | def is_array_value(self, array=[], value=""):
69 | if value == "":
70 | return False
71 | for t in range(len(array)):
72 | if array[t] in value:
73 | return False
74 | return True
75 |
76 | # 获取文件名
77 | def get_filename(self, url_str):
78 | url = urlparse(url_str)
79 | i = len(url.path) - 1
80 | while i > 0:
81 | if url.path[i] == '/':
82 | break
83 | i = i - 1
84 | filename = url.path[i+1:len(url.path)]
85 | if not filename.strip():
86 | return False
87 | return filename
88 |
89 | # headers头部信息处理
90 | def handle_headers(self, primaryheaders={}, new_headers=""):
91 | new_headers = new_headers.split("\\n")
92 | while "" in new_headers:
93 | new_headers.remove("")
94 | for i in new_headers:
95 | if ": " not in i:
96 | break
97 | primaryheaders[i.split(": ")[0]] = i.split(": ")[1]
98 | return primaryheaders
99 |
100 | # 过滤文件内容
101 | def filter_content(self, content=""):
102 | if len(content.strip()) > 2:
103 | if re.match(r"^./(.*)", content.strip(), re.M|re.I) == None:
104 | if re.match(r"^/(.*).js$", content.strip(), re.M|re.I) == None:
105 | filter = ["DD/MM/YYYY", "MM/DD/YYYY", "MM/D/YYYY", "YYYY/MM/DD", "YYYY/MM/DD", "YYYY/M/D", "DD/M/YYYY", "application/x-www-form-urlencoded", "multipart/form-data", "application/xml", "application/json", "/./", "image/webp", "application/octet-stream", "text/html", "image/jpeg", "image/gif", "image/png", "text/plain", "text/xml", "static/js", "text/css", "static/css", "static/js/", "static/css/", "http://www.w3.org/2000/svg", "http://www.w3.org/1999/xlink", "http://www.w3.org/1998/Math/MathML", "http://www.w3.org/TR/REC-html40", "http://www.w3.org/2000/xmlns/", "http://www.w3.org/XML/1998/namespace"]
106 | if content.strip() not in filter:
107 | return True
108 | return False
--------------------------------------------------------------------------------
/lib/vuln/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xz-zone/Webpackfind/ad573bdeeea44cacd464622c34d71db36403ae67/lib/vuln/__init__.py
--------------------------------------------------------------------------------
/lib/vuln/unauthorized.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import re, requests, chardet, os
5 | from lib.common.utils import Utils
6 | from lib.common.regular import Regular
7 | from requests.packages import urllib3
8 | from urllib.parse import urlparse
9 | from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED
10 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
11 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
12 | from lib.common.Printlog import Printlog
13 |
14 |
15 | # 未授权检测
16 | class unauthorized():
17 |
18 | def __init__(self, domainlog="", headers={}, domain="", rulesJson=[], path=""):
19 | urllib3.disable_warnings()
20 | self.headers = headers
21 | self.rulesJson = rulesJson
22 | self.domain = domain
23 | self.path = path
24 | self.api_result = []
25 | self.rules_result = []
26 | self.domainlog = domainlog
27 |
28 | def run(self, filepath=[]):
29 |
30 | self.log = Printlog(self.path).get_logger()
31 |
32 | self.log.info("{} 【{}】 未授权检测模块加载完成".format(Utils().tellTime(), self.domainlog))
33 |
34 | if len(filepath) > 0:
35 |
36 | # 创建线程池
37 | pool = ThreadPoolExecutor(len(filepath))
38 |
39 | allTask = [pool.submit(self.api_unauthorized, path) for path in filepath]
40 |
41 | # 开启异步线程池
42 | wait(allTask, return_when=ALL_COMPLETED)
43 |
44 | # 表格方式输出
45 | Utils().gettable(["URL", "标题", "响应状态"], "扫描网站:{}".format(self.domain), self.api_result, os.path.join(self.path, 'table_result.txt'))
46 |
47 | Utils().gettable(["模块", "名称", "URL", "内容"], "扫描网站:{}".format(self.domain), self.rules_result, os.path.join(self.path, 'table_result.txt'))
48 |
49 | self.log.info("{} 【{}】 未授权检测模块结果保存路径:{}".format(Utils().tellTime(), self.domainlog, os.path.join(self.path, 'table_result.txt')))
50 |
51 | # log日志输出
52 | self.log.info("{} 【{}】 未授权检测模块扫描完成".format(Utils().tellTime(), self.domainlog))
53 |
54 | # API未授权探测并检测接口标题
55 | def api_unauthorized(self, filename=""):
56 | try:
57 | fopen = open(filename, 'r', encoding='utf-8')
58 | data = str(fopen.read())
59 | fopen.close()
60 | data = Regular().Extract_URL(data)
61 | if len(data) != 0:
62 | for i in range(len(data)):
63 | data[i] = data[i].replace('./', '/').replace('\\', '')
64 | if data[i][0:2] != "//" and data[i][0] == "/":
65 | try:
66 | if Utils().filter_content(data[i].replace('', '')):
67 | url = self.domain + data[i].replace('', '')
68 | status_code = 200
69 | info = requests.get(url, headers=self.headers, timeout=10, verify=False, allow_redirects=False)
70 | try:
71 | cont = info.content
72 | charset = chardet.detect(info)['encoding']
73 | html_doc = cont.decode(charset)
74 | title = re.findall('