274
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
# @Time    : 2019/10/25 15:25
4
# @Author  : xiaodong
5
# @github  : https://github.com/dongfangyuxiao/
6
# @Site    : #
7
# @File    : Burp_Dir.py
8
# @Software: PyCharm
9
import threading
10
import re
11
from burp import IBurpExtender
12
from burp import IHttpListener
13
from burp import IHttpRequestResponse
14
from burp import IResponseInfo
15
from burp import IRequestInfo
16
from burp import IHttpService
17
from burp import IScannerCheck
18
from burp import IScannerInsertionPointProvider
19
from burp import IParameter
20
from burp import IScanIssue
21
from java.net import URL
22
from urlparse import urlparse  #我自己的环境是py3,但bp只支持py2,这里没有导入没关系的
23
import urllib2
24
class FuzzLFI:
25
    def __init__(self,):
26
        # 目录扫描敏感目录
27
        self.dir = [
28
            #目录参考李姐姐的https://github.com/lijiejie/BBScan/
29
            'config/config_ucenter.php.bak',
30
            'config/.config_ucenter.php.swp',
31
            'config/.config_global.php.swp',
32
            'config/config_global.php.1',
33
            'uc_server/data/config.inc.php.bak',
34
            'config/config_global.php.bak',
35
            'include/config.inc.php.tmp',
36
            #discuz敏感目录,正则匹配<?php>
37
            'access.log',
38
            'www.log',
39
            'error.log',
40
            'log.log',
41
            'sql.log',
42
            'errors.log',
43
            'debug.log',
44
            'db.log',
45
            'install.log',
46
            'server.log',
47
            'sqlnet.log',
48
            'WS_FTP.log',
49
            'database.log',
50
            'data.log',
51
            'app.log',
52
            'log.tar.gz',
53
            'log.rar',
54
            'log.zip',
55
            'log.tgz',
56
            'log.tar.bz2',
57
            'log.7z',
58
            #日志文件
59
            'WEB-INF/classes',
60
            'jPlayer',
61
            'jwplayer',
62
            'extjs',
63
            'swfupload',
64
            'boss',
65
            'editor',
66
            'ckeditor',
67
            'htmedit',
68
            'htmleditor',
69
            'ueditor',
70
            'tomcat',
71
            'output',
72
            'fck',
73
            'cgi-bin',
74
            'admin',
75
            'bak',
76
            'backup',
77
            'conf',
78
            'config',
79
            'db',
80
            'debug',
81
            'data',
82
            'database',
83
            'deploy',
84
            'WEB-INF',
85
            'install',
86
            'manage',
87
            'manager',
88
            'monitor',
89
            'tmp',
90
            'temp',
91
            'test',
92
            'RPC2',
93
            'wp-config.php.inc',
94
            'wp-config.inc',
95
            'wp-config.bak',
96
            'wp-config.php~',
97
            '.wp-config.php.swp',
98
            'wp-config.php.bak',
99
            'auto/config',
100
            'configprops',
101
            'beans',
102
            'dump',
103
            'env',
104
            'env/{name}',
105
            'health',
106
            'info',
107
            'metrics',
108
            'shutdown',
109
            'trace',
110
            'manage',
111
            '.svn/entries',
112
            'code',
113
            'phpmyadmin',
114
            'cache',
115
            'compile',
116
            'data1',
117
            'entries',
118
            'storage',
119
            'upload',
120
            'add',
121
            'test.zip',
122
            '.zip',
123
            'www.rar',
124
            'foo/default/master/..%252F..%252F..%252F..%252Fetc%252fpasswd',
125
            '..%252F..%252F..%252F..%252Fetc%252fpasswd',
126
127
        ]
128
129
class BurpExtender(IBurpExtender, IScannerCheck):
130
131
    def registerExtenderCallbacks(self, callbacks):
132
        self._callbacks = callbacks
133
        self._helpers = callbacks.getHelpers()
134
        self._callbacks.registerScannerCheck(self)  # 注册扫描插件
135
        self._callbacks.setExtensionName("DIR")  # 定义插件的名称
136
        print("for dir scan")
137
        self.fuzzLFI = FuzzLFI()  # 引入上面定义的类
138
        self.lfiD = []
139
    # 以上都是标准化程序
140
141
    def doPassiveScan(self, baseRequestResponse):
142
        lfiTarget = []
143
        Request = baseRequestResponse.getRequest()
144
        analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters = self.get_request_info(Request)
145
        httpService = baseRequestResponse.getHttpService()
146
        host, port, protocol, ishttps, = self.get_server_info(httpService)
147
        targetHost = ['saicmaxus', 'saicmotor', 'maxuscloud', '224', '226']
148
        url = self.get_request_url(protocol,reqHeaders)
149
        #link = reqHeaders[0].split(' ')[1]
150
151
        dir_black  =['.html','.gif','.js','.css','.woff','.jpg','.png','.jpeg']
152
        if (host.split('.')[-3] in targetHost) or (host.split('.')[-2] in targetHost):  # 多级或单级或类似baidu.net.cn的形势取主域名字段,比对是否在目标列表中
153
            url = urlparse(url)
154
            if url.path.endswith not in dir_black:
155
                if url.path.endswith('/'):
156
                    url = url.scheme + '://' + url.netloc + url.path
157
                else:
158
                    url = url.scheme + '://' + url.netloc + url.path + '/'
159
            #print(url)
160
                html404_status, html404_content = self.html_404_analyze(url)
161
                self.DirScan(Request, protocol, host, port, ishttps, url,html404_status, html404_content)
162
163
164
    def consolidateDuplicateIssues(self, existingIssue, newIssue):
165
166
        return 0
167
168
    def doActiveScan(self, baseRequestResponse, insertionPoint):
169
        # report the issue
170
        return None
171
172
    def DirScan(self, request, protocol, host, port, ishttps, url,html404_status, html404_content):
173
174
175
        for paraNewValue in self.fuzzLFI.dir:
176
            newRequest = self._helpers.buildHttpRequest(URL(url+paraNewValue));  # 更新url值#这个地方一定要指定URL类型的,不然重组的时候会报错
177
            #print(url+paraNewValue)
178
            newResponse = self._callbacks.makeHttpRequest(host, port, ishttps, newRequest);  # 生成http请求
179
            newAnalyzedRequest, newReqHeaders, newReqBodys, newReqMethod, newReqParameters = self.get_request_info(
180
                newRequest)  # 获取请求包信息(如果存在可以获取payload)
181
            # analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters = self.get_request_info(newRequest)
182
            # url = self.get_request_url(protocol,reqHeaders)
183
            # print(url)
184
            newResHeaders, newResBodys, newResStatusCode, resLength = self.get_response_info(
185
                newResponse)  # 获取返回包头、body内容、和状态信息(这个是sql注入的判断的一个依据)
186
            #print ("404html status {} ".format(html404_status))
187
            #print "response status {} ".format(newResStatusCode)
188
            if newResStatusCode ==206 or ((newResStatusCode ==200) & (newResStatusCode!=html404_status)):
189
                newReqUrl = self.get_request_url(protocol, newReqHeaders)
190
                content = '[+]{} -> {}\n[Headers] ->'.format('[DirScan GET]', newReqUrl,
191
                                                                               newReqHeaders)
192
                print(content)
193
                self.save(content + '\t\n')
194
                print('-' * 50)
195
                break
196
197
198
199
    # 获取响应的一些信息:响应头,响应内容,响应状态码
200
    def html_404_analyze(self, url):
201
        url = url+'justfortesthtmlshifoucunzai.html'
202
        print url
203
        try:
204
            html_404 = urllib2.urlopen(url)
205
            return html_404.code, html_404.read()
206
        except Exception as e:
207
            return None,None
208
            pass
209
210
        # 获取服务端的信息,主机地址,端口,协议
211
212
    def get_server_info(self, httpService):
213
        host = httpService.getHost()
214
        port = httpService.getPort()
215
        protocol = httpService.getProtocol()
216
        ishttps = False
217
        if protocol == 'https':
218
            ishttps = True
219
        return host, port, protocol, ishttps
220
221
        # 获取请求的参数名、参数值、参数类型(get、post、cookie->用来构造参数时使用)
222
223
    def get_response_info(self, response):
224
        analyzedResponse = self._helpers.analyzeResponse(
225
            response)  # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
226
        resHeaders = analyzedResponse.getHeaders()  # getHeaders方法用于获取响应中包含的HTTP标头。返回:响应中包含的HTTP标头。
227
        resBodys = response[
228
                   analyzedResponse.getBodyOffset():].tostring()  # getBodyOffset方法用于获取消息正文开始的响应中的偏移量。返回:消息正文开始的响应中的偏移量。response[analyzedResponse.getBodyOffset():]获取正文内容
229
        resStatusCode = analyzedResponse.getStatusCode()  # getStatusCode获取响应中包含的HTTP状态代码。返回:响应中包含的HTTP状态代码。
230
        for header in resHeaders:
231
            if 'Content-Length' in header:
232
                resLength = int(header.split(':')[1].strip())  # 我在这里增加了一个返回包长度检测
233
            else:
234
                resLength = int(0)
235
        return resHeaders, resBodys, resStatusCode, resLength
236
237
    def get_parameter_Name_Value_Type(self, parameter):
238
        parameterName = parameter.getName()
239
        parameterValue = parameter.getValue()
240
        parameterType = parameter.getType()
241
        return parameterName, parameterValue, parameterType
242
243
        # 获取请求的url
244
245
    def get_request_url(self, protocol, reqHeaders):
246
        link = reqHeaders[0].split(' ')[1]
247
        host = reqHeaders[1].split(' ')[1]
248
        if 'http' in link:
249
            return link
250
        else:
251
            return protocol + '://' + host + link
252
253
    # 保存结果
254
    def save(self, content):
255
        print(content)
256
        f = open('burp_lfi.txt', 'at')
257
        f.writelines(content + '\n\n')
258
        f.close()
259
260
    def saveUrl(self, content):
261
        print(content)
262
        f = open('burp_url.txt', 'at')
263
        f.writelines(content + '\n\n')
264
        f.close()
265
266
    def get_request_info(self, request):
267
        analyzedRequest = self._helpers.analyzeRequest(
268
            request)  # analyzeRequest用于分析HTTP请求,并获取有关它的各种关键详细信息。生成的IRequestInfo对象
269
        reqHeaders = analyzedRequest.getHeaders()  # 用于获取请求中包含的HTTP头。返回:请求中包含的HTTP标头。
270
        reqBodys = request[analyzedRequest.getBodyOffset():].tostring()  # 获取消息正文开始的请求中的偏移量。返回:消息正文开始的请求中的偏移量。
271
        reqMethod = analyzedRequest.getMethod()  # 获取请求方法
272
        reqParameters = analyzedRequest.getParameters()
273
        return analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters
274
思路就是从获取的url中提取path信息,然后加上列表中的进行扫描
策略就是
    1、返回状态码206,为下载,一般是压缩和目录文件
    2、返回状态码200,但是和我定义的404页面内容不一样就认为是的。
    3、如果返回404,就认为不存在