Burpsuite Java插件开发 - API篇
网上也有一些BP插件API相关的文章,本文主要就写BP插件API的一些细节,但不会覆盖到所有的API。下方是官方的API文档和示例代码链接。入门BP插件编写,主要就是看官方的几个示例代码,去编译运行,去用用,去查API文档,就差不多了。
旧API:
- 官方文档:https://portswigger.net/burp/extender
- API: https://portswigger.net/burp/extender/api/index.html
- 代码示例:https://github.com/PortSwigger?q=example-&type=all&language=&sort=
新API:
- 官方文档:https://portswigger.net/burp/documentation/desktop/extensions/creating
- API:https://portswigger.github.io/burp-extensions-montoya-api/javadoc/index.html(2022年10月,官方出的新API)
- 代码示例:https://github.com/PortSwigger/burp-extensions-montoya-api-examples
BP插件API都出新的了,不过这里还是说旧API,目前网上使用旧API的插件还是比较多的。
文中的代码主要在 Burpsuite Pro v2023.4.5 版本下测试运行。
void IBurpExtenderCallbacks.setExtensionName(java.lang.String name)
设置插件名。最好在插件入口一开始就设置。 因为插件名跟插件配置绑定,所以最好不要随意更改插件名。 插件名也被用于插件自己添加的TAB页的标题和右键 - Extensions 菜单显示。
void IBurpExtenderCallbacks.customizeUiComponent(java.awt.Component component)
将指定的UI组件调整为Burp的UI风格。
某些组件调用了这个方法会导致问题 :
- 鼠标不能拖动JTable表格的列头以更换顺序,即使表格调用了
table.getTableHeader().setReorderingAllowed(true)
也不行 JComboBox
下拉框不能编辑,即使设置了Editable
为true
。
解决方式:
- 先调用
customizeUiComponent
方法,再修改组件(比如将JComboBox
组件的Editable
为true
)。 - 不要调用
customizeUiComponent
方法,实际测试调不调用这个方法对界面外观没有什么影响。(所以这个方法是做了什么操作?没去细究)
void IBurpExtenderCallbacks.addScanIssue(IScanIssue issue)
将Issue
添加到Burpsuite
的Dashboard
。
需自己编写一个实现IScanIssue
接口的类。无论该类的getIssueType()
方法返回什么值,由IScannerListener
监听拿到的issue
对象的issueType
固定是 0x08000000
,表示是由插件添加的issue
。IssueType
列表见:https://portswigger.net/kb/issues 或 打开 bupsuite - Target - Issue definitions
查看。
byte[] IExtensionHelpers.updateParameter(byte[] request, IParameter parameter)
更新HTTP请求中参数的值。如果更新了请求body,还会添加或更新Content-Length请求头。
该方法不会自动对参数值编码。且仅支持IParameter.PARAM_URL
, IParameter.PARAM_BODY
和 IParameter.PARAM_COOKIE
类型的IParameter
对象,传递其他类型的IParameter
对象会抛异常。
示例:
1 |
|
void IExtensionStateListener.extensionUnloaded()
该回调方法在卸载(unload)插件时被调用,关闭Burp时不会被调用。
通常在这个回调方法里关闭插件里启动的后台线程和连接、文件资源等,不然线程还会继续运行,容易造成问题。
IRequestInfo
调用IExtensionHelpers
对象的IRequestInfo analyzeRequest(byte[] request)
方法解析请求数据,返回IRequestInfo
对象,该对象包含一些请求信息,如请求头,参数列表等。IRequestInfo
对象的几个方法的细节:
java.util.List<IParameter> IRequestInfo.getParameters()
:返回请求包里的参数列表。burp支持解析的参数类型有URL query参数、Cookie、body里的urlencoded格式、json格式、xml格式或multipart格式的参数。对于multipart格式请求包的解析结果需要说明下,以下示例代码: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
35package burp;
public class BurpExtender implements IBurpExtender
{
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
{
callbacks.setExtensionName("Test Extension");
IExtensionHelpers helpers = callbacks.getHelpers();
byte[] request = ("POST / HTTP/1.1\r\n" +
"Host: example.org\r\n" +
"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxUcPkAgfQy8GyoF5\r\n" +
"Content-Length: 259\r\n" +
"\r\n" +
"------WebKitFormBoundaryxUcPkAgfQy8GyoF5\r\n" +
"Content-Disposition: form-data; name=\"note\";\r\n" +
"\r\n" +
"test\r\n" +
"------WebKitFormBoundaryxUcPkAgfQy8GyoF5\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" +
"\r\n" +
"content\r\n" +
"------WebKitFormBoundaryxUcPkAgfQy8GyoF5--\r\n").getBytes();
IRequestInfo requestInfo = helpers.analyzeRequest(request);
for(IParameter parameter: requestInfo.getParameters()) {
callbacks.printOutput(String.valueOf(parameter.getType()));
callbacks.printOutput(parameter.getName());
callbacks.printOutput(parameter.getValue());
callbacks.printOutput("---------------------");
}
}
}输出:
1
2
3
4
5
6
7
8
9
10
11
121 // 即 IParameter.PARAM_BODY
note
test
---------------------
5 // 即 IParameter.PARAM_MULTIPART_ATTR
filename
test.txt
---------------------
1 // 即 IParameter.PARAM_BODY
file
content
---------------------IParameter.PARAM_MULTIPART_ATTR
类型的参数,指multipart属性,即这里的filename="test.txt"
部分,而body里的其他参数归于IParameter.PARAM_BODY
这类。body里的urlencoded格式参数也是IParameter.PARAM_BODY
。另外对于xml这种
<empty/>
空元素不解析,但解析<empty></empty>
这种,类型为IParameter.PARAM_XML
。java.util.List<java.lang.String> IRequestInfo.getHeaders()
:获取请求头列表,包含请求体第一行(如:GET / HTTP/1.1
)byte IRequestInfo.getContentType()
: 返回body的Content Type。其不是根据Content-Type
请求头判断,而是根据body内容来判断:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package burp;
public class BurpExtender implements IBurpExtender
{
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
{
callbacks.setExtensionName("Test Extension");
IExtensionHelpers helpers = callbacks.getHelpers();
byte[] request = "POST / HTTP/1.1\r\nHost: test.com\r\nContent-Type: application/json\r\n\r\n<user>admin</user>".getBytes();
IRequestInfo requestInfo = helpers.analyzeRequest(request);
callbacks.printOutput("ct: " + requestInfo.getContentType());
}
}
/*
输出:
3 // 即 IRequestInfo.CONTENT_TYPE_XML
*/
IResponseInfo
调用IExtensionHelpers
对象的IResponseInfo analyzeResponse(byte[] response)
方法解析请求数据,返回IResponseInfo
对象,该对象包含一些响应信息,如状态码,响应头,MIME类型等。
IResponseInfo
对象获取MIME类型有两个方法:
java.lang.String IResponseInfo.getStatedMimeType()
:根据Content-Type
响应头返回MIME类型java.lang.String IResponseInfo.getInferredMimeType()
:根据body内容判断MIME类型。 (在Burp的Proxy
面板的MIME列就是根据body内容判断的)
返回值可能是HTML
、GIF
、SVG
、image
、PNG
、JPEG
、CSS
、JSON
、XML
、script
、video
或空字符串等。
IParameter
上面提到通过解析请求数据,获取请求参数列表,即IParameter
对象列表。通过IParameter
对象的getValue()
方法可以获取到参数值,需要注意的是,获取到的值为未解码过的。示例:
1 |
|
输出:
1 |
|
上方的query参数/?url=http:%2f%2f
,拿到的值是http:%2f%2f
,burp没有对其进行解码,在数据包里什么样就是什么样。
对于JSON参数 {"usernmae": "admin", "rememberme": 1}
的"admin"
和1
参数值,getValue()
返回值分别是admin
和1
,并不能从值去判断该JSON参数是字符串还是数字,可以使用request[parameter.getValueStart()-1] == '"'
来判断。
IScannerCheck
通过 IBurpExtenderCallbacks
对象的registerScannerCheck(IScannerCheck check)
方法注册一个IScannerCheck
对象来向burp添加自己的扫描检测。
IScannerCheck
接口的几个方法:
int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue)
:当同一个URL路径报了多个issue,该方法就会被调用。若返回-1
则只报告existingIssue
,若返回0
则报告这两个issue
,若返回1则报告newIssue
。通常返回0
。java.util.List<IScanIssue> doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint)
:主动扫描时被调用,需要时可在此方法里发包。 返回值为issue列表,其会被加到Burp的Dashboard的Issue Activity
面板里。(也可以直接返回空列表,然后通过调用IBurpExtenderCallbacks.addScanIssue(IScanIssue issue)
方法添加issue
)java.util.List<IScanIssue> doPassiveScan(IHttpRequestResponse baseRequestResponse)
:被动检测时被调用,返回值为issue列表,其会被加到Burp的Dashboard的Issue Activity
面板里。
调用burp主动扫描的一种方式是在请求项上右键 - Do Active Scan
。对于被动检测,burp默认会在Dashboard
的Task
面板中建立一个Audit checks - passive
的任务,该任务就是对Proxy
流量进行被动检测,或者自己在请求项上右键 - Do passive scan
。
Github上很多burp被动扫描插件会在IScannerCheck
的 doPassiveScan
这个回调方法上发包检测漏洞,但官方建议是在这个回调方法里不发包,仅做数据包检测(如匹配敏感信息等),所以被动扫描器跟burp
的doPassiveScan
被动检测的”被动“不是一个意思。
假如一个FastJson被动扫描插件在doPassiveScan
这个回调方法里做检测,且该插件有一个是否开启FastJson扫描的配置,就会出现这样的bug:
- 用户将浏览器流量代理到burp,且FastJson被动扫描插件为关闭状态。
- 用户访问
https://example.com/api
,burp会调用doPassiveScan
回调方法,但FastJson被动扫描插件为关闭状态,插件直接不检测。 - 用户开启FastJson被动扫描插件,再访问
https://example.com/api
,burp不会再调用doPassiveScan
回调方法了,导致即使开启了FastJson被动扫描插件也不会对https://example.com/api
扫描。
原因在于,burp对于拥有相同的URL路径和参数键名的不同请求只会调用一次doPassiveScan
。这个行为可以在 Dashboard - Task - "Audit check - passive" - Depulication
配置里进行关闭。
所以如果想要实现一个burp被动扫描插件,应该监听Proxy流量,然后自行判断该请求是否扫描过,避免重复扫描。
makeHttpRequest
调用IBurpExtenderCallbacks
的makeHttpRequest
方法来发送http请求,有几个问题:
该方法为同步发送请求,没有提供异步发送的方式。
该方法不跟踪跳转。(测试发现,跟burp的
Settings - Network - HTTP - Allowed redirect type
这个配置无关)有个默认的超时时间(120秒),可在burp的
Settings - Network - Connections - Timeouts
里配置。大部分用户不会去配置,导致用burp api
发大量请求时会比较慢。makeHttpRequest
方法没有设置超时时间的参数。发生连接不上、超时等错误时也不会抛异常。只能通过返回值判断下是否发生错误:
IHttpRequestResponse makeHttpRequest(IHttpService httpService, byte[] request)
:返回值IHttpRequestResponse
对象的getResponse()
方法返回null
或者长度为0的byte数组。byte[] makeHttpRequest(java.lang.String host, int port, boolean useHttps, byte[] request)
:返回值为null
或者长度为0的byte数组。具体的错误信息只在 Burp 的
Dashboard - Event Log
里可以看到。
如果不使用burp提供的API,而是用第三方库(如okhttp3)发送请求,需要注意:
- 发送的请求不会被记录到
Logger
面板里。 - burp里的配置不会影响到第三方库,如代理设置。
urlEncode 和 urlDecode
IExtensionHelpers
对象提供的URL编码和URL解码函数:
byte[] urlDecode(byte[] data)
java.lang.String urlDecode(java.lang.String data)
byte[] urlEncode(byte[] data)
:不对'<>/\"
等编码,对?#@:
等编码,将空格转为+
号而不是%20
。java.lang.String urlEncode(java.lang.String data)
:同上
这几个函数并不处理编码方式。下方示例会打印乱码与错误结果:
1 |
|
如果要URL解码支持中文,需调用URL解码为byte数组,再指定编码方式将byte数组转为字符串。示例:
1 |
|
如果要对中文进行URL编码:
1 |
|
上方示例并没有输出期望的结果 "%e4%b8%ad%2b%e6%96%87"
,而是输出 "中%2b文"
。这是因为burp的urlEncode
方法不对不可见字节(如空字节、0xe4)进行url编码。所以想要期望的效果,需要自己实现URL编码方法了。
bytesToString 和 stringToBytes
IExtensionHelpers
对象提供的在字节数组与字符串之间转换的方法:
java.lang.String bytesToString(byte[] data)
byte[] stringToBytes(java.lang.String data)
这两个方法也是不考虑编码方式,直接将字符串中的各个char字符和byte之间转换。示例:
1 |
|
如果考虑中文字符的编码,就不要用这两个方法,使用:
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!