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方法内用到

方法
只列举常用方法讲解
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数组,一般需要用IExtensionHelpers.analyzeRequest(byte[] request)方法转成IRequestInfo对象
setResponse
- void setResponse(byte[] message)
复制代码 设置响应,在需要修改响应内容时用到
getResponse
获取响应,注意此时是byte数组,一般需要用IExtensionHelpers.analyzeRespnse(byte[] response)方法转成IResponseInfo对象
IRequestInfo
HTTP请求对象
getMethod
- java.lang.String getMethod()
复制代码 获取请求方法,GET、POST、PUT······
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
获取请求头中的Content-Type,注意此处返回的是一个整形数字,可以与该类中的静态变量对比

getBodyOffset
获取请求body开始时的偏移量(索引值),常使用如request[analyzedRequest.getBodyOffset():].tostring()获取整个请求body
IResponseInfo
HTTP响应对象
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
获取响应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
检索参数类型,比如说是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
获取服务器端口
getProtocol
- java.lang.String getProtocol()
复制代码 获取服务器使用的协议,HTTP、HTTPS
监听类
IProxyListener
Proxy 消息监听类。使用IBurpExtenderCallbacks.registerProxyListener()方法注册该监听器后,便可监听所有流经Proxy的请求与响应

所有请求/响应均会被
processProxyMessage
- void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message)
- # messageIsRequest 表示此次处理的是请求还是响应
- # IInterceptedProxyMessage message 表示一个被拦截HTTP消息对象,可通过该对象的 getMessageInfo()方法获取到具体的请求、响应详情信息
复制代码 方法捕获到,然后在其中完成用户自定的处理行为
例如:我们想要查看所有请求中是否有使用shiro的系统,并将该请求高亮显示,便可以这样实现

图片是网上找的,没太多时间重新搭环境
- 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提供了很多常用的类与方法

百度主站并没有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");
- }
- }
- }
复制代码
IExtensionStateListener
插件状态监听类。通过调用 IBurpExtenderCallbacks.registerExtensionStateListener() 来注册一个扩展状态监听器。 侦听器将收到扩展状态更改的通知。 注意:任何启动后台线程或打开系统资源(例如文件或数据库连接)的扩展都应该注册一个侦听器并在卸载扩展时终止线程/关闭资源。
当插件被卸载时会调用如下方法:注意:退出BURP时也会调用哦
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
获取点击右键动作是在哪个模块内触发的,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 调用
比方说我想实现一个这样的右键菜单,完成一些自定义的事情

那便可以这样写:- 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;
- }
-
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |