【工具开发】Burpsuite 插件开发

想写一个burpsuite插件,看到burpsuite更新了最新的api,首先通过学习官网的github示例学习下api功能使用
https://github.com/PortSwigger/burp-extensions-montoya-api-examples

Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package example.helloworld;

import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.logging.Logging;

//Burp will auto-detect and load any class that extends BurpExtension.
public class HelloWorld implements BurpExtension
{
@Override
public void initialize(MontoyaApi api)
{
// set extension name
api.extension().setName("Hello world extension");

Logging logging = api.logging();

// write a message to our output stream
logging.logToOutput("Hello output.");

// write a message to our error stream
logging.logToError("Hello error.");

// write a message to the Burp alerts tab
logging.raiseInfoEvent("Hello info event.");
logging.raiseDebugEvent("Hello debug event.");
logging.raiseErrorEvent("Hello error event.");
logging.raiseCriticalEvent("Hello critical event.");

// throw an exception that will appear in our error stream
throw new RuntimeException("Hello exception.");
}
}

上面代码的logging是输出在这里的:

可以用来输出自动化扫描后的结果

HTTP Handler Example Extension

它注册一个 HTTP 处理程序
对于传出请求消息:
如果请求是请求:POST
请求正文将记录到 output
将向请求添加注释
将 URL 参数添加到请求中
对于传入的响应消息:
如果相应的请求包含标头,则会添加高亮显示Content-LengthBLUE

MyHttpHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.Annotations;
import burp.api.montoya.core.HighlightColor;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.logging.Logging;

import static burp.api.montoya.http.handler.RequestToBeSentAction.continueWith;
import static burp.api.montoya.http.handler.ResponseReceivedAction.continueWith;
import static burp.api.montoya.http.message.params.HttpParameter.urlParameter;

class MyHttpHandler implements HttpHandler {
private final Logging logging;

public MyHttpHandler(MontoyaApi api) {
this.logging = api.logging();
}


@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
Annotations annotations = requestToBeSent.annotations();

// If the request is a post, log the body and add notes.
if (isPost(requestToBeSent)) {
annotations = annotations.withNotes("Request was a post");
logging.logToOutput(requestToBeSent.bodyToString());
}

//Modify the request by adding url param.
HttpRequest modifiedRequest = requestToBeSent.withAddedParameters(urlParameter("foo", "bar"));

//Return the modified request to burp with updated annotations.
return continueWith(modifiedRequest, annotations);
}

@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
Annotations annotations = responseReceived.annotations();
//Highlight all responses where the request had a Content-Length header.
if (responseHasContentLengthHeader(responseReceived)) {
annotations = annotations.withHighlightColor(HighlightColor.BLUE);
}

return continueWith(responseReceived, annotations);
}

private static boolean isPost(HttpRequestToBeSent httpRequestToBeSent) {
return httpRequestToBeSent.method().equalsIgnoreCase("POST");
}

private static boolean responseHasContentLengthHeader(HttpResponseReceived httpResponseReceived) {
return httpResponseReceived.initiatingRequest().headers().stream().anyMatch(header -> header.name().equalsIgnoreCase("Content-Length"));
}
}

HttpHandlerExample.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;


//Burp will auto-detect and load any class that extends BurpExtension.
public class HttpHandlerExample implements BurpExtension {
@Override
public void initialize(MontoyaApi api) {
api.extension().setName("HTTP Handler Example");

//Register our http handler with Burp.
api.http().registerHttpHandler(new MyHttpHandler(api));
}
}

Proxy Handler Example Extension

The extension works as follows:

  • It registers a Proxy handler
  • For outgoing request messages:
    • It drops all POST requests
    • Requests with foo in the URL are not intercepted
    • If the Content-Type is JSON, the request is highlighted RED and Burp rules for Interception are followed
    • All other requests are intercepted
    • User modified requests are continued as normal
  • For incoming response messages:
    • All responses that contain the string username are highlighted BLUE
    • All other responses follow Burp rules for Interception

ProxyHandlerExample.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;

//Burp will auto-detect and load any class that extends BurpExtension.
public class ProxyHandlerExample implements BurpExtension
{
@Override
public void initialize(MontoyaApi api)
{
api.extension().setName("Proxy Handler Example");

//Register proxy handlers with Burp.
api.proxy().registerRequestHandler(new MyProxyHttpRequestHandler());
api.proxy().registerResponseHandler(new MyProxyHttpResponseHandler());
}
}

MyProxyHttpResponseHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import burp.api.montoya.proxy.http.InterceptedResponse;
import burp.api.montoya.proxy.http.ProxyResponseHandler;
import burp.api.montoya.proxy.http.ProxyResponseReceivedAction;
import burp.api.montoya.proxy.http.ProxyResponseToBeSentAction;

import static burp.api.montoya.core.HighlightColor.BLUE;

class MyProxyHttpResponseHandler implements ProxyResponseHandler {
@Override
public ProxyResponseReceivedAction handleResponseReceived(InterceptedResponse interceptedResponse) {
//Highlight all responses that have username in them
if (interceptedResponse.bodyToString().contains("username")) {
return ProxyResponseReceivedAction.continueWith(interceptedResponse, interceptedResponse.annotations().withHighlightColor(BLUE));
}

return ProxyResponseReceivedAction.continueWith(interceptedResponse);
}

@Override
public ProxyResponseToBeSentAction handleResponseToBeSent(InterceptedResponse interceptedResponse) {
return ProxyResponseToBeSentAction.continueWith(interceptedResponse);
}
}

MyProxyHttpRequestHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import burp.api.montoya.proxy.http.InterceptedRequest;
import burp.api.montoya.proxy.http.ProxyRequestHandler;
import burp.api.montoya.proxy.http.ProxyRequestReceivedAction;
import burp.api.montoya.proxy.http.ProxyRequestToBeSentAction;

import static burp.api.montoya.core.HighlightColor.RED;
import static burp.api.montoya.http.message.ContentType.JSON;

class MyProxyHttpRequestHandler implements ProxyRequestHandler {
@Override
public ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest) {
//Drop all post requests
if (interceptedRequest.method().equals("POST")) {
return ProxyRequestReceivedAction.drop();
}

//Do not intercept any request with foo in the url
if (interceptedRequest.url().contains("foo")) {
return ProxyRequestReceivedAction.doNotIntercept(interceptedRequest);
}

//If the content type is json, highlight the request and follow burp rules for interception
if (interceptedRequest.contentType() == JSON) {
return ProxyRequestReceivedAction.continueWith(interceptedRequest, interceptedRequest.annotations().withHighlightColor(RED));
}

//Intercept all other requests
return ProxyRequestReceivedAction.intercept(interceptedRequest);
}

@Override
public ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest) {
//Do nothing with the user modified request, continue as normal.
return ProxyRequestToBeSentAction.continueWith(interceptedRequest);
}
}


观察可以看到上面的代码似乎已经取代了我这里的拦截,因为即便是拦截关闭的话,也依旧是拦截状态

Event Listeners Example Extension

This extension demonstrates how to register listeners for various runtime events:

  • HTTP requests and responses for all Burp tools.
  • HTTP messages intercepted by the Proxy.
  • Addition of new scan issues.
  • The extension being unloaded by the user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public class EventListeners implements BurpExtension {
private Logging logging;

@Override
public void initialize(MontoyaApi api) {
logging = api.logging();

Http http = api.http();
Proxy proxy = api.proxy();
Extension extension = api.extension();
Scanner scanner = api.scanner();

// set extension name
extension.setName("Event listeners");

// register a new HTTP handler
http.registerHttpHandler(new MyHttpHandler(api));

// register new Proxy handlers
proxy.registerRequestHandler(new MyProxyRequestHandler(api));
proxy.registerResponseHandler(new MyProxyResponseHandler(api));

// register a new Audit Issue handler
scanner.registerAuditIssueHandler(new MyAuditIssueListenerHandler());

// register a new extension unload handler
extension.registerUnloadingHandler(new MyExtensionUnloadHandler());
}

private class MyAuditIssueListenerHandler implements AuditIssueHandler {
@Override
public void handleNewAuditIssue(AuditIssue auditIssue) {
logging.logToOutput("New scan issue: " + auditIssue.name());
}
}

private class MyExtensionUnloadHandler implements ExtensionUnloadingHandler {
@Override
public void extensionUnloaded() {
logging.logToOutput("Extension was unloaded.");
}
}
}

public class MyHttpHandler implements HttpHandler
{
private final Logging logging;

public MyHttpHandler(MontoyaApi api)
{
logging = api.logging();
}

@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent httpRequestToBeSent) {
logging.logToOutput("HTTP request to " + httpRequestToBeSent.httpService() + " [" + httpRequestToBeSent.toolSource().toolType().toolName() + "]");

return RequestToBeSentAction.continueWith(httpRequestToBeSent);
}

@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived httpResponseReceived) {
logging.logToOutput("HTTP response from " + httpResponseReceived.initiatingRequest().httpService() + " [" + httpResponseReceived.toolSource().toolType().toolName() + "]");

return ResponseReceivedAction.continueWith(httpResponseReceived);
}
}

public class MyProxyRequestHandler implements ProxyRequestHandler
{
private final Logging logging;

public MyProxyRequestHandler(MontoyaApi api)
{
logging = api.logging();
}

@Override
public ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest) {
logging.logToOutput("Initial intercepted proxy request to " + interceptedRequest.httpService());

return ProxyRequestReceivedAction.continueWith(interceptedRequest);
}

@Override
public ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest) {
logging.logToOutput("Final intercepted proxy request to " + interceptedRequest.httpService());

return ProxyRequestToBeSentAction.continueWith(interceptedRequest);
}
}

public class MyProxyResponseHandler implements ProxyResponseHandler
{
private final Logging logging;

public MyProxyResponseHandler(MontoyaApi api)
{
logging = api.logging();
}

@Override
public ProxyResponseReceivedAction handleResponseReceived(InterceptedResponse interceptedResponse) {
logging.logToOutput("Initial intercepted proxy response from " + interceptedResponse.initiatingRequest().httpService());

return ProxyResponseReceivedAction.continueWith(interceptedResponse);
}

@Override
public ProxyResponseToBeSentAction handleResponseToBeSent(InterceptedResponse interceptedResponse) {
logging.logToOutput("Final intercepted proxy response from " + interceptedResponse.initiatingRequest().httpService());

return ProxyResponseToBeSentAction.continueWith(interceptedResponse);
}

}

原来上面的输出out是指记录在这里导入插件之后的out
在插件页面这里也可以看到

Traffic Redirector Example Extension

This extension demonstrates how to redirect outgoing HTTP requests from one host to another. This task might arise, for example, if you have mapped out an application which then moves to a different staging URL. By simply redirecting traffic to the new hostname, you can continue to drive your testing from the original site map.

The extension works as follows:

  • It registers an HTTP handler.
  • For outgoing request messages, it retrieves the HTTP service for the request.
  • If the HTTP service host matches the “from” host, builds an HTTP service using the “to” host, and other details unchanged.
  • It returns the HTTP request with the new HTTP service.

Note: The sample code uses “host1.example.org” and “host2.example.org” as the “from” and “to” hostnames. You should edit the code to use your own hostnames before using it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class TrafficRedirector implements BurpExtension {
static final String HOST_FROM = "host1.example.org";
static final String HOST_TO = "host2.example.org";

@Override
public void initialize(MontoyaApi api) {
// set extension name
api.extension().setName("Traffic redirector");

// register a new HTTP handler
api.http().registerHttpHandler(new MyHttpHandler());
}
}

import burp.api.montoya.http.HttpService;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.http.message.requests.HttpRequest;

import static burp.api.montoya.http.HttpService.httpService;
import static burp.api.montoya.http.handler.RequestToBeSentAction.continueWith;

public class MyHttpHandler implements HttpHandler
{
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent httpRequestToBeSent) {
HttpService service = httpRequestToBeSent.httpService();

if (TrafficRedirector.HOST_FROM.equalsIgnoreCase(service.host())) {
HttpRequest updatedHttpServiceRequest = httpRequestToBeSent.withService(httpService(TrafficRedirector.HOST_TO, service.port(), service.secure()));
HttpRequest updatedHostHeaderRequest = updatedHttpServiceRequest.withUpdatedHeader("Host", TrafficRedirector.HOST_TO);

return continueWith(updatedHostHeaderRequest);
}

return continueWith(httpRequestToBeSent);
}

@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived httpResponseReceived) {
return ResponseReceivedAction.continueWith(httpResponseReceived);
}
}

Custom Logger Example Extension

Adds a new tab to Burp’s UI and displays a log of HTTP traffic for all Burp tools.


This extension provides a suite-wide HTTP logger within the main Burp UI.

The extension uses the following techniques:

  • It creates a custom tab within the main Burp UI, in which to display logging user interface
  • It displays a table of items and a read-only editor for requests and responses within a splitpane
  • When an item passes through the HttpHandler, it gets added to the table
  • You can view the request and response for an item in the table by clicking on the relevant row
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// 引入所需的类和接口
import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.handler.HttpResponseReceived;
import burp.api.montoya.ui.UserInterface;
import burp.api.montoya.ui.editor.HttpRequestEditor;
import burp.api.montoya.ui.editor.HttpResponseEditor;

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

// 使编辑器为只读
import static burp.api.montoya.ui.editor.EditorOptions.READ_ONLY;

// 实现 BurpExtension 接口的 CustomLogger 类
public class CustomLogger implements BurpExtension {
private MontoyaApi api;

@Override
public void initialize(MontoyaApi api) {
this.api = api; // 存储 MontoyaApi 实例
api.extension().setName("Custom logger"); // 设置插件的名称

MyTableModel tableModel = new MyTableModel(); // 创建表格模型实例
api.userInterface().registerSuiteTab("Custom logger", constructLoggerTab(tableModel)); // 注册插件UI到Burp的标签页
api.http().registerHttpHandler(new MyHttpHandler(tableModel)); // 注册HTTP处理器来记录响应
}

// 构建日志标签页的方法,接受一个 MyTableModel 对象作为参数
private Component constructLoggerTab(MyTableModel tableModel) {
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); // 创建垂直分割面板

JTabbedPane tabs = new JTabbedPane(); // 创建标签页容器

UserInterface userInterface = api.userInterface();
HttpRequestEditor requestViewer = userInterface.createHttpRequestEditor(READ_ONLY); // 创建请求编辑器,设置为只读
HttpResponseEditor responseViewer = userInterface.createHttpResponseEditor(READ_ONLY); // 创建响应编辑器,同样设置为只读

tabs.addTab("Request", requestViewer.uiComponent()); // 添加请求编辑器到标签页
tabs.addTab("Response", responseViewer.uiComponent()); // 添加响应编辑器到标签页

splitPane.setRightComponent(tabs); // 设置分割面板的右侧组件为标签页

JTable table = new JTable(tableModel) {
@Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
// 选择表格行时显示相应的HTTP请求和响应
HttpResponseReceived responseReceived = tableModel.get(rowIndex);
requestViewer.setRequest(responseReceived.initiatingRequest());
responseViewer.setResponse(responseReceived);

super.changeSelection(rowIndex, columnIndex, toggle, extend); // 调用父类方法处理选择逻辑
}
};

JScrollPane scrollPane = new JScrollPane(table); // 将表格放在滚动窗格中

splitPane.setLeftComponent(scrollPane); // 设置分割面板的左侧组件为滚动窗格

return splitPane; // 返回构建好的组件
}
}

import burp.api.montoya.http.handler.*;

// 实现 HttpHandler 接口的 MyHttpHandler 类
public class MyHttpHandler implements HttpHandler
{
private final MyTableModel tableModel; // 私有最终成员变量,用于存储引用到 MyTableModel 对象

// 构造函数,接收一个 MyTableModel 实例
public MyHttpHandler(MyTableModel tableModel)
{
this.tableModel = tableModel; // 存储传入的 MyTableModel 对象
}

// 处理即将发送的 HTTP 请求的方法
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent)
{
// 这个方法目前仅将请求继续传递出去,没有进行修改或其他处理
return RequestToBeSentAction.continueWith(requestToBeSent);
}

// 处理接收到的 HTTP 响应的方法
@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived)
{
tableModel.add(responseReceived); // 将接收到的响应添加到表格模型中,用于日志记录
// 这个方法也将响应继续传递出去,确保其他可能的处理器或 Burp 本身可以进一步处理响应
return ResponseReceivedAction.continueWith(responseReceived);
}
}

import burp.api.montoya.http.handler.HttpResponseReceived;

import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.List;

// 继承自 AbstractTableModel 的 MyTableModel 类,用于管理和显示 HTTP 响应数据
public class MyTableModel extends AbstractTableModel
{
private final List<HttpResponseReceived> log; // 私有最终列表,用于存储 HttpResponseReceived 对象

// 构造函数,初始化 log 列表
public MyTableModel()
{
this.log = new ArrayList<>();
}

// 获取表格的行数,等于 log 列表的大小
@Override
public synchronized int getRowCount()
{
return log.size();
}

// 获取表格的列数,这里固定为 2 列
@Override
public int getColumnCount()
{
return 2;
}

// 根据列索引获取列名
@Override
public String getColumnName(int column)
{
return switch (column)
{
case 0 -> "Tool"; // 第一列显示使用的工具
case 1 -> "URL"; // 第二列显示请求的 URL
default -> "";
};
}

// 获取指定单元格的值,用于表格显示
@Override
public synchronized Object getValueAt(int rowIndex, int columnIndex)
{
HttpResponseReceived responseReceived = log.get(rowIndex);

return switch (columnIndex)
{
case 0 -> responseReceived.toolSource().toolType(); // 第一列显示工具类型
case 1 -> responseReceived.initiatingRequest().url(); // 第二列显示请求 URL
default -> "";
};
}

// 添加一个新的 HttpResponseReceived 对象到 log 列表,并通知表格有新行插入
public synchronized void add(HttpResponseReceived responseReceived)
{
int index = log.size();
log.add(responseReceived);
fireTableRowsInserted(index, index); // 触发表格更新事件
}

// 根据行索引获取 HttpResponseReceived 对象
public synchronized HttpResponseReceived get(int rowIndex)
{
return log.get(rowIndex);
}
}

上面创建的面板界面如图所示

Custom Request Editor Tab Example Extension

Adds a new tab to Burp’s HTTP message editor, in order to handle a data serialization format


This extension provides a new tab on the message editor for requests that contain a specified parameter.

The extension uses the following techniques:

  • It creates a custom request tab on the message editor, provided that the data parameter is present
  • If it is appropriate, the editor is set to be read-only
  • The value of the data parameter is deserialized (URL decoded, then Base64 decoded) and displayed in the custom tab
  • If the value of the data is modified, the content will be re-serialized (Base64 encoded then URL encoded) and updated in the HttpRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider;

// 实现 HttpRequestEditorProvider 接口的 MyHttpRequestEditorProvider 类
class MyHttpRequestEditorProvider implements HttpRequestEditorProvider
{
private final MontoyaApi api; // 私有最终字段,用于存储 MontoyaApi 实例

// 构造函数,接收一个 MontoyaApi 实例
MyHttpRequestEditorProvider(MontoyaApi api)
{
this.api = api;
}

// 提供自定义的 HTTP 请求编辑器的方法
@Override
public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext creationContext)
{
// 返回一个新创建的 MyExtensionProvidedHttpRequestEditor 实例
// 传递 MontoyaApi 实例和编辑器创建上下文
return new MyExtensionProvidedHttpRequestEditor(api, creationContext);
}
}

import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.params.HttpParameter;
import burp.api.montoya.http.message.params.ParsedHttpParameter;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.ui.Selection;
import burp.api.montoya.ui.editor.EditorOptions;
import burp.api.montoya.ui.editor.RawEditor;
import burp.api.montoya.ui.editor.extension.EditorCreationContext;
import burp.api.montoya.ui.editor.extension.EditorMode;
import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor;
import burp.api.montoya.utilities.Base64EncodingOptions;
import burp.api.montoya.utilities.Base64Utils;
import burp.api.montoya.utilities.URLUtils;

import java.awt.*;
import java.util.Optional;

import static burp.api.montoya.core.ByteArray.byteArray;

// 自定义 HTTP 请求编辑器实现
class MyExtensionProvidedHttpRequestEditor implements ExtensionProvidedHttpRequestEditor
{
private final RawEditor requestEditor; // 原始编辑器接口
private final Base64Utils base64Utils; // Base64工具类
private final URLUtils urlUtils; // URL工具类
private HttpRequestResponse requestResponse; // 当前的请求响应对象
private final MontoyaApi api; // Montoya API 接口
private ParsedHttpParameter parsedHttpParameter; // 解析的HTTP参数

// 构造函数
MyExtensionProvidedHttpRequestEditor(MontoyaApi api, EditorCreationContext creationContext)
{
this.api = api;
base64Utils = api.utilities().base64Utils();
urlUtils = api.utilities().urlUtils();

// 根据编辑模式创建编辑器
if (creationContext.editorMode() == EditorMode.READ_ONLY)
{
requestEditor = api.userInterface().createRawEditor(EditorOptions.READ_ONLY);
}
else {
requestEditor = api.userInterface().createRawEditor();
}
}

// 获取编辑后的请求
@Override
public HttpRequest getRequest()
{
HttpRequest request;

if (requestEditor.isModified())
{
// 如果编辑器被修改,重新序列化数据
String base64Encoded = base64Utils.encodeToString(requestEditor.getContents(), Base64EncodingOptions.URL);
String encodedData = urlUtils.encode(base64Encoded);

// 更新请求参数
request = requestResponse.request().withUpdatedParameters(HttpParameter.parameter(parsedHttpParameter.name(), encodedData, parsedHttpParameter.type()));
}
else
{
request = requestResponse.request();
}

return request;
}

// 设置请求响应对象
@Override
public void setRequestResponse(HttpRequestResponse requestResponse)
{
this.requestResponse = requestResponse;

// 解码参数值
String urlDecoded = urlUtils.decode(parsedHttpParameter.value());
ByteArray output;

try
{
output = base64Utils.decode(urlDecoded);
}
catch (Exception e)
{
output = byteArray(urlDecoded);
}

// 设置编辑器内容
this.requestEditor.setContents(output);
}

// 确定编辑器是否适用于给定的请求响应
@Override
public boolean isEnabledFor(HttpRequestResponse requestResponse)
{
Optional<ParsedHttpParameter> dataParam = requestResponse.request().parameters().stream()
.filter(p -> p.name().equals("data")).findFirst();

dataParam.ifPresent(httpParameter -> parsedHttpParameter = httpParameter);

return dataParam.isPresent();
}

// 编辑器的标题
@Override
public String caption()
{
return "Serialized input";
}

// 获取UI组件
@Override
public Component uiComponent()
{
return requestEditor.uiComponent();
}

// 获取选中的数据
@Override
public Selection selectedData()
{
return requestEditor.selection().isPresent() ? requestEditor.selection().get() : null;
}

// 判断编辑器内容是否被修改
@Override
public boolean isModified()
{
return requestEditor.isModified();
}
}

import burp.api.montoya.BurpExtension; // 引入 BurpExtension 接口
import burp.api.montoya.MontoyaApi; // 引入 MontoyaApi,用于访问 Burp Suite API

// 实现 BurpExtension 接口的 CustomRequestEditorTab 类
public class CustomRequestEditorTab implements BurpExtension
{
@Override
public void initialize(MontoyaApi api)
{
// 设置插件的名称
api.extension().setName("Serialized input editor");

// 在 Burp 用户界面中注册自定义的 HTTP 请求编辑器提供者
// MyHttpRequestEditorProvider 是一个自定义类,负责提供编辑器的具体实现
api.userInterface().registerHttpRequestEditorProvider(new MyHttpRequestEditorProvider(api));
}
}

上面的代码为含有参数data的请求新建了特殊的编辑器,可以对data参数进行配置修改

Custom Scan Checks Example Extension

The sample extension demonstrates the following techniques:

  • Registering a custom AuditInsertionPointProvider
  • If the request contains the data parameter, it will provide a custom AuditInsertionPoint
  • The custom AuditInsertionPoint will perform the following:
    • Deserialize the data (URL decode and then Base64 decode)
    • Parse the location of the input= string withing the decoded data
    • Split the data into a prefix, location to place the payload, and a suffix
    • When building the request with the appropriate payload, it will perform the following:
      • Build the raw data with the appropriate payload
      • Re-serialize the data (Base64 encode then URL encode)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import burp.api.montoya.MontoyaApi;  // 引入 MontoyaApi,用于访问 Burp Suite 的 API
import burp.api.montoya.http.message.HttpRequestResponse; // 引入用于处理 HTTP 请求响应的类
import burp.api.montoya.http.message.params.ParsedHttpParameter; // 引入用于解析 HTTP 参数的类
import burp.api.montoya.scanner.audit.insertionpoint.AuditInsertionPoint; // 引入审核插入点接口
import burp.api.montoya.scanner.audit.insertionpoint.AuditInsertionPointProvider; // 引入审核插入点提供者接口

import java.util.List; // 引入 Java 的 List 接口
import static java.util.stream.Collectors.toList; // 引入用于收集 Stream 结果的工具方法

// 实现 AuditInsertionPointProvider 接口的 MyInsertionPointProvider 类
class MyInsertionPointProvider implements AuditInsertionPointProvider
{
private final MontoyaApi api; // 保存 MontoyaApi 实例的私有最终字段

// 构造函数,接收一个 MontoyaApi 实例
MyInsertionPointProvider(MontoyaApi api)
{
this.api = api;
}

// 重写提供插入点的方法
@Override
public List<AuditInsertionPoint> provideInsertionPoints(HttpRequestResponse baseHttpRequestResponse)
{
// 从请求中获取所有参数
List<ParsedHttpParameter> parameters = baseHttpRequestResponse.request().parameters();

// 过滤出名为 "data" 的参数,并为每个这样的参数创建一个新的审核插入点
return parameters.stream()
.filter(p -> p.name().equals("data")) // 过滤出参数名为 "data" 的参数
.map(p -> new MyAuditInsertionPoint(api, baseHttpRequestResponse, p)) // 为每个符合条件的参数创建一个 MyAuditInsertionPoint 实例
.collect(toList()); // 收集结果到 List 中
}
}

import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.core.Range;
import burp.api.montoya.http.message.HttpRequestResponse;
import burp.api.montoya.http.message.params.HttpParameter;
import burp.api.montoya.http.message.params.ParsedHttpParameter;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.scanner.audit.insertionpoint.AuditInsertionPoint;
import burp.api.montoya.utilities.Utilities;

import java.util.List;

import static burp.api.montoya.http.message.params.HttpParameter.parameter;

// 实现 AuditInsertionPoint 接口的 MyAuditInsertionPoint 类
class MyAuditInsertionPoint implements AuditInsertionPoint
{
private final String insertionPointPrefix; // 插入点之前的文本
private final String insertionPointSuffix; // 插入点之后的文本
private final HttpRequestResponse requestResponse; // 请求响应对象
private final ParsedHttpParameter parameter; // 被操作的 HTTP 参数
private final String baseValue; // 参数值的基本形式
private final Utilities utilities; // Utilities 对象,提供编解码等功能

// 构造函数,初始化参数和实用工具
MyAuditInsertionPoint(MontoyaApi api, HttpRequestResponse baseHttpRequestResponse, ParsedHttpParameter parameter)
{
this.requestResponse = baseHttpRequestResponse;
this.parameter = parameter;
this.utilities = api.utilities();

String paramValue = parameter.value();

// URL 和 base-64 解码数据
String urlDecoded = utilities.urlUtils().decode(paramValue);
ByteArray byteData = utilities.base64Utils().decode(urlDecoded);

String data = byteData.toString();

// 解析输入字符串在解码数据中的位置
int start = data.indexOf("input=") + 6;
int end = data.indexOf("&", start);

if (end == -1)
{
end = data.length();
}

baseValue = data.substring(start, end);

insertionPointPrefix = data.substring(0, start);
insertionPointSuffix = data.substring(end);
}

// 返回插入点的名称
@Override
public String name()
{
return "Base64-wrapped input";
}

// 返回原始值
@Override
public String baseValue()
{
return baseValue;
}

// 使用特定的 payload 构建带有负载的 HTTP 请求
@Override
public HttpRequest buildHttpRequestWithPayload(ByteArray payload)
{
// 构建原始数据使用指定的负载
String input = insertionPointPrefix + payload.toString() + insertionPointSuffix;

// Base-64 和 URL 编码数据
String updatedParameterValue = utilities.urlUtils().encode(utilities.base64Utils().encodeToString(input));

// 更新 HTTP 参数
HttpParameter updatedParameter = parameter(parameter.name(), updatedParameterValue, parameter.type());

// 返回更新参数的 HTTP 请求
return requestResponse.request().withUpdatedParameters(updatedParameter);
}

// 返回问题高亮区域的方法,此处未实现
@Override
public List<Range> issueHighlights(ByteArray payload)
{
return null;
}
}

import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;

public class CustomScanInsertionPoints implements BurpExtension
{
@Override
public void initialize(MontoyaApi api)
{
api.extension().setName("Custom scan insertion points");

api.scanner().registerInsertionPointProvider(new MyInsertionPointProvider(api));
}
}

上面这个示例不是很懂,也没找到对应的出发点