BurpSuite 插件开发 – JAVA

对 JAVA 的了解仅限于基础语法,如何傻瓜式地使用 JAVA 编写一个 Burp 插件,并带 GUI 界面?

环境:IntelliJ IDEA、JDK 1.8

新建项目

首先新建一个项目,填写一个插件的名字

然后在 Burp 中将 JAVA API 导出到刚新建项目的 /src 目录当中,此时我们调用 Burp 相应方法时便会获得提示

并且在 /src/burp 目录下新建一个名为 BurpExtender 的 JAVACLASS

设置导出为 JAR 文件

在 BurpSuite 中引用插件需要以 jar 格式才行

首先打开 File->Project Structure,项目结构设置

首先看到 Project Settings->Modules,确保有一个与项目名称相同的 Module(正常来说肯定会有)

然后再看到 Project Settings->Artifacts,点击+号,新建一个导出格式

点击 OK 即可

得到,也是点击 OK 即可

尝试导出 JAR

点击 Build->Build Artifacts ,即可导出 jar 文件

需要注意的是,由于此时我们所编写的 BurpExtender 类为空,所以无法导入到 Burp 中

开始编写插件核心功能

首先为了能让插件导入到 Burp 中不报错,我们首先得让 BurpExtender 类实现 IBurpExtender 接口,并初始化一下,输出一点东西

// BurpExtender.java

package burp;

// 插件的入口固定为 BurpExtender
public class BurpExtender implements IBurpExtender{
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        callbacks.printOutput("Hava Fun!");
    }
}

重新 Build 后,再在 Burp 中加载

可以看到已经成功创建了插件,接下来我们便可以逐步开始完善核心功能,打造一个 SRC 挖掘机了

GUI

新建 UI package 并创建 GUI From

首先在 /src 目录下新建一个与 /burp 同级的 Package,例如 ui

然后在 /ui 目录下通过 IDEA 创建 GUI Form

最终得到:

拖拽构建页面

首先选中页面中的 Jpanel 组件,给 field name 取一个名字,如 root,然后在代码框中将 root 变量的 private 改为 public(为了后续在 Burp 中使用)。然后便可以将一些组件直接拖入到页面中,构建自定义UI,如此处显示一句话 Hello GUI From

在 Burp 中显示该组件

首先,初始化 UI 组件,获取到 UI 中主界面变量(此时为 root),并注册到 Burp

BurpGui UI = new BurpGui();
jPanelMain = UI.root;
callbacks.customizeUiComponent(jPanelMain);

然后使 BurpExtender 类实现 ITab 接口

//返回 Tab 的名字
@Override
public String getTabCaption() {
	return "liteTools";
}

//返回 UI 的主界面变量
@Override
public Component getUiComponent() {
	return jPanelMain;
}

此时,重新 Build 一下项目,在 Burp 中导入即可得到以下结果

实现点击事件监听

通常,UI 页面是用来交互的,那么如何在 GUI From 中实现呢?

首先简单拖入一个按钮,并给 field name 取名为 buttom(截图时写错了button)

此时右键 buttom 组件,点击 Create Listener->ActionLister,即可为按钮创建事件监听函数

例如,点击按钮修改显示文本

package ui;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class BurpGui {
    public JPanel root;
    private JLabel text;
    private JButton button;

    public BurpGui() {
        buttom.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                text.setText("震惊!大黑阔!!!");
            }
        });
    }
}

此时,重新 Build 并在 Burp 中导入,即可看到如下效果

使用 Burp 的 UI 组件

如下所示,首先需要自定义实现 UI 父组件(也就是 jPanel root),并实现相应的接口。同时也需要实现构造函数,引入 IBurpExtenderCallbacks 工具类

附代码:

//BurpGUI.java
package ui;

import javax.swing.*;

import burp.IHttpService;
import burp.IMessageEditor;
import burp.IBurpExtenderCallbacks;
import burp.IMessageEditorController;


public class BurpGUI implements IMessageEditorController {
    public JPanel root;
    private IMessageEditor reqMessageEditor;
    private IBurpExtenderCallbacks callbacks;

    public BurpGui(IBurpExtenderCallbacks cb) {
        callbacks = cb;
    }

    private void createUIComponents() {
        // TODO: place custom component creation code here
        root = new JPanel();

        reqMessageEditor = this.callbacks.createMessageEditor(BurpGUI.this, false);
        root.add(reqMessageEditor.getComponent());
    }

    @Override
    public IHttpService getHttpService() {
        //自定义逻辑返回 Service 信息
        return null;
    }

    @Override
    public byte[] getRequest() {
        //自定义逻辑返回请求信息
        return new byte[0];
    }

    @Override
    public byte[] getResponse() {
        //自定义逻辑返回响应信息
        return new byte[0];
    }
}

//BurpExtender.java
package burp;


import javax.swing.*;
import java.awt.*;
import ui.*;




public class BurpExtender implements IBurpExtender,ITab{
    private JPanel jPanelMain;
    private IMessageEditor reqMessageEditor;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        callbacks.setExtensionName("liteTools");
        callbacks.printOutput("Hava Fun!");

        BurpGui UI = new BurpGui(callbacks);

        jPanelMain = UI.root;
        callbacks.customizeUiComponent(jPanelMain);
        callbacks.addSuiteTab(this);

    }

    @Override
    public String getTabCaption() {
        return "liteTools";
    }

    @Override
    public Component getUiComponent() {
        return jPanelMain;
    }

}

当什么也没写就得到了这么个玩意

附常用接口、类

插件入口类

IBurpExtender

所有插件都必须实现该接口

void    registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)

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

工具类

IBurpExtenderCallbacks

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

常量

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

img

方法

只列举常用方法讲解

printError
void    printError(java.lang.String error)

输出错误信息

printOutput
void    printOutput(java.lang.String output)

输出一般信息

image-20220820163812766

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保存文件

img

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

img

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

getMessageInfo

IHttpRequestResponse	getMessageInfo()

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

IHttpRequestResponse

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

setHighlight

void	setHighlight(java.lang.String color)

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

img

getHighlight

java.lang.String	getHighlight()

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

setComment

void	setComment(java.lang.String comment)

设置备注

img

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,注意此处返回的是一个整形数字,可以与该类中的静态变量对比

img

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对象列表

img

getStatedMimeType

java.lang.String	getStatedMimeType()

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

img

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里传输的参数,具体可以该类中的静态变量对比

img

参数名索引

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

监听类

IProxyListener

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

img

所有请求/响应均会被

processProxyMessage

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

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

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

img

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

package burp;

import java.util.List;


public class BurpExtender implements IBurpExtender,IProxyListener{
    private IExtensionHelpers helper;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        callbacks.setExtensionName("liteTools");
        this.helper = callbacks.getHelpers();
        callbacks.registerProxyListener(this);
    }

    @Override
    public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message) {
        if(!messageIsRequest){
            IHttpRequestResponse reqrep = message.getMessageInfo();
            byte[] reqrep_b = reqrep.getResponse();
            IResponseInfo rep = helper.analyzeResponse(reqrep_b);
            List<ICookie> cookies = rep.getCookies();
            for(ICookie cookie:cookies){
                if(cookie.getName() == "rememberMe"){
                    reqrep.setHighlight("red");
                }
            }
        }
    }
}

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

img

百度主站并没有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判断来源,如

package burp;

public class BurpExtender implements IBurpExtender,IHttpListener{
    IBurpExtenderCallbacks cb;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        callbacks.setExtensionName("liteTools");
        this.cb=callbacks;
    }

    @Override
    public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
        if(toolFlag == cb.TOOL_PROXY){
            cb.printOutput("Proxy");
        } else if (toolFlag == cb.TOOL_REPEATER) {
            cb.printOutput("Repeater");
        }else{
            cb.printOutput("Others");
        }
    }
}

img

IExtensionStateListener

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

当插件被卸载时会调用如下方法:注意:退出BURP时也会调用哦

extensionUnloaded

void	extensionUnloaded()

通常我们会在其中完成善后工作,如保存项目配置

package burp;

import java.io.*;


public class BurpExtender implements IBurpExtender,IExtensionStateListener{
    private IBurpExtenderCallbacks cb;
    private String dir = System.getProperty("user.dir");

    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        callbacks.setExtensionName("liteTools");
        callbacks.printOutput("Hava Fun!");
        callbacks.registerExtensionStateListener(this);
        File file = new File(dir + "/config.json");
        byte[] filecontent = new byte[(int) file.length()];
        try{
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
        }catch (IOException e){
            cb.printError(e.toString());
        }
        callbacks.loadConfigFromJson(new String(filecontent));
        this.cb = callbacks;
    }

    @Override
    public void extensionUnloaded() {
        String config = cb.saveConfigAsJson(dir);

        try{
            BufferedWriter out = new BufferedWriter(new FileWriter("config.json"));
            out.write(config);
            out.close();
            cb.printOutput("File Saved at "+ dir);
        }catch (IOException e){
            cb.printError(e.toString());
        }

    }
}

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

UI类

IContextMenuInvocation

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

getToolFlag

int	getToolFlag()

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

getInvocationContext

byte	getInvocationContext()

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

img

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 调用

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

img

那便可以这样写:

package burp;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;


public class BurpExtender implements IBurpExtender,IContextMenuFactory{
    IBurpExtenderCallbacks cb;
    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        callbacks.setExtensionName("liteTools");
        callbacks.registerContextMenuFactory(this);
        this.cb = callbacks;
    }

    @Override
    public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
        ArrayList<JMenuItem> menus = new ArrayList<>();
        if(invocation.getToolFlag() == cb.TOOL_REPEATER || invocation.getToolFlag() == cb.TOOL_PROXY){
            JMenuItem btn = new JMenuItem("PrintHOST");
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    IHttpRequestResponse[] reqreps = invocation.getSelectedMessages();
                    for(IHttpRequestResponse reqrep:reqreps){
                        cb.printOutput(reqrep.getHttpService().getHost());
                    }
                }
            });
            menus.add(btn);
        }
        return menus;
    }
    
}
版权声明:本文为1ndex原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/wjrblogs/p/16618145.html