Burp Suite插件开发Python 1ndex

Burp Suite插件开发-Python

​ 相较于挖SRC,我还是喜欢做一点东西。由于我对JAVA并不是很了解,所以选择Python来做插件开发。在学习过程中遇到很多坑,写个文档用于记录。由于我也不熟悉UI相关的开发,所以UI相关的类暂时就不写了

前言

熟读官方的API文档

打开导出的 index.html

基础模板

BURP 插件的开发可以简单理解为对API类的继承以及对方法的重写。

#coding=utf-8
from burp import IBurpExtender
import sys

if sys.version[0] == '2':  # 为什么要判断是不是python2呢?鬼知道后面jython会不会支持python3
    reload(sys)
    sys.setdefaultencoding("utf-8") #设置系统编码,否则碰到中文的地方都会报错(即使是设置了coding也没用)
    
    
class BurpExtender(IBurpExtender):  # 所有插件都必须实现BurpExtender类,并且继承IBurpExtender
    def registerExtenderCallbacks(self,callbacks): # 该方法在启动插件时会自动调用,用于注册插件
        # callbacks是一个IBurpExtenderCallbacks,里面提供了很多基础方法,如注册监听器等
        callbacks.setExtenderName("MyExt")  # 设置插件名称
    def Myfunc(self,paras): # 自定义方法
        pass

上面的代码实现了一个没有任何功能的插件

Python基础知识

在阅读其他人写的Burp插件时,常常会在registerExtenderCallbacks函数中看到self._callbacks = callbacks,此举是用于注册全局变量,方便在其他函数中使用该变量(后面会经常用到),且self对象只会在插件卸载时才会被释放

注意:重写的方法或者自定义的方法第一个参数均为self,因为是类中的方法。且调用自定义方法需要使用 self.Myfunc() 的方式调用

常用API介绍

Burp的API大体可以分为5类:插件入口类、监听类、工具类、HTTP消息类、UI类

为了后续实操,所以先将会用到的类简单介绍一下,重点需要注意方法的参数及返回值两者的数据类型

插件入口类

IBurpExtender

所有扩展都必须实现这个接口。也就是如上方模板所写,BurpExtender 必须继承 IBurpExtender 类

该类下面只有一个方法,并只会在启动插件的时候调用一次。因此常在该方法内执行一些初始化操作,如定义插件名称、初始化UI、注册全局变量、注册监听器

注意:打开BURP时会启动插件,所以也会调用一次该方法

registerExtenderCallbacks

void	registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)

可以看到,方法内的参数为一个 IBurpExtenderCallbacks 对象,具体可看下面

IBurpExtenderCallbacks

Burp Suite 使用此接口将一组回调方法传递给扩展,扩展可以使用这些方法在 Burp 中执行各种操作。可能不是很好理解,简单来说就是该类提供了很多方法,用来告诉BURP应该在什么时候干什么事的

变量

查看API文档首先可以看到,该类下面有一些静态变量。这些变量常用来判断HTTP消息是从哪个地方传过来的,如 Proxy、Repeater、Intruder等,常在IHttpListener的processHttpMessage方法内用到,暂时可以不用管

方法

只涉及常用方法讲解

printError
void	printError(java.lang.String error)

输出错误信息

printOutput
void	printOutput(java.lang.String output)

输出一般信息

setExtensionName
void	setExtensionName(java.lang.String name)

用于设置插件名称

getProxyHistory
IHttpRequestResponse[]	getProxyHistory()

用于获取 Proxy 模块内所有的历史请求,并返回一个 IHttpRequestResponse 类型的数组。如果想在安装插件的时候自动扫描历史请求,可以使用这个方法

registerProxyListener
void	registerProxyListener(IProxyListener listener)

用于注册Proxy监听器,监听Proxy模块正在处理的请求和响应的通知。

registerHttpListener
void	registerHttpListener(IHttpListener listener)

用于注册HTTP监听器,监听所有模块(Proxy、Repeater等模块)正在处理的请求和响应的通知。

registerExtensionStateListener
void	registerExtensionStateListener(IExtensionStateListener listener)

