0x00 前言

最近没搞白嫖版而是用的社区版的Burp,很多功能用不上,而且之前的Burp插件都是基于Python来写的,现在就换Java来写下插件吧,就拿CSRF PoC生成器来练练手。

项目地址:BurpExtender-CSRFPoCGenerator

0x01 Burp插件开发基础

主要参考Her0in大佬系列文章:

BurpSuite插件开发指南之 API 上篇 – Her0in

BurpSuite插件开发指南之 API 下篇 – Her0in

BurpSuite插件开发指南之 Java 篇 – Her0in

BurpSuite插件开发指南之 Python 篇 – Her0in

当然,官方也给出了一些插件实例供参考学习:https://portswigger.net/burp/extender/

将Burp提供的扩展接口和使用方式大致了解之后就能入手了。

0x02 插件开发——CSRF PoC生成器

Burp的Extender标签下,有个APIs子标签,其中展示了有哪些扩展接口及其功能。这里点击Save Interface files下载下来:

下载下来是个burp文件夹,其中都是Burp插件相关接口的java文件。

使用IDEA打开,将burp包放入src目录中,在burp包下新建名为“BurpExtender”的类:

直接看代码吧。

这里BurpExtender类实现了IBurpExtender接口和IContextMenuFactory接口,其中定义了三个BurpExtender类成员变量,并在registerExtenderCallbacks()函数中完成初始化,然后设置插件名和注册上下文菜单工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BurpExtender implements IBurpExtender, IContextMenuFactory {

public IBurpExtenderCallbacks callbacks;
public IExtensionHelpers helpers;
public PrintWriter stdout;

@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
this.callbacks = callbacks;
this.helpers = callbacks.getHelpers();
this.stdout = new PrintWriter(callbacks.getStdout(), true);

// 设置插件名
callbacks.setExtensionName("Mi1k7ea");
callbacks.registerContextMenuFactory(this);
}

Burp所有插件必须实现IBurpExtender接口且命名必须为“BurpExtender”,且必须实现其registerExtenderCallbacks()函数。当插件被加载时,Burp会调用registerExtenderCallbacks()函数并传递一个IBurpExtenderCallbacks类对象,插件可以通过这个对象来调用很多扩展Burp功能必需的方法,比如这里的getHelpers()、getStdout()、setExtensionName()、registerContextMenuFactory()等方法。

  • getHelpers()函数:用于获取IExtensionHelpers接口类对象,在后续用到了该类的analyzeRequest()函数来解析请求报文;
  • getStdout()函数:用于获取标准输出流,在Extender->Extensions->Output窗口中输出内容;
  • setExtensionName()函数:用于设置插件名;
  • registerContextMenuFactory()函数:用于注册自定义上下文菜单项的工厂。当用户在Burp中的任何地方调用一个上下文菜单时就会触发这个工厂函数,根据菜单调用的细节,提供应该被显示在上下文菜单中的任何自定义上下文菜单项;

接着,是实现IContextMenuFactory接口类的createMenuItems()函数,先看外围部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
// 上下文菜单
List<JMenuItem> jMenuItemList = new ArrayList<>();
JMenu jMenu = new JMenu("Mi1k7ea's Extender");
JMenuItem jMenuItem = new JMenuItem("Generate CSRF PoC");
jMenu.add(jMenuItem);
jMenuItemList.add(jMenu);

// 监听上下文菜单点击事件
jMenuItem.addActionListener(e -> {
...
});

return jMenuItemList;
}

这里其实涉及到的就是Java Swing编程,具体看网上资料即可。主要是创建自己的上下文菜单项然后设置监听事件,当点击时触发addActionListener()函数里面的代码逻辑。

其中监听触发的代码,直接看注释就行:

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
// 生成PoC的GUI框
JFrame frame = new JFrame("CSRF PoC");
JPanel codePanel = new JPanel(new GridLayout());

// getSelectedMessages()函数用于获取当前显示的或用户选中的HTTP请求/响应的细节
// analyzeRequest()函数用于分析HTTP请求信息以便获取到多个键的值
IHttpRequestResponse iHttpRequestResponse = invocation.getSelectedMessages()[0];
IRequestInfo iRequestInfo = this.helpers.analyzeRequest(iHttpRequestResponse);

URL url = iRequestInfo.getUrl();
String method = iRequestInfo.getMethod();
String http_method = "";
String request_url = "";
String params = "";

try {
// 获取request参数并生成对应input标签
List<IParameter> iParameters = iRequestInfo.getParameters();
for (IParameter iParameter : iParameters) {
if (method.equals("POST")) {
request_url = url.toString();
http_method = " method=\"POST\"";
if (iParameter.getType() == IParameter.PARAM_BODY) {
params += " <input type=\"hidden\" name=\"" + EncodeSpecialChars(iParameter.getName()) + "\" value=\"" + EncodeSpecialChars(iParameter.getValue()) + "\" />\n";
}
} else if (method.equals("GET")) {
request_url = url.toString().split("\\?")[0];
http_method = " method=\"GET\"";
if (iParameter.getType() == IParameter.PARAM_URL) {
params += " <input type=\"hidden\" name=\"" + EncodeSpecialChars(iParameter.getName()) + "\" value=\"" + EncodeSpecialChars(iParameter.getValue()) + "\" />\n";
}
}
}
} catch (Exception exception) {
exception.printStackTrace();
}

// 组合成CSRF自动提交PoC
final String PoC = "<html>\n" +
" <body>\n" +
" <form action=\"" + request_url + "\"" + http_method + ">\n" +
params +
" <input type=\"submit\" value=\"Submit request\" />\n" +
" </form>\n" +
" </body>\n" +
" <script>\n" +
" var m = document.getElementsByTagName('form')[0];\n" +
" m.submit();\n" +
" </script>\n" +
"</html>";;

// 将PoC设置到新建的GUI框中
JTextArea jt = new JTextArea(PoC);
JScrollPane scrollPane = new JScrollPane(jt);
jt.setEditable(false);

frame.add(codePanel, BorderLayout.CENTER);
codePanel.add(scrollPane);

// 新建Buttom用于Copy PoC
JPanel buttonPanel = new JPanel(new FlowLayout());
JButton button = new JButton("Copy");
buttonPanel.add(button);
frame.add(buttonPanel, BorderLayout.PAGE_END);
button.addActionListener(e1 -> {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Clipboard clipboard = toolkit.getSystemClipboard();
StringSelection CSRFCodeToCopy = new StringSelection(PoC);
clipboard.setContents(CSRFCodeToCopy, CSRFCodeToCopy);
});

// 设置GUI框样式
frame.setSize(600,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

其中EncodeSpecialChars()函数用于处理一些PoC的input标签中特殊字符和解码问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 进行URL解码和对input标签内的属性值特殊字符进行HTML编码,可自行补充
public static String EncodeSpecialChars(String content) throws Exception {
content = URLDecoder.decode(content, "UTF-8");
String special_word = "<>\"'";
for (int i = 0; i < special_word.length(); i++) {
char word = special_word.charAt(i);
String w = Character.toString(word);
if (content.contains(w)) {
String ascii_word = Integer.toString(word);
String html_word = "&#" + ascii_word + ";";
System.out.println(html_word);
content = content.replace(w, html_word);
}
}
return content;
}

OK,最后来看下效果吧。

将项目打包成jar包后,然后加载进来:

对任意报文,右键就能看到插件,点击就能加载生成CSRF PoC:

OK,更多的插件后面再写。