BurpSuite 插件开发 - Montoya Api
今年十月份,PortSwigger 发布了新版本的 BurpSuite,其中包含了新版本的 Montoya 插件 API,并宣布会逐步放弃支持旧版插件,艹
我也是醉了,github 上的 Api 跟 Burp 导出的 Api 还 TM 不一样,所以建议使用 Maven 或 Gradle 创建项目,但是不知道为什么有时候下载的包含注释有时候不含
新建项目
初始化
环境:IntelliJ IDEA、JDK 1.8
然后在 build.gradle 文件内添加 implementation 'net.portswigger.burp.extensions:montoya-api:+'
这句话
或者在 pom.xml 中添加( 创建时选择 Maven)
<dependencies>
<dependency>
<groupId>net.portswigger.burp.extensions</groupId>
<artifactId>montoya-api</artifactId>
<version>LATEST</version>
</dependency>
</dependencies>
相较于之前一锅炖的情况,此次 Burp 贴心的帮我们分了类
不难看出,collaborator、comparer、decoder、intruder、proxy、repeater、scanner、scope、sitemap、是与 Burp 上的功能相对应的,那也就能猜出 Api 所提供的能力应该也与之对应
剩下的,core、http、internal、logging、burpsuite、persistence、ui、utilities、websocket 提供了 Burp 其他能力
设置打包
首先打开 File->Project Structure
,项目结构设置
然后再看到 Project Settings->Artifacts
,点击+号,新建一个导出格式,注意选择From modules with xxx
实现插件入口
新建一个 package,并创建一个类,实现 BurpExtension
接口
然后点击 Build->Build Artifacts
即可打包 jar 文件
打包时可能会出现错误说 xx 函数不能为 private,删除这个函数的 private 关键字即可
Montoya Api
首先来看一下在初始化插件时提供的 montoyaApi 吧,家人们
很多地方都会用到这个 Api,所以可以把它定义为一个 public static 变量,方便在其他地方使用它提供的功能
extension()
提供插件 相关信息
void setName(String extensionName)
很显然,就是用来设置插件名称的
String filename()
返回插件 jar 文件所在位置
如:C:\I\Love\You\Desktop\JAVA\MontoyaExt\MontoyaExt.jar
boolean isBapp()
判断插件是不是来自官方的 BappStore
void unload()
卸载(关闭)插件
Registration registerExtensionUnloadHandler(ExtensionUnloadHandler handler)
用于注册插件卸载监听器,需要自行实现 ExtensionUnloadHandler 接口才可以,那么如何注册呢?后面写。
burpSuite()
Version version()
获取 burp 的版本,返回一个 Version 对象,至于这个对象有怎样的属性以及方法,可以点击查看
例如,获取Burp是 Professional、Community Edition、Enterprise Edition 中那个版本即可这样使用
String edition = Api.burpSuite().version().edition().name;
List<String> commandLineArguments()
返回一个包含命令行启动 Burp 时参数的 List<String>
String exportProjectOptionsAsJson(String... paths)
导出 Burp project 的配置信息,其参数为 paths 可能不太好理解,其实就是 json 文件中的某些键,如果为空的话,则表示导出所有配置信息
例如我只想导出 Proxy 的 Options 的 Proxy Listenters 以及 Repeater 的配置,就可以这样写
String configString = Api.burpSuite().exportProjectOptionsAsJson("proxy.request_listeners","repeater");
void importProjectOptionsFromJson(String json)
与 exportProjectOptionsAsJson 相反,从 json 字符串中导入Burp 配置信息
void shutdown(ShutdownOptions... options)
关闭 Burp,其中关闭参数 ShutdownOptions 目前只有一个选项:PROMPT_USER,会在关闭时弹窗提示,如果不填的话则是直接关闭(千万在测试的时候写上 PROMPT_USER,否则的话之后就打不开 Burp 了,除非删除或移动 jar 文件位置,快快给我这种脚本小子上一课吧)
logging()
很显然,用来打印信息的
void logToOutput(String message)
打印日志信息,输出到 Burp
void logToError(String message)
打印错误信息,输出到 Burp
PrintStream error()
返回一个日志输出流,同样的会打印在 log 窗口
PrintStream logStream = api.logging().output();
logStream.print("aaaa");
PrintStream output()
返回一个错误输出流,同上,不过是打印在 error 窗口
void raiseInfoEvent(String message)
在 Dashboard 页的 Event log 处记录一个 Info 等级的日志
void raiseCriticalEvent(String message)
记录一个 Critical 等级的日志
void raiseDebugEvent(String message)
记录一个 Debug 等级的日志
void raiseErrorEvent(String message)
记录一个 Error 等级的日志
userInterface()
主要是一些 UI 相关的功能
void applyThemeToComponent(Component component)
用来将 Burp 风格的样式应用到用户实现的 UI 页面上,比如字体大小、颜色等
JPanel myUI = new JPanel();
api.userInterface().applyThemeToComponent(myUI);
SwingUtils swingUtils()
返回一个 SwingUtils 类,该类的方法可以操作一些 ui 相关的东西,具体我也没用过
Registration registerSuiteTab(String title, Component component)
用来注册一个同 Proxy、Repeater 同级别的 Tab 页面,如:
JPanel myUI = new JPanel();
api.userInterface().registerSuiteTab("Test",myUI);
HttpRequestEditor createHttpRequestEditor(EditorOptions options)
用来创建一个类似 Proxy 页面中的 Request 界面,其中传的 EditorOptions 参数只有 READ_ONLY 可选,表示只读,如果不传则表示可读可写
JPanel myUI = new JPanel();
HttpRequestEditor myRequestEditor = api.userInterface().createHttpRequestEditor(READ_ONLY );
myUI.add(myRequestEditor.uiComponent());
api.userInterface().registerSuiteTab("Test",myUI);
HttpResponse createHttpResponseEditor(EditorOptions options)
用来创建一个类似 Proxy 页面中的 Response 界面
RawEditor createRawEditor(EditorOptions options)
用来创建一个原生编辑框,与另外两个相比其实区别不大,只是少了几个方法而已
Theme currentTheme()
返回一个主题常量:DARK、LIGHT
Registration registerContextMenuItemsProvider(ContextMenuItemsProvider provider)
用来注册一个右键菜单,其中 ContextMenuItemsProvider 参数需要自己创意一个类并实现ContextMenuItemsProvider 接口
public class MontoyaExt implements BurpExtension {
public static MontoyaApi Api;
@Override
public void initialize(MontoyaApi montoyaApi) {
Api = montoyaApi;
Api.extension().setName("Test");
api.userInterface().registerContextMenuItemsProvider(new MyContextMenus());
}
private class MyContextMenus implements ContextMenuItemsProvider{
@Override
public List<JMenuItem> provideMenuItems(ContextMenuEvent event) {
ArrayList<JMenuItem> menus = new ArrayList<>();
JMenuItem TestJMenuItem = new JMenuItem("Test");
TestJMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Api.logging().logToOutput("Test");
}
});
menus.add(TestJMenuItem);
return menus;
}
}
}
Registration registerHttpRequestEditorProvider(ExtensionHttpRequestEditorProvider provider)
用来注册一个自定义的 Http 请求编辑框
package extension;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.extension.EditorMode;
import burp.api.montoya.ui.editor.extension.ExtensionHttpRequestEditor;
import burp.api.montoya.ui.editor.extension.ExtensionHttpRequestEditorProvider;
import javax.swing.*;
import java.awt.*;
public class MyExtension implements BurpExtension {
public static MontoyaApi Api;
ExtensionHttpRequestEditor myExtensionHttpRequestEditor;
@Override
public void initialize(MontoyaApi montoyaApi) {
Api = montoyaApi;
montoyaApi.extension().setName("Test");
this.myExtensionHttpRequestEditor = new MyExtensionHttpRequestEditor();
Api.userInterface().registerHttpRequestEditorProvider(new MyExtensionHttpRequestEditorProvider());
}
private class MyExtensionHttpRequestEditorProvider implements ExtensionHttpRequestEditorProvider {
@Override
public ExtensionHttpRequestEditor provideHttpRequestEditor(HttpRequestResponse httpRequestResponse, EditorMode editorMode) {
return myExtensionHttpRequestEditor;
}
}
private class MyExtensionHttpRequestEditor implements ExtensionHttpRequestEditor{
HttpRequestResponse MyHttpRequestResponse;
JPanel MainPanel;
HttpRequestEditor MyHttpRequestEditor;
MyExtensionHttpRequestEditor(){
this.MainPanel = new JPanel();
MyHttpRequestEditor = Api.userInterface().createHttpRequestEditor();
this.MainPanel.add(MyHttpRequestEditor.uiComponent());
}
@Override
public HttpRequest getHttpRequest() {
return this.MyHttpRequestResponse.httpRequest().addHeader("111","222");
}
@Override
public void setHttpRequestResponse(HttpRequestResponse httpRequestResponse) {
this.MyHttpRequestResponse = httpRequestResponse;
}
@Override
public boolean isEnabledFor(HttpRequestResponse httpRequestResponse) {
return true;
}
@Override
public String caption() {
return "Test";
}
@Override
public Component uiComponent() {
return this.MainPanel;
}
@Override
public Selection selectedData() {
return null;
}
@Override
public boolean isModified() {
return false;
}
}
}
Registration registerHttpResponseEditorProvider(ExtensionHttpResponseEditorProvider provider)
用来注册一个自定义的 Http 响应编辑框,同上
http()
http 请求相关功能
HttpRequestResponse issueRequest()
用来发起 http 请求,这个方法有三个不同的重载
- HttpRequestResponse issueRequest(HttpRequest request)
- HttpRequestResponse issueRequest(HttpRequest request, HttpMode httpMode)
- HttpRequestResponse issueRequest(HttpRequest request, HttpMode httpMode, String connectionId)
其中,request 是一个 HttpRequest 对象;httpMode 是请求协议,有AUTO、HTTP_1、HTTP_2、HTTP_2_IGNORE_AlPN;connectionId 表示“要使用的连接的标识符”,不懂
ResponseKeywordsAnalyzer createResponseKeywordsAnalyzer(List<String> list)
用于创建新的响应关键字分析器,
ResponseVariationsAnalyzer createResponseVariationsAnalyzer()
创建新的响应变化分析器
Registration registerHttpHandler(HttpHandler httpHandler)
注册一个处理 Http 请求的监听器,所有经过 Proxy、Repeater... 的请求都会被它捕获,需要自行实现一个继承 HttpHandler 接口的类
Registration registerSessionHandlingAction(SessionHandlingAction sessionHandlingAction)
注册自定义会话处理操作。每个注册的操作都将在会话处理规则UI中提供,供用户选择作为规则操作。用户可以选择直接调用操作,也可以选择在宏执行之后调用操作
CookieJar cookieJar()
返回一个 CookieJar 对象,这个一般是用来构建 HttpRequest 对象用的
utilities()
提供一些编码工具
Base64Utils base64Utils()
返回一个 Base64Utils 对象,该对象内有针对 Base64 加解密的方法
例如实现base64解密:
Base64Utils MyBase64Utils = Api.utilities().base64Utils();
byte[] ByteResult = MyBase64Utils.getDecoder().decode("MW5kZXg=");
ByteUtils MyByteUtils = Api.utilities().byteUtils();
String StringResult = MyByteUtils.convertToString(ByteResult);
Api.logging().logToOutput(StringResult);
ByteUtils byteUtils()
返回一个 ByteUtils 对象,常用于byte与String之间的互相转换
CompressionUtils compressionUtils()
返回一个 CompressionUtils 对象,用于压缩数据
CryptoUtils cryptoUtils()
返回一个 CryptoUtils 对象,用于加密,例如实现 MD5 加密
ByteUtils MyByteUtils = Api.utilities().byteUtils();
CryptoUtils MyCryptoUtils = Api.utilities().cryptoUtils();
ByteArray MyByteArray = ByteArray.byteArray("1ndex");
ByteArray ResultByteArray = MyCryptoUtils.generateDigest(MyByteArray, DigestAlgorithm.MD5);
String EncryptString = MyByteUtils.convertToString(ResultByteArray.getBytes());
Api.logging().logToOutput(EncryptString);
HtmlUtils htmlUtils()
返回一个 HtmlUtils 对象,用于处理 HTML 编码解码
NumberUtils numberUtils()
返回一个 NumberUtils 对象,用于将如16进制字符串转换成文本字符串
RandomUtils randomUtils()
返回一个 RandomUtils 对象,用于创建一个随机长度的字符串
StringUtils stringUtils()
返回一个 StringUtils 对象,用于HEX与ASCII之间的相互转换
URLUtils urlUtils()
返回一个 URLUtils 对象,用于 URL 编解码
siteMap()
返回一个 SiteMap 对象,用来查询、修改 Burp 中的 SiteMap
void add(AuditIssue aduitIssue)
添加 SiteMap 记录,需要传一个 AuditIssue 对象参数
void add(HttpRequestResponse httpRequestResponse)
通过一个 HttpRequestResponse 对象添加一个添加 SiteMap 记录
List<AuditIssue> Issues()
获取所有 SiteMap 内容
List<AuditIssue> Issues(SiteMapFilter siteMapFilter)
获取符合条件的 SiteMap 内容,需要传一个 SiteMapFilter 对象参数
List<HttpRequestResponse> requestResponses()
获取SiteMap中所有的 HttpRequestResponse 对象
List<HttpRequestResponse> requestResponses(SiteMapFilter siteMapFilter)
获取符合条件的 SiteMap 内容
scope()
返回一个 Scope 对象,用来查询、添加 Burp 中的 Scope 内容
void includeInScope(String url)
用来往Include in scope中添加记录
Api.scope().includeInScope("https://xxx.com/a/b/c");
void excludeFromScope(String url)
用来往Exclude from scope中添加记录
boolean isInScope(String url)
判断某个记录是否在 Include in scope 记录里,注意没有 Enabled 的即使存在该记录仍返回 false
Registration registerHandler(ScopeChangeHandler handler)
用于注册一个scope变更监听器,需要传一个 ScopeChangeHandler 对象
persistence()
提供缓存相关功能
TemporaayFile temporaayFile()
返回一个 TemporaayFile 对象,使用该对象中的方法可以设置缓存、获取缓存
比方其中的 setxxx(String key, X xxx) 方法便是用来设置缓存的,getxxx(String key) 便是用来获取对应缓存的
Preferences preferences()
返回一个 Preferences 的 JAVA 缓存对象,该类允许从java首选项存储中存储和访问数据(对此我是一窍不通)
proxy()
提供 Proxy 面板中相关的一些功能
void disableIntercept()
很显然,关闭拦截
void enableIntercept()
很显然,开启拦截
List<ProxyRequestResponse> history()
获取所有 Proxy 中的请求历史
List<ProxyRequestResponse> history(ProxyHistoryFilter proxyHistoryFilter)
获取符合条件的 Proxy 中的请求历史,需要传一个 ProxyHistoryFilter 对象
Registration registerRequestHandler(ProxyHttpRequestHandler proxyHttpRequestHandler)
注册一个 Proxy 请求处理监听器,每一个流经 Proxy 的请求都会被 ProxyHttpRequestHandler 处理
Registration registerResponseHandler(ProxyHttpResponseHandler proxyHttpResponseHandler)
注册一个 Proxy 响应处理监听器,每一个流经 Proxy 的响应都会被 ProxyHttpResponseHandler 处理
Registration registerWebSocketCreationHandler(ProxyWebSocketCreationHandler proxyWebSocketCreationHandler)
注册一个 WebSocket 处理监听器,每一个流经 Proxy 的 WebSocket 时都会被 ProxyWebSocketCreationHandler 处理
websockets()
websocket 相关功能
Registration registerWebSocketCreationHandler(ProxyWebSocketCreationHandler proxyWebSocketCreationHandler)
注册一个 WebSocket 处理监听器,任何 Burp 工具创建 WebSocket 时都会被 ProxyWebSocketCreationHandler 处理
collaborator()
提供 Collaborator 面板相关功能,相当于一个 dnslog
CollaboratorClient createClient()
创建一个 CollaboratorClient 客户端,因为这个服务器在国外,所以访问可能会存在一些问题
CollaboratorClient restoreClient(SecretKey secretKey)
恢复一个创建过的 CollaboratorClient 客户端
comparer()
提供 Comparer 面板相关功能
void sendToComparer(ByteArray... byteArrays)
将信息发送到 Comparer 面板
Api.comparer().sendToComparer(ByteArray.byteArray("1ndex"),ByteArray.byteArray("2ndex"));
decoder()
提供 Decoder 面板相关功能
void sendToDecoder(ByteArray... byteArrays)
将信息发送到 Decoder 面板
intruder()
提供 Intruder 面板相关功能
void sendToIntruder(HttpRequest httpRequest)
将请求发送到 Intruder 面板
void sendToIntruder(Httpservice httpService,HttpRequestTemplate httpRequestTemplate)
将请求发送到 Intruder 面板
Registration registerPayloadGeneratorProvider(PayloadGeneratorProvider payloadGeneratorProvider)
注册一个 Intruder 有效负载生成器
Registration registerPayloadProcessor(PayloadProcessor payloadProcessor)
注册一个 PayloadProcessor
package extension;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.intruder.*;
import burp.api.montoya.ui.editor.extension.ExtensionHttpRequestEditor;
import static burp.api.montoya.intruder.Payload.from;
import static burp.api.montoya.intruder.PayloadProcessingResult.skipPayload;
public class MyExtension implements BurpExtension {
public static MontoyaApi Api;
ExtensionHttpRequestEditor myExtensionHttpRequestEditor;
@Override
public void initialize(MontoyaApi montoyaApi) {
Api = montoyaApi;
Api.intruder().registerPayloadProcessor(new MyPayloadProcessor());
Api.intruder().registerPayloadGeneratorProvider(new MyPayloadGeneratorProvider());
}
class MyPayloadGeneratorProvider implements PayloadGeneratorProvider {
@Override
public String displayName() {
return "TestPayloadGeneratorProvider";
}
@Override
public PayloadGenerator providePayloadGenerator(AttackConfiguration attackConfiguration) {
return new MyPayloadGenerator();
}
}
class MyPayloadGenerator implements PayloadGenerator{
@Override
public Payload generatePayloadFor(IntruderInsertionPoint intruderInsertionPoint) {
return from("Test");
}
}
class MyPayloadProcessor implements PayloadProcessor {
@Override
public String displayName() {
return "TestPayloadProcessor";
}
@Override
public PayloadProcessingResult processPayload(Payload currentPayload, Payload originalPayload, IntruderInsertionPoint intruderInsertionPoint) {
//跳过,不处理
PayloadProcessingResult MyPayloadProcessingResult = skipPayload();
return MyPayloadProcessingResult;
}
}
}
repeater()
提供 Repeater 面板相关功能
void sendToRepeater(HttpRequest httpRequest)
将请求发送到 Repeater 面板
void sendToRepeater(String name,HttpRequest httpRequest)
将请求发送到 Repeater 面板,并设置一个 Tab 名字
scanner()
提供 Scanner 面板相关功能
Scan createScan()
创建一个扫描
void generateReport(List<AuditIssue> issues, ReportFormat format,Path path)
生成扫描报告
Registration registerAuditIssueHandler(AuditIssueHandler auditIssueHandler)
注册一个审计监听器,该处理程序将收到Scanner报告的新审计问题的通知,交由 AuditIssueHandler 处理
Registration registerInsertionPointProvider(AuditInsertPointProvider auditInsertPointProvider)
注册一个扫描器插入点监听器,对于每个主动扫描的基本请求,Burp 将要求提供者提供适合该请求的任何自定义扫描仪插入点。
Registration registerScanCheck(ScanCheck scanCheck)
注册一个扫描监听器,每一个经过被动、主动扫描的请求均会被 ScanCheck 处理
看完你会发现,怎么好像少了 ui()、core()、internal()、persistence() 这几个包的方法呢?其实已经包含在 userInterface() 、sitemap() 等方法中了
其他方法
通过查看导出的 API 相关文件,你会发现其中还有一些接口的方法并不能通过初始化得到的 Montoya Api 调用,那么,除了Montoya Api提供的方法之外,我们还有哪些方法可以直接用呢?
所有接口中的被 static 关键字标记的方法。有的时候实现某个接口的方法时,需要返回实现特定接口的类,但是又不想实现该类,便可利用此类方法快速创建一个符合条件的类
实现接口
前面已经提到了很多 registerxxx() 的方法,参数中均是各种需要自己实现xx接口的类
要想在Burp处理请求时添加自定义逻辑,需要实现某些接口,并注册才可以。因此,首先看到能实现哪些接口
看到 官方文档:
举个例子
Registration registerUnloadingHandler(ExtensionUnloadingHandler extensionUnloadingHandler)
看最简单的卸载插件监听,首先定义一个 MyExtensionUnloadingHandler 类实现自 ExtensionUnloadingHandler 接口,然后在 initialize 初始化函数中通过 registerUnloadingHandler 注册
package extension.core;
import burp.api.montoya.extension.ExtensionUnloadingHandler;
import static extension.MyExtension.Api;
public class MyExtensionUnloadingHandler implements ExtensionUnloadingHandler {
@Override
public void extensionUnloaded() {
Api.logging().logToOutput("插件被卸载了");
}
}