用于注册插件状态监听(在卸载插件时需要)

registerContextMenuFactory
void	registerContextMenuFactory(IContextMenuFactory factory)

用于为自定义上下文菜单项注册工厂(注册右键菜单时需要)

还有很多类似的register方法

saveBuffersToTempFiles
IHttpRequestResponsePersisted	saveBuffersToTempFiles(IHttpRequestResponse httpRequestResponse)

用于将请求、响应消息对象保存到临时文件中,减少内存开销

saveToTempFile
ITempFile	saveToTempFile(byte[] buffer)

用于将传入的buffer保存到临时文件,如byte类型请求响应等,并返回一个ITempFile对象

saveConfigAsJson
java.lang.String	saveConfigAsJson(java.lang.String... configPaths)

用于获取BURP项目级别的配置,传入configPaths可获取指定配置,可配合saveTempfile保存文件

loadConfigFromJson
void	loadConfigFromJson(java.lang.String config)

导入项目配置文件

makeHttpRequest
IHttpRequestResponse	makeHttpRequest(IHttpService httpService, byte[] request)

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

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

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

用于 发起 HTTP/1请求

makeHttp2Request
byte[]	makeHttp2Request(IHttpService httpService, java.util.List<IHttpHeader> headers, byte[] body)

byte[]	makeHttp2Request(IHttpService httpService, java.util.List<IHttpHeader> headers, byte[] body, boolean forceHttp2)

byte[]	makeHttp2Request(IHttpService httpService, java.util.List<IHttpHeader> headers, byte[] body, boolean forceHttp2, java.lang.String connectionIdentifier)

用于 发起 HTTP/2请求

getHelpers
IExtensionHelpers	getHelpers()

此方法用于获取 IExtensionHelpers 对象,扩展程序可以使用该对象来执行许多有用的任务。

是一个小工具,里面有很多常用的方法供我们使用,让插件编写更加方便

工具类

IExtensionHelpers

该接口包含许多辅助方法,扩展可以使用这些方法来协助 Burp 扩展出现的各种常见任务

analyzeRequest

IRequestInfo	analyzeRequest(byte[] request)

IRequestInfo	analyzeRequest(IHttpRequestResponse request)

IRequestInfo	analyzeRequest(IHttpService httpService, byte[] request)

可以看到,该方法重载了三次。传入不同的参数类型,最终得到的都是一个IRequestInfo对象(请求对象)

这里有一个坑,第一个跟第二个得到的对象存在一点区别

analyzeResponse

IResponseInfo	analyzeResponse(byte[] response)

得到一个IResponseInfo对象(响应对象)

编码相关

# base64编码解码
byte[]	base64Decode(byte[] data)
byte[]	base64Decode(java.lang.String data)
java.lang.String	base64Encode(byte[] data)
java.lang.String	base64Encode(java.lang.String data)

# URL编码解码
byte[]	urlDecode(byte[] data)
java.lang.String	urlDecode(java.lang.String data)
byte[]	urlEncode(byte[] data)
java.lang.String	urlEncode(java.lang.String data)

# 字符与byte相互转换
java.lang.String	bytesToString(byte[] data)
byte[]	stringToBytes(java.lang.String data)

buildHttpMessage

byte[]	buildHttpMessage(java.util.List<java.lang.String> headers, byte[] body)

构建一个请求、响应消息,常用于拦截请求并自动修改其中的参数时会使用

HTTP消息类

IInterceptedProxyMessage

该类表示一条被Proxy模块拦截的请求

getClientIpAddress

java.net.InetAddress	getClientIpAddress()

获取被拦截请求的客户端IP,也就是请求来源IP

getListenerInterface

java.lang.String	getListenerInterface()

获取被拦截请求的Proxy Listener,返回值:127.0.0.1:8080

可用来针对不同来源的请求做特殊化处理,如PC/手机

getMessageInfo

IHttpRequestResponse	getMessageInfo()

获取被拦截的请求/响应消息的IHttpRequestResponse对象

IHttpRequestResponse

此接口用来获取&更新请求响应

