第6章:Burpsuite 扩展

发布于 2022-04-19  1254 次阅读


写在前面

  这一章中主要介绍了BurpSuite工具的扩展模块如何去编写,整体学习下来的感受呢,讲真并不太好。这里主要是因为Java语言和Python的不同、函数的调用方法不同等等问题,最后我还是觉得这块如果想要深入的话还是学习Java,然后再去开发BurpSuite的扩展会比较好。
  这一章的脚本我并没有进行测试,所以说呢,我并不知道是否真的能够使用,可能还会有一些错误(中间有一部分是我自己进行了更改)。这些对于我来说就当做是扩宽知识面、了解一些Python如何去编写BurpSuite扩展就OK了,其他就不再继续深究了。
  哦对了忘了说一句,这涨所有的东西想要运行起来的话需要自行准备BurpSuite和jython两个环境。

bhp_fuzzer.py

代码

import burp
import random

from java.util import ArrayList, List

class BurpExtender(burp.IBurpExtender,burp.IIntruderPayloadGeneratorFactory):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()

        callbacks.registerIntruderPayloadGeneratorFactory(self)

        return

    def getGeneratorName(self):  # type: () -> str
        return "BHP Payload Generator"

    def createNewInstance(self, attack):  # type: (IIntruderAttack) -> IIntruderPayloadGenerator
        return BHPFuzzer(self, attack)

class BHPFuzzer(burp.IIntruderPayloadGenerator):
    def __init__(self,extender,attack):
        self._extender = extender
        self._attack = attack
        self._helpers = extender._helpers
        self.max_payloads = 10
        self.num_iterattions = 0

        return

    def hasMorePayloads(self):  # type: () -> bool
        if self.num_iterattions == self.max_payloads:
            return False
        else:
            return True

    def getNextPayload(self, baseValue):  # type: (array) -> attay
        # getNextPayload获取的是一个byte[]对象,所以Python这里需要进行bytes to string
        # Python3 使用str()函数或decode()函数进行处理
        payload = "".join(str(x) for x in baseValue)
        # 将所有的payload拼接成一个字符串,下面在mutate_payload函数中会用随机数offset进行切片取值
        payload = self.mutate_payload(payload)
        self.num_iterattions += 1
        return payload

    def reset(self):  # type: () -> None
        self.num_iterattions = 0
        return

    def mutate_payload(self,original_payload):
        picker = random.randint(1,3)

        offset = random.randint(0,len(original_payload)-1)
        payload = original_payload[:offset]
        # 切片取值

        if picker == 1:
            payload += "'"

        if picker == 2:
            payload += "<script>alert(1)</script>"

        if picker == 3:
            chunk_length = random.randint(len(payload[offset:]),len(payload)-1)
            repeater = random.randint(1,10)
            for i in range(repeater):
                payload += original_payload[offset:offset+chunk_length]

        payload += original_payload[offset:]

        return payload

关于这个脚本

  这个脚本主要有三个测试内容,插入一个',来测试SQL注入;插入一个反射型XSS的Payload,测试XSS漏洞;最后一个功能比较迷惑,切片截取标记的片段的部分,这个标记片段是从BurpSuite的intruder模块中标记的,然后将这个截取的部分重新插入到标记片段处。
  总的来说没什么功能,仅仅只是让你知道一下最基本burp扩展Python脚本的写法,这个扩展并没有什么实际的作用
  另外脚本里不能写入中文注释,否则在导入扩展时会报出编码错误。

bhp_bing.py

代码

import burp

from javax.seing import JMenuItem
from java.util import List,ArrayList
from java.net import URL

import socket
import urllib
import json
import re
import base64
import numpy as np

bing_api_key = ""

class BurpExtender(burp.IBurpExtender,burp.IContextMenuFactory):
    def registerExtenderCallbacks(self, callbacks):  # type: (IBurpExtenderCallbacks) -> None
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        self.context = None

        callbacks.setExtensionName("BHP Bing")
        callbacks.registerContextMenuFactory(self)

        return

    def createMenuItems(self, invocation):  # type: (IContextMenuInvocation) -> List[JMenuItem]
        self.context = invocation
        menu_list = ArrayList()
        menu_list.add(JMenuItem("Send to Bing",actionPerformed=self.bing_menu))
        return menu_list

    def bing_menu(self,event):
        http_traffic = self.context.getSelectedMessages()
        print("%d requests highlighted" % len(http_traffic))

        for traffic in http_traffic:
            http_service = traffic.getHttpService()
            host = http_service.getHost()
            print("User selected host: %s" % host)
            self.bing_search(host)

        return

    def bing_search(self,host):
        is_ip = re.match("[0-9]+(?:\.[0-9]+){3}",host)

        if is_ip:
            ip_address = host
            domain = False
        else:
            ip_address = socket.gethostbyname(host)
            domain = True

        bing_query_string = "'ip:%s'" % ip_address
        self.bing_query(bing_query_string)

        if domain:
            bing_query_string = "'domain:%s'" % host
            self.bing_query(bing_query_string)

    def bing_query(self,bing_query_string):
        print("Performing Bing search: %s" % bing_query_string)

        quoted_query = urllib.parse.quote(bing_query_string,safe="(:/)")

        requests_url = f"https://api.datamarket.azure.com/Bing/Search/Web?$format=json&$top=20&Query={quoted_query}"
        headers =[
            "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
            "Connection: close",
            f"Authorization:Basic: {base64.b64encode(bing_api_key)}"
        ]
        boby = "test"
        requests_body = self._helpers.buildHttpMessage(headers,boby)
        json_boby = self._callbacks.makeHttpRequest(requests_url,443,True,requests_body).tostring()

        json_boby = json_boby.split("\r\n\r\n",1)[1]

        try:
            r = json.loads(json_boby)

            if len(r["d"]["results"]):
                for site in r["d"]["results"]:
                    print("*" * 100)
                    print(site['Title'])
                    print(site["Url"])
                    print(site["Description"])
                    print("*" * 100)
                    j_url = URL(site['Url'])
                if not self._callbacks.isInScope(j_url):
                    print("Adding to Burp scope")
                    self._callbacks.includeInScope(j_url)

        except:
            print("No results from Bing")
            pass

        return

关于这个脚本

  这个脚本还是比较头大的,我这里并没有注册Bing API,也就没有测试,中间最主要的一点就是如何让BurpSuite发送一个HTTP请求,这么说可能会显得有点简单,最为主要的是如何发送一个符合自己预期的HTTP请求,比如说设置HTTP Body、更换或设置headers
  其次,这里所使用的是BurpSuite所提供的函数,跟以往我们自己使用Python是完全不同,并且由于是BurpSuite提供的函数,那么就会带有部分Java的特性,比如说Python没有的数据类型——数组
  解决以上问题我给出的办法就是——学Java吧,用Java开发这个BurpSuite扩展。就这样

bhp_wordlist.py

代码

from burp import IBurpExtender
from burp import IContextMenuFactory

from javax.swing import JMenuItem
from java.util import List,ArrayList
from java.net import URL

import re
from datetime import datetime
from html.parser import HTMLParser

class TagStripper(HTMLParser):
    '''
        去掉HTTP响应包中的HTML标签
    '''

    def __init__(self):
        super(TagStripper, self).__init__()
        self.page_text = []

    def handle_data(self, data: str) -> None:
        self.page_text.append(data)

    def handle_comment(self, data: str) -> None:
        self.handle_data(data)

    def strip(self,html):
        self.feed(html)
        return "".join(self.page_text)

class BurpExtender(IBurpExtender, IContextMenuFactory):
    def __init__(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        self.context = None
        self.host = set()
        self.wordlist = set(["password"])

        callbacks.setExtensionName("BHP Wordlist")
        callbacks.registerContextMenuFactory(self)

        return

    def createMenuItems(self, invocation):  # type: (IContextMenuInvocation) -> List[JMenuItem]
        self.context = invocation
        menu_list = ArrayList()
        menu_list.add(JMenuItem("Create Wordlist",actionPerformed=self.wordlist_menu))

        return menu_list

    def wordlist_menu(self,event):
        http_traffic = self.context.getSelectedMessages()

        for traffic in http_traffic:
            http_service = traffic.getHttpService()
            host = http_service.getHost()

            self.host.add(host)

            http_response = traffic.getResponse()

            if http_response:
                self.get_words(http_response)

        self.display_wordlist()

        return

    def get_words(self,http_response):
        headers,body = http_response.tostring().strip('\r\n\r\n',1)

        if headers.lower().find("content-type: text") == -1:
            return

        tag_stripper = TagStripper()
        page_text = tag_stripper.strip(body)

        pattern = re.compile(r'[a-zA-Z]\w{2,}',re.S)
        words = re.findall(pattern, page_text)

        for word in words:
            if len(word) <= 12:
                self.wordlist.add(word.lower())

        return

    def mangle(self,word):
        year = datetime.now().year
        suffixes = ["","1","!",year]
        mangled = []

        for password in (word,word.capitalize()):
            for suffix in suffixes:
                mangled.append(f"{password,suffix}")

        return mangled

    def display(self):
        print(f"#!comment: BHP Wordlist for site(s) %s" % ",".join(self.hosts))

        for word in sorted(self.wordlist):
            for password in self.mangle(word):
                print(password)

        return

关于这个脚本

  这个我没有进行过多的研究,简单来说这里摆烂了,对我来说感觉并没有太大的作用。程序里最主要起作用的是get_words(self,http_response)然后改了一下他正则的写法,最后由mangle(self,word)对获取的字段进行加工。

其中一些使用的BurpSuite中的方法

makeHttpRequest()

这个方法有两种写法:

  • IHttpRequestResponse makeHttpRequest( IHttpService httpService, byte[] request, boolean forceHttp1)

  • byte[] makeHttpRequest(java.lang.String host, int port, boolean useHttps, byte[] request, boolean forceHttp1)

IHttpRequestResponse makeHttpRequest()

  byte[] request对象可以使用Helpers.buildHttpMessage()进行创建,Python调用方法为self._helpers.buildHttpMessage()。对应makeHttpResquest()Python的调用方法为self._callbacks.makeHttpResquest()
  在Python中,buildHttpMessage方法第一个参数为List [],第二个参数为array,但是这里处理数组的时候有一些问题。众所周知——Python是没有数组的,有的是listtupledict,有一说是list具有其他语言中的数组特性。这个说法并没能进行验证,Pycharm中使用list一直会报错type error

List < String > headers = new ArrayList <> ();
headers.add("POST /console/j_security_check HTTP/1.1");
headers.add("Host: " + url.getHost() + ":" + url.getPort());
headers.add("Content-Type: application/x-www-form-urlencoded");
headers.add("Cookie:ADMINCONSOLESESSION=pTsBVcsdVx2g20mxPJyyPDvqTwQmQDtw7R541DGJGGXD2qh4rDBJ!1211788216");

String user = "user";
String pwd = "pwd";
body = "userName=" + user + "&password=" + pwd + "&submit=+Login+";

byte[] loginMessage = helpers.buildHttpMessage(headers, body.getBytes());
IHttpRequestResponse resp = callbacks.makeHttpRequest(baseRequestResponse.getHttpService(), loginMessage);
byte[] httpResponse = resp.getResponse();

// 另一种request创建
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
IHttpRequestResponse checkUUID = this.callbacks.makeHttpRequest(httpService, insertionPoint.buildRequest(this.helpers.stringToBytes(uuid)));

  另外,IHttpService对象在burp中可以通过抓取的数据包所生成的对象通过.getHttpService()方法进行生成,可以直接利用,如果需要自定义目标则需要使用第二种写法。

byte[] makeHttpRequest()

  这个方法说实话有点懵,怎么说呢,byte[] request这个对象一直没有弄明白怎么写,看了一些Demo,很多人选择直接下面的方式使用方法

URL url = new URL(VERSION_URI + "?v=" +
      currentVersion.getVersionString() +
      "&t=" +
      (automatic ? "a" : "m") + // reports if automated or manual update
      "&b=" +
      (CO2Config.isLoadedFromBappStore(AboutTab.this.callbacks) ? "y" : "n") // loaded from a bappstore version?
  );
byte[] request = callbacks.getHelpers().buildHttpRequest(url);
byte[] response = callbacks.makeHttpRequest("burpco2.com", 80, false, request);

参考资料

写在最后

  这一章学起来还是比较吃力,但是也不用过度纠结,你说自己看懂书中,没关系,书中很多都是结合Java的内容去编写的,如果真的想去研究这部分,建议先熟悉BurpSuite API,知道API中有哪些方法,什么方法可以起到什么作用,什么方法在Python中怎么调用?在Java中怎么调用?这个过程势必会消耗很大一笔时间。
  个人看来,这一章起到一个介绍的作用吧,如果感兴趣当然可以继续深究,如果不感兴趣,也不会有什么过多的影响,知道有这么个东西就行了。