BurpSuite插件编写[中级篇]

0x00 前言

在初级篇中,我们分别介绍了burp的debug的方法和一个官方插件库中的一个burp插件源码分析,而该中级篇则将讲的是开发一个基于被动或者主动识别漏洞路径关键词插件,而该插件可以在我开着burp流量网站的时候,替我们主动或被动识别出可能存在相关漏洞的URL路径。

0x01 类似轮子探索

我个人的性格是不喜欢从0开始开发一个完整的工具,除非真的找不见相关类似的轮子,工具的本身是拿来用的,时间成本最重要。故我们就在Burp Request Timer这个插件的基础上进行修改。

根据上篇的源码分析,我们知道该插件代码的核心部分都在 BurpExtender 这个类的processHttpMessage函数和addLog函数中,那就再重点细分下这部分的代码部分。

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
/**
* Process the HTTP message
*
* @param toolFlag originating tool
* @param messageIsRequest true if request, false if response
* @param messageInfo <code>IHttpRequestResponse</code> object
*/
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {

if (isRunning) {
if (toolFilter == 0 || toolFilter == toolFlag) {
URL url = helpers.analyzeRequest(messageInfo).getUrl(); // 分析HTTP请求,获得URL
if (messageIsRequest) { // 判断当前是否调用或请求该方法
reqResMap.put(url, System.currentTimeMillis()); // 保存当前url和对应的发现时间
}
else {
if (reqResMap.containsKey(url)) { // 判断当前是否已储存该URL对应关系
long time = System.currentTimeMillis() - reqResMap.get(url); //计算获取响应的时间
reqResMap.remove(url); // 删除原指定关系
synchronized (panel.getLogTableModel().getLogArray()) { // 获取当前所有存放的URL信息数据;加上线程锁
int row = panel.getLogTableModel().getLogArray().size(); // 获取字典信息总的个数
if (panel.getURLFilterText().isEmpty() && !panel.isScopeSelected()) {
addLog(messageInfo, toolFlag, time, row); // 如果相关信息没有被记录,则进行记录信息
}
// 判断是否开启日志筛选器
else if (!panel.isScopeSelected() && !panel.getURLFilterText().isEmpty() &&
helpers.analyzeRequest(messageInfo).getUrl().toExternalForm().contains(panel.getURLFilterText())) {
addLog(messageInfo, toolFlag, time, row);
}
// Log in-scope requests
else if (panel.isScopeSelected() && panel.getURLFilterText().isEmpty() &&
callbacks.isInScope(helpers.analyzeRequest(messageInfo).getUrl())) {
addLog(messageInfo, toolFlag, time, row);
}
// Log in-scope requests and filter
else if (panel.isScopeSelected() && !panel.getURLFilterText().isEmpty() &&
callbacks.isInScope(helpers.analyzeRequest(messageInfo).getUrl()) &&
helpers.analyzeRequest(messageInfo).getUrl().toExternalForm().contains(panel.getURLFilterText())) {
addLog(messageInfo, toolFlag, time, row);
}
}
}
}
}
}
}

/**
* Helper to add a log entry
*
* @param messageInfo <code>IHttpRequestResponse</code> object
* @param toolFlag tool
* @param time time taken in ms
* @param row row to insert at
*/
private void addLog(IHttpRequestResponse messageInfo, int toolFlag, long time, int row) {

panel.getLogTableModel().getLogArray().add(new Log(LocalDateTime.now(),
callbacks.getToolName(toolFlag),
callbacks.saveBuffersToTempFiles(messageInfo),
helpers.analyzeRequest(messageInfo).getUrl(),
helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode(),
helpers.analyzeResponse(messageInfo.getResponse()).getStatedMimeType(),
time));
panel.getLogTableModel().fireTableRowsInserted(row, row);
}

整理代码功能就是把所有监听到的URL请求进行计算响应时间,但是具体的代码流程,还是需要我们进行Debug一下才能明白,要不然很容易改动的时候出错。

当我设置好远程调试环境后 {

burp使用以下代码启动方式:

1
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888 -jar burpsuite_free_v1.7.26.jar

然后在Eclipse中设置好debug模式:

最后,启动插件的开始按钮和浏览器的代理设置,我们进行调试分析。

}

0x02 调试分析插件流程

1、当我们启动了插件中start按钮后,isRuning变量的值将会变为True,其次toolFilter变量的值在类函数默认初始化的时候就是0,获取当前url地址。

2、若当前正在请求该URL地址,则填到到初始化时生成的HashMap字典中,字典格式内容如下:{http:/sh1yan.top:1590199786945},键是URL地址,值是请求的时间。

3、若当前无新增请求包,而出现相对应的URL返回包时,则开始处理显示URL的请求时间到burp的UI界面上。首先判断是否接受到返回包响应,其次判断该响应的URL是否在HashMap中储存。

4、若当前的响应包是在HashMap中,则响应时间减去请求时间为该URL的整体等待时间。然后删除字典中的该URL地址,加上线程锁后,开始对该条数据进行储存显示到插件的UI界面上。