setHighlight

void	setHighlight(java.lang.String color)

高亮请求响应,可传入red、orange、yellow、green、cyan、blue、pink、magenta、gray值,传空值则表示清除高亮设置

getHighlight

java.lang.String	getHighlight()

获取请求是否高亮,若有则返回对应颜色,若没有则返回None

setComment

void	setComment(java.lang.String comment)

设置备注

getComment

java.lang.String	getComment()

获取备注

setHttpService

void	setHttpService(IHttpService httpService)

设置请求的服务器地址,需要传入一个IHttpService对象

getHttpService

IHttpService	getHttpService()

获取请求的服务器地址IHttpService对象

setRequest

void	setRequest(byte[] message)

设置请求,在需要修改请求内容时用到

getRequest

byte[]	getRequest()

获取请求,注意此时是byte数组,一般需要用IExtensionHelpers.analyzeRequest(byte[] request)方法转成IRequestInfo对象

setResponse

void	setResponse(byte[] message)

设置响应,在需要修改响应内容时用到

getResponse

byte[]	getResponse()

获取响应,注意此时是byte数组,一般需要用IExtensionHelpers.analyzeRespnse(byte[] response)方法转成IResponseInfo对象

IRequestInfo

HTTP请求对象

getMethod

java.lang.String	getMethod()

获取请求方法,GET、POST、PUT······

getUrl

java.net.URL	getUrl()

获取请求的URL,注意此处返回值是一个java.net.URL对象,需要再调用具体的方法获取URL相关内容

getHeaders

java.util.List<java.lang.String>	getHeaders()

以数组方式返回请求行与请求头,如["GET / HTTP/1.1", "Host: example.com"]

getParameters

java.util.List<IParameter>	getParameters()

获取所有请求参数,包含了JSON数据中的参数,返回一个IParameter类型的数组

getContentType

byte	getContentType()

获取请求头中的Content-Type,注意此处返回的是一个整形数字,可以与该类中的静态变量对比

getBodyOffset

int	getBodyOffset()

获取请求body开始时的偏移量(索引值),常使用如request[analyzedRequest.getBodyOffset():].tostring()获取整个请求body

IResponseInfo

HTTP响应对象

getStatusCode

short	getStatusCode()

获取响应状态码

getHeaders

java.util.List<java.lang.String>	getHeaders()

以数组方式返回请求行与请求头,如["HTTP/1.1 200 OK", "Content-Length: 1256"]

getCookies

java.util.List<ICookie>	getCookies()

获取服务器返回的Set-Cookie字段的ICookie对象列表

getStatedMimeType

java.lang.String	getStatedMimeType()

获取响应头中标注的 Content-Type,注意此处返回的是string类型,与 IRequestInfo 中的稍有区别

getInferredMimeType

java.lang.String	getInferredMimeType()

获取BURP自动判断响应内容的Content-Type

getBodyOffset

int	getBodyOffset()

获取响应body开始时的偏移量(索引值),常使用如response[analyzedResponse.getBodyOffset():].tostring()获取整个响应body

IParameter

用于获取请求参数的详情,Key和Value等。This interface is used to hold details about an HTTP request parameter,由官方文档可以看出来,其并未考虑目前前后端分离网站的情况,未提供获取响应中的参数,因此只能直接写获取响应参数的方法

getName

java.lang.String	getName()

获取参数名称

getValue

java.lang.String	getValue()

获取参数值

getType

byte	getType()

检索参数类型,比如说是JSON内的参数还是URL里传输的参数,具体可以该类中的静态变量对比

参数名索引

int	getNameStart()

int	getNameEnd()

获取参数名开始与结束的偏移量(索引)

值索引

int	getValueStart()

int	getValueEnd()

获取参数值开始与结束的偏移量(索引)

IHttpHeader

getName

java.lang.String	getName()

获取请求头名称

getValue

java.lang.String	getValue()

获取请求头的值

ICookie

getName

java.lang.String	getName()

获取Cookie名称

getValue

java.lang.String	getValue()

获取Cookie值

getDomain

java.lang.String	getDomain()

获取Cookie适用的域名

getPath

java.lang.String	getPath()

获取Cookie使用的PATH

getExpiration

java.util.Date	getExpiration()

获取Cookie过期时间

IHttpService

getHost

java.lang.String	getHost()

获取服务器域名

getPort

int	getPort()

获取服务器端口

getProtocol

java.lang.String	getProtocol()

获取服务器使用的协议,HTTP、HTTPS

监听类

监听类API只有一个方法,并且会在一定的条件下自动触发

IProxyListener

Proxy 消息监听类。使用IBurpExtenderCallbacks.registerProxyListener()方法注册该监听器后,便可监听所有流经Proxy的请求与响应

所有请求/响应均会被

processProxyMessage

void	processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message)
# messageIsRequest 表示此次处理的是请求还是响应
# IInterceptedProxyMessage message 表示一个被拦截HTTP消息对象,可通过该对象的	getMessageInfo()方法获取到具体的请求、响应详情信息

方法捕获到,然后在其中完成用户自定的处理行为

例如:我们想要查看所有请求中是否有使用shiro的系统,并将该请求高亮显示,便可以这样实现

图片是网上找的,没太多时间重新搭环境

#coding=utf-8
from burp import IBurpExtender
from burp import IProxyListener
import sys

if sys.version[0] == '2':
    reload(sys)
    sys.setdefaultencoding("utf-8")

class BurpExtender(IBurpExtender,IProxyListener):
    def registerExtenderCallbacks(self,callbacks):
        self._helpers = callbacks.getHelpers()
        callbacks.setExtensionName("FindShiro")
        callbacks.registerProxyListener(self)
    def processProxyMessage(self,messageIsRequest,message):
        # 我们只用判断 响应 头中是否有 rememberMe 字段就可以了
        if not messageIsRequest:
            RepReq = message.getMessageInfo() # 获取请求与响应
            Rep_B = RepReq.getResponse() # 获取响应,byte数据类型
            Rep = self._helpers.analyzeResponse(Rep_B) # 转换为IResopnse对象
            cookies = Rep.getCookies() # 获取所有cookie
            for cookie in cookies:
                if cookie.getName() == "rememberMe": # 判断cookie中是否有rememberMe,有的话就将请求高亮显示
                    RepReq.setHighlight("red")

可以看到,我们甚至都不需要自己写一些函数来处理请求响应,BURP API提供了很多常用的类与方法

百度主站并没有shrio哈,此处是为了方便插件测试的时候所以判断是不是有 BDSVRTM 这个cookie

IHttpListener

HTTP 消息监听类。使用IBurpExtenderCallbacks.registerHTTPListener()方法注册该监听器后,便可监听所有请求与响应,包括Proxy、Repeater、Intruder等等所有的 made by any Burp tool 的流量

请求响应会被

processHttpMessage

void	processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
# toolFlag表示来源,可在 IBurpExtenderCallbacks 类中看到

方法捕获到,使用toolFlag判断来源,如

#coding=utf-8
from burp import IBurpExtender
from burp import IHttpListener
import sys

if sys.version[0] == '2':
    reload(sys)
    sys.setdefaultencoding("utf-8")

class BurpExtender(IBurpExtender,IHttpListener):
    def registerExtenderCallbacks(self,callbacks):
        self._callbacks = callbacks
        callbacks.setExtensionName("PlaceYoucame")
        callbacks.registerHttpListener(self)
    def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
        if toolFlag == self._callbacks.TOOL_PROXY: # 表示来自Proxy模块
            print("Proxy")
        elif toolFlag == self._callbacks.TOOL_REPEATER: # 表示来自Repeater模块
            print("Repeater")
        else:
            print("Others")

IExtensionStateListener

插件状态监听类。通过调用 IBurpExtenderCallbacks.registerExtensionStateListener() 来注册一个扩展状态监听器。 侦听器将收到扩展状态更改的通知。 注意:任何启动后台线程或打开系统资源(例如文件或数据库连接)的扩展都应该注册一个侦听器并在卸载扩展时终止线程/关闭资源。

当插件被卸载时会调用如下方法:

注意:退出BURP时也会调用哦

extensionUnloaded

void	extensionUnloaded()

通常我们会在其中完成善后工作,如

#coding=utf-8
from burp import IBurpExtender
from burp import IExtensionStateListener
import sys

if sys.version[0] == '2':
    reload(sys)
    sys.setdefaultencoding("utf-8")

class BurpExtender(IBurpExtender,IExtensionStateListener):
    def registerExtenderCallbacks(self,callbacks):
        callbacks.setExtensionName("Unload")
        callbacks.registerExtensionStateListener(self)
        print("Link Start!")
    def extensionUnloaded(self):
        print("主人,你忍心么?还不快插上?")

当然我们也可以做一个自动导入&保存BURP配置的插件

#coding=utf-8
from burp import IBurpExtender
from burp import IExtensionStateListener
import sys
import json

if sys.version[0] == '2':
    reload(sys)
    sys.setdefaultencoding("utf-8")

class BurpExtender(IBurpExtender,IExtensionStateListener):
    def registerExtenderCallbacks(self,callbacks):
        self._callbacks = callbacks
        callbacks.setExtensionName("SaveConfig")
        callbacks.registerExtensionStateListener(self)
        try:
            with open("config.json","r") as f:
                config = f.read()
                callbacks.loadConfigFromJson(config)
        except:
            pass
    def extensionUnloaded(self):
        config = self._callbacks.saveConfigAsJson(os.getcwd())
        with open("config.json","w") as f:
            f.write(config)

从此妈妈再也不用担心我忘记保存配置了

IScannerListener

过段时间再写吧

IScopeChangeListener

过段时间再写吧

UI类

IContextMenuInvocation

当 Burp 使用上下文菜单调用的详细信息调用扩展提供的 IContextMenuFactory 时,将使用此接口。 自定义上下文菜单工厂可以查询此接口以获取调用事件的详细信息,以确定应显示哪些菜单项。

getToolFlag

int	getToolFlag()

获取点击右键动作是在哪个模块内触发的,Proxy、Repeater等

getInvocationContext

byte	getInvocationContext()

获取点击右键动作是在哪里触发的(详细信息),具体可对比

getSelectedMessages

IHttpRequestResponse[]	getSelectedMessages()

获取右键内容的的HTTP请求响应(IHttpRequestResponse对象)

getSelectedIssues

IScanIssue[]	getSelectedIssues()

获取右键内容的的扫描IScanIssue对象

注意:至于什么时候使用getSelectedMessages或getSelectedIssues,可根据getInvocationContext获取的结果加以判断

IContextMenuFactory

注册右键菜单

该类只有一个方法

createMenuItems

java.util.List<javax.swing.JMenuItem>	createMenuItems(IContextMenuInvocation invocation)

当用户在 Burp 内的任何位置调用上下文菜单(右键)时,该方法将被 Burp 调用

比方说我想实现一个这样的右键菜单,完成一些自定义的事情

那便可以这样写:

#coding=utf-8
from burp import IBurpExtender
from burp import IContextMenuFactory
from javax.swing import JMenuItem
import sys

if sys.version[0] == '2':
    reload(sys)
    sys.setdefaultencoding("utf-8")

class BurpExtender(IBurpExtender, IContextMenuFactory):
    def registerExtenderCallbacks(self,callbacks):
    	self._callbacks = callbacks
        callbacks.setExtensionName("myMenu")
        callbacks.registerContextMenuFactory(self)
    def createMenuItems(self, invocation):
        if invocation.getToolFlag() == self._callbacks.TOOL_REPEATER or self._callbacks.TOOL_PROXY:
            menu = []
            menu.append(JMenuItem("PrintHOST", None, actionPerformed=lambda x, y=invocation: self.myFunc(x, y)))
        return menu
    def myFunc(self,event,invocation):
        reqreps = invocation.getSelectedMessages()
        for reqrep in reqreps:
            print(reqrep.getHttpService().getHost())
原文地址:https://www.cnblogs.com/wjrblogs/p/15705552.html