5、下面这个函数就是具体保存的相对应数据的函数。

整体主要的判断核心代码流程就是如此,其它一些细节部分的代码请参考上一篇的源码分析即可。

0x03 功能需求构思与轮子改造方案

我们先看一下轮子插件的UI界面:

工具功能需求:

  1. 匹配漏洞Path中漏洞关键词,若存在该关键词则在UI中显示
  2. 关键词路径和漏洞名称可灵活配置化
  3. 匹配出的疑似漏洞路径只显示一次,不重复显示
  4. UI字段{Timestamp、Tool、Vulnerability name、Vulnerability characteristics、Vulnerability URL、HTTP Status}

0x04 具体改造过程①

  • 首先需要实现一个核心函数,即通过获取到的URL,匹配其中的URL路径部分是否存在漏洞关键词,若存在则返回漏洞关键词,则通过get的方式获取对应的需要的值。
  • 漏洞配置文件部分,匹配jar同目录下的配置文件。
  • 而漏洞的Url应该只出现一次。

1、为了实现灵活配置,我这里采用json的格式,用于存放漏洞特征和漏洞名称,这里只是简单的列举一些dome,根据漏洞特征分为URL路径部分和COOKIE中等数据包的可能存在关键字的地方。

2、这里我们使用以下代码来获取对应的漏洞值,并存储到内存的字典中。

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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.ArrayList;

import com.alibaba.fastjson.*;

class KeywordsOfVulnerability{

private HashMap<String,String> POC_URL_MAP = new HashMap<String, String>();
private HashMap<String, String> POC_COOKIE_MAP = new HashMap<String, String>();

private static String readJsonFile() {
String jsonStr = "";
try {
File directory = new File("");
File filePath = directory.getAbsoluteFile();
String filePath1 = filePath + "/KeywordsOfVulnerability.json";
FileReader fileReader = new FileReader(filePath1);
Reader reader = new InputStreamReader(new FileInputStream(filePath1),"utf-8");
int ch = 0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

private void jsonSort() {
JSONObject jobj = JSON.parseObject(readJsonFile());

for(String key:jobj.keySet()) {
if("POC_URL".equals(key)) {
JSONArray student =jobj.getJSONArray(key);
for(int i=0;i<student.size();i++) {
JSONObject Br1 = student.getJSONObject(i);
for(String emty:Br1.keySet()) {
POC_URL_MAP.put(emty,Br1.getString(emty));
}
}
}else if("POC_COOKIE".equals(key)) {
JSONArray student =jobj.getJSONArray(key);
for(int i=0;i<student.size();i++) {
JSONObject Br1 = student.getJSONObject(i);
for(String emty:Br1.keySet()) {
POC_COOKIE_MAP.put(emty,Br1.getString(emty));
}
}
}

}
}

public HashMap<String, String> getPocUrlMap() {
this.jsonSort();
return POC_URL_MAP;
}

public HashMap<String, String> getPocCookieMap() {
this.jsonSort();
return POC_COOKIE_MAP;
}
}

3、既然获取可以获取到对应的漏洞特征后,我们就可以执行遍历的方式,来匹配对应的值是否可以漏洞特征,若匹配到漏洞特征后,则返回漏洞特征和漏洞名称,若没有匹配到则返回一个空的ArrayList。

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
public class VulnerabilityMatching {

public ArrayList vulnerabilityMatchingUrl(String url) {
KeywordsOfVulnerability featuresdata = new KeywordsOfVulnerability();
ArrayList callbackdata = new ArrayList();
for(String key : featuresdata.getPocUrlMap().keySet()) {
String keyre = ".*"+key+".*";
if(url.matches(keyre)) {
String zhi = featuresdata.getPocUrlMap().get(key);
callbackdata.add(key);
callbackdata.add(zhi);
}
}
return callbackdata;
}

public ArrayList vulnerabilityMatchingCookie(String cookie) {
KeywordsOfVulnerability featuresdata = new KeywordsOfVulnerability();
ArrayList callbackdata = new ArrayList();
for(String key : featuresdata.getPocCookieMap().keySet()) {
String keyre = ".*"+key+".*";
if(cookie.matches(keyre)) {
String zhi = featuresdata.getPocCookieMap().get(key);
callbackdata.add(key);
callbackdata.add(zhi);
}
}
return callbackdata;
}

}

3、为了保证我们传入的漏洞Url在插件的UI界面上显示时是唯一的,我们需要把经过我们匹配后,确认是问题的Url存储到一个ArrayList中,然后在存储之前判断下,是否该内容部分已经存在了,若存在了则不再重复在UI上进行显示。

1
2
3
4
5
6
7
8
9
10
public boolean isVulnerabilityFeatures(String Vurl) {
// 判断键是否在字典中,若在则返回true。
// 这里有一个可能存在的小bug,若url路径中同时存在多个特征,则只会显示最先发现的漏洞特征。
boolean isresult = vulnerabilityFeaturesDict.containsKey(Vurl);
return isresult;
}

public void setVulnerabilityFeatures(String Vurl,String Vname) {
vulnerabilityFeaturesDict.put(Vurl, Vname);
}

0x05 具体改造过程②

  • 需要把对监听到的流量包信息进行匹配,若匹配成功则让其在UI界面上显示。
  • 设置最新的UI字段{Timestamp、Tool、Vulnerability name、Vulnerability characteristics、Vulnerability URL、HTTP Status}

1、在整体显示字段上,我们其实只需要修改UI代码中显示字段,其次对我们传入的参数位置和具体显示的大小进行一系列调整既可以了。

需要修改的Java文件:

LogTableModel.java

LogTale.java

Log.java

2、以上系列部分就不再展示,具体看最终的成效代码即可。

3、其次,我们根据监听到的流量包,进行监听匹配,若匹配出现存在的漏洞情况,则进行显示在UI界面上。

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

package burp;

import java.io.PrintWriter;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;


@SuppressWarnings("unused")
public class BurpExtender implements IBurpExtender, IHttpListener {

static IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private MainPanel panel;
private HashMap<URL, Long> reqResMap = new HashMap<>();
private boolean isRunning = false;
private int toolFilter = 0;
VulnerabilityMatching vdata = new VulnerabilityMatching();

public static void main(String args[]) {

}


@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) {
BurpExtender.callbacks = callbacks;
helpers = callbacks.getHelpers();
callbacks.setExtensionName("Vulnerability Matching");
panel = new MainPanel(this);
callbacks.addSuiteTab(panel);
callbacks.registerHttpListener(this);
}

void setRunning(boolean running) {

this.isRunning = running;
}

void setToolFilter(int toolFilter) {

this.toolFilter = toolFilter;
}

@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
isVulnerabilityJudge(toolFlag, messageIsRequest, messageInfo);
}

public void isVulnerabilityJudge(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
if(isRunning) {
if (toolFilter == 0 || toolFilter == toolFlag) {
if (messageIsRequest) {

}else {

URL url = helpers.analyzeRequest(messageInfo).getUrl();
System.out.println(url.getHost()+url.getPath());
ArrayList<?> url111 = vdata.vulnerabilityMatchingUrl(url.getHost()+url.getPath());

if(!url111.isEmpty()) {
String vname = (String) url111.get(0);
String vcname = (String) url111.get(1);

synchronized (panel.getLogTableModel().getLogArray()) {
int row = panel.getLogTableModel().getLogArray().size();
if (panel.getURLFilterText().isEmpty() && !panel.isScopeSelected()) {
addLog(messageInfo, toolFlag,vname ,vcname, row);
}
// Log filter URL requests
else if (!panel.isScopeSelected() && !panel.getURLFilterText().isEmpty() &&
helpers.analyzeRequest(messageInfo).getUrl().toExternalForm().contains(panel.getURLFilterText())) {
addLog(messageInfo, toolFlag, vname ,vcname, row);
}
// Log in-scope requests
else if (panel.isScopeSelected() && panel.getURLFilterText().isEmpty() &&
callbacks.isInScope(helpers.analyzeRequest(messageInfo).getUrl())) {
addLog(messageInfo, toolFlag, vname ,vcname, row);
}
// Log in-scope requests and filter
else if (panel.isScopeSelected() && !panel.getURLFilterText().isEmpty() &&
callbacks.isInScope(helpers.analyzeRequest(messageInfo).getUrl()) &&
helpers.analyzeRequest(messageInfo).getUrl().toExternalForm().contains(panel.getURLFilterText())) {
addLog(messageInfo, toolFlag, vname ,vcname, row);
}
}
}
}

}
}

}



private void addLog(IHttpRequestResponse messageInfo, int toolFlag, String vname,String vcname, int row) {


panel.getLogTableModel().getLogArray().add(new Log(LocalDateTime.now(),
callbacks.getToolName(toolFlag),
callbacks.saveBuffersToTempFiles(messageInfo),
helpers.analyzeRequest(messageInfo).getUrl(),
helpers.analyzeResponse(messageInfo.getResponse()).getStatusCode(),
vcname,
vname));
panel.getLogTableModel().fireTableRowsInserted(row, row);
}

HashMap<URL, Long> getReqResMap() {

return reqResMap;
}
}

0x06 功能效果展示

因时间问题,我们就先实现以上部分的预定功能,落下的功能和代码优化部分等后期慢慢完善。

在打包时有1个注意事项,就是因为我们调用了三方的jar库包,如果不是以可执行jar形式的打包的话,在导入插件后,burp会提示找不见json库,所以我们在BurpExtender中写了一个空的main函数。

其次,我们的规则库json文件,也必须放在 burp 工具的同目录下。

dome代码和演示jar文件:

http://sh1yan.top\photo\Burpseuite-plug-in-writing-intermediate\TestCode.rar


BurpSuite插件编写[中级篇]
https://sh1yan.top/2020/05/30/Burpseuite-plug-in-writing-intermediate/
作者
shiyan
发布于
2020年5月30日
许可协议