Burp suite支持三种语言的插件开发:
1、Java
2、Python
3、Ruby
综合个人的实际情况,选择了Java进行了开发(真实原因:Ruby没学、python插件调试不方便)。用Java开发的优势,这里不过多描述,毕竟Burpsuite都是Java写的,最起码Java开发插件的兼容性会好很多。
自己百度
IDEA社区版
社区版对一般开发来说足够使用,还免费。为啥不用Eclipse,这个问题回答起来也很简答,因为界面丑。
Maven作为一个构建工具,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的、流行的构建工具。
Maven不仅是构建工具,还是一个依赖管理工具和项目管理工具,它提供了中央仓库,能帮我自动下载构件。
个人学习安装版本如下:
具体安装流程,小伙子自行百度一下。
出现如下界面说明你配好了
一般项目配置过程如下:
上图红框内容,需要带入Burp的启动命令内。本人Burp启动bat命令如下:
java --illegal-access=permit -Dfile.encoding=utf-8 -javaagent:BurpSuiteLoader_v2021.8.jar -noverify -`jar burpsuite_pro_v2021.8.jar
开启插件调试命令如下:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 --illegal-access=permit -Dfile.encoding=utf-8 -javaagent:BurpSuiteLoader_v2021.8.jar -noverify -jar burpsuite_pro_v2021.8.jar
上图所示即为开启了Burp的调试。使用Burp安装IDEA导出的插件jar包,在IDEA开始Debug。
下好断点,开启Burp代理进行数据包拦截。IDEA调式模式效果如下:
注:因为国内使用Burp破解版情况比较多,尝试调试可使用Burp社区版。
有时,想单独查看插件处理过的请求。可在Burp>Logger下进行查看。相关设置可结合实际自行选择。
官方各种示例代码:https://portswigger.net/burp/extender
官方API文档:https://portswigger.net/burp/extender/api/
官方针对Burp插件的开发有提供三种语言的编写示例,并提供了Python、Ruby相关注意事项。简单数据包处理,官方示例缝合一下基本够用。推荐过一边官方提供的内容。
下图为Burp完整的一次请求,其包含的信息相信大家都有一定的了解。原则上,当前页面的信息我们都是可以获取到的。
针对相关信息的操作,可概况为“增删改查”:
请求包:
响应包:
httpService:
相关代码示例:
package burp;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
public class BurpExtender implements IBurpExtender, IHttpListener
{//所有burp插件都必须实现IBurpExtender接口,而且实现的类必须叫做BurpExtender
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private PrintWriter stdout;
private PrintWriter stderr;
private String ExtenderName = "Dome";
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
{//IBurpExtender必须实现的方法
stdout = new PrintWriter(callbacks.getStdout(), true);
stderr = new PrintWriter(callbacks.getStderr(), true);
callbacks.printOutput(ExtenderName);
this.callbacks = callbacks;
helpers = callbacks.getHelpers();
callbacks.setExtensionName(ExtenderName);
callbacks.registerHttpListener(this);
//如果没有注册,下面的processHttpMessage方法是不会生效的。处理请求和响应包的插件,这个应该是必要的
}
@Override
public void processHttpMessage(int toolFlag,boolean messageIsRequest,IHttpRequestResponse messageInfo)
{
if (toolFlag == IBurpExtenderCallbacks.TOOL_PROXY){
//不同的toolFlag代表了不同的burp组件
//https://portswigger.net/burp/extender/api/constant-values.html#burp.IBurpExtenderCallbacks
if (messageIsRequest){ //对请求包进行处理
IRequestInfo analyzeRequest = helpers.analyzeRequest(messageInfo);
//对消息体进行解析,messageInfo是整个HTTP请求和响应消息体的总和,各种HTTP相关信息的获取都来自于它,HTTP流量的修改都是围绕它进行的。
/*****************获取参数**********************/
List<IParameter> paraList = analyzeRequest.getParameters();
//获取参数的方法
//当body是json格式的时候,这个方法也可以正常获取到键值对;但是PARAM_JSON等格式不能通过updateParameter方法来更新。
//如果在url中的参数的值是 key=json格式的字符串 这种形式的时候,getParameters应该是无法获取到最底层的键值对的。
for (IParameter para : paraList){// 循环获取参数,判断类型,进行加密处理后,再构造新的参数,合并到新的请求包中。
String key = para.getName(); //获取参数的名称
String value = para.getValue(); //获取参数的值
int type = para.getType();
stdout.println("参数 key value type: "+key+" "+value+" "+type);
}
/*****************修改并更新参数**********************/
IParameter newPara = helpers.buildParameter("testKey", "testValue", IParameter.PARAM_BODY); //构造新的参数
byte[] new_Request = messageInfo.getRequest();
new_Request = helpers.updateParameter(new_Request, newPara); //构造新的请求包
messageInfo.setRequest(new_Request);//设置最终新的请求包
/*****************删除参数**********************/
for (IParameter para : paraList){// 循环获取参数,判断类型,进行加密处理后,再构造新的参数,合并到新的请求包中。
String key = para.getName(); //获取参数的名称
if (key.equals("aaa")) {
new_Request = helpers.removeParameter(new_Request, para); //构造新的请求包
}
}
/*****************获取header**********************/
List<String> headers = analyzeRequest.getHeaders();
for (String header : headers){// 循环获取参数,判断类型,进行加密处理后,再构造新的参数,合并到新的请求包中。
stdout.println("header "+header);
if (header.startsWith("referer")) {
/*****************删除header**********************/
headers.remove(header);
}
}
/*****************新增header**********************/
headers.add("myheader:aaaa");
/*****************获取body 方法一**********************/
int bodyOffset = analyzeRequest.getBodyOffset();
byte[] byte_Request = messageInfo.getRequest();
String request = new String(byte_Request); //byte[] to String
String body = request.substring(bodyOffset);
byte[] byte_body = body.getBytes(); //String to byte[]
/*****************获取body 方法二**********************/
int len = byte_Request.length;
byte[] byte_body1 = Arrays.copyOfRange(byte_Request, bodyOffset, len);
new_Request = helpers.buildHttpMessage(headers, byte_body);
//如果修改了header或者数修改了body,不能通过updateParameter,使用这个方法。
messageInfo.setRequest(new_Request);//设置最终新的请求包
}
}
else{//处理响应包
IResponseInfo analyzedResponse = helpers.analyzeResponse(messageInfo.getResponse()); //getResponse获得的是字节序列
short statusCode = analyzedResponse.getStatusCode();
List<String> headers = analyzedResponse.getHeaders();
String resp = new String(messageInfo.getResponse());
int bodyOffset = analyzedResponse.getBodyOffset();//响应包是没有参数的概念的,大多需要修改的内容都在body中
String body = resp.substring(bodyOffset);
if (statusCode==200){
String newBody= body+"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&";
byte[] bodybyte = newBody.getBytes();
messageInfo.setResponse(helpers.buildHttpMessage(headers, bodybyte));
}
}
}
}
学习插件编写先输出一下“hello world”,这该死的仪式感。以官网的example-hello-word改编一下,带大家先了解一下Burp的几个输出位置:
package burp;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender
{
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
{
// 设置插件的名称
callbacks.setExtensionName("Hello world");
// 获取burp提供的标准输出流和错误输出流
PrintWriter stdout = new PrintWriter(callbacks.getStdout(), true);
PrintWriter stderr = new PrintWriter(callbacks.getStderr(), true);
// 打印到标准输出流
stdout.println("Hello world 1");
// 答应到错误输出流
stderr.println("Hello world 2");
// 写一个报警信息到burp的报警面板
callbacks.issueAlert("Hello world 3");
// 抛出一个异常,将会在错误输出流中显示
throw new RuntimeException("Hello world 4");
}
}
Burp相关显示位置:
### callbacks作用
通过 callbacks 这个实例对象,传递给插件一系列burp的原生方法。
这个地方贴一下官方的示例,来说明一下:
package burp;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender, IHttpListener,
IProxyListener, IScannerListener, IExtensionStateListener
{
private IBurpExtenderCallbacks callbacks;
private PrintWriter stdout;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
{
this.callbacks = callbacks;
// 设置插件的名称
callbacks.setExtensionName("Event listeners");
// 获取burp提供的标准输出流和错误输出流
stdout = new PrintWriter(callbacks.getStdout(), true);
// 注册HTTP listener
callbacks.registerHttpListener(this);
// 注册Proxy listener
callbacks.registerProxyListener(this);
// 注册Scanner listener
callbacks.registerScannerListener(this);
// 注册extension state listener
callbacks.registerExtensionStateListener(this);
}
// 实现HTTP监听
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
{
stdout.println(
(messageIsRequest ? "HTTP request to " : "HTTP response from ") +
messageInfo.getHttpService() +
" [" + callbacks.getToolName(toolFlag) + "]");
}
// 实现Proxy监听
@Override
public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message)
{
stdout.println(
(messageIsRequest ? "Proxy request to " : "Proxy response from ") +
message.getMessageInfo().getHttpService());
}
// 实现扫描监听
@Override
public void newScanIssue(IScanIssue issue)
{
stdout.println("New scan issue: " + issue.getIssueName());
}
// 实现Extender监听
@Override
public void extensionUnloaded()
{
stdout.println("Extension was unloaded");
}
}
通过上面的几个例子,大家应该对Burp流量监听、数据包处理、数据显示有了一定的了解。
那这部分一起来搞点花活,创建一下简单的鼠标右键事件,示例代码如下:
package burp;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenuItem;
public class BurpExtender implements IBurpExtender, IContextMenuFactory
{//所有burp插件都必须实现IBurpExtender接口,而且实现的类必须叫做BurpExtender
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private PrintWriter stdout;
private PrintWriter stderr;
private String ExtenderName = "Burp鼠标右键事件练习";
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
{//IBurpExtender必须实现的方法
stdout = new PrintWriter(callbacks.getStdout(), true);
stderr = new PrintWriter(callbacks.getStderr(), true);
callbacks.printOutput(ExtenderName);
this.callbacks = callbacks;
helpers = callbacks.getHelpers();
callbacks.setExtensionName(ExtenderName);
callbacks.registerContextMenuFactory(this);
}
@Override
public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
ArrayList<JMenuItem> menu_item_list = new ArrayList<JMenuItem>();
JMenuItem eventname = new JMenuItem("选项一");
eventname.addActionListener(new eventname(invocation));
menu_item_list.add(eventname);
return menu_item_list;
}
public class eventname implements ActionListener{
private IContextMenuInvocation invocation;
public eventname(IContextMenuInvocation invocation) {
this.invocation = invocation;
}
@Override
public void actionPerformed(ActionEvent event) {
stdout.println("右键响应");
}
}
}
Burp相关位置及事件响应结果显示:
我们完成了鼠标右键的事件创建。现在花活继续,来写个界面吧,官方示例代码:
package burp;
import java.awt.Component;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
public class BurpExtender extends AbstractTableModel implements IBurpExtender, ITab, IHttpListener, IMessageEditorController
{
private IBurpExtenderCallbacks callbacks;
private IExtensionHelpers helpers;
private JSplitPane splitPane;
private IMessageEditor requestViewer;
private IMessageEditor responseViewer;
private final List<LogEntry> log = new ArrayList<LogEntry>();
private IHttpRequestResponse currentlyDisplayedItem;
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks)
{
this.callbacks = callbacks;
helpers = callbacks.getHelpers();
callbacks.setExtensionName("Burp Dome");
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
// 主要面版分割
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
Table logTable = new Table(BurpExtender.this);
JScrollPane scrollPane = new JScrollPane(logTable);
splitPane.setLeftComponent(scrollPane);
// 带有请求/响应的标签页
JTabbedPane tabs = new JTabbedPane();
requestViewer = callbacks.createMessageEditor(BurpExtender.this, false);
responseViewer = callbacks.createMessageEditor(BurpExtender.this, false);
tabs.addTab("Request", requestViewer.getComponent());
tabs.addTab("Response", responseViewer.getComponent());
splitPane.setRightComponent(tabs);
// 定制UI组件
callbacks.customizeUiComponent(splitPane);
callbacks.customizeUiComponent(logTable);
callbacks.customizeUiComponent(scrollPane);
callbacks.customizeUiComponent(tabs);
// 在Burp的UI中添加自定义的标签
callbacks.addSuiteTab(BurpExtender.this);
//注册HTTP监听
callbacks.registerHttpListener(BurpExtender.this);
}
});
}
@Override
public String getTabCaption()
{
//返回标签TAG名称
return "Dome Tag";
}
@Override
public Component getUiComponent()
{
return splitPane;
}
//HTTP监听及相关处理
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
{
// only process responses
if (!messageIsRequest)
{
// create a new log entry with the message details
synchronized(log)
{
int row = log.size();
log.add(new LogEntry(toolFlag, callbacks.saveBuffersToTempFiles(messageInfo),
helpers.analyzeRequest(messageInfo).getUrl()));
fireTableRowsInserted(row, row);
}
}
}
@Override
public int getRowCount()
{
return log.size();
}
@Override
public int getColumnCount()
{
return 2;
}
@Override
public String getColumnName(int columnIndex)
{
switch (columnIndex)
{
case 0:
return "Tool";
case 1:
return "URL";
default:
return "";
}
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
return String.class;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
LogEntry logEntry = log.get(rowIndex);
switch (columnIndex)
{
case 0:
return callbacks.getToolName(logEntry.tool);
case 1:
return logEntry.url.toString();
default:
return "";
}
}
@Override
public byte[] getRequest()
{
return currentlyDisplayedItem.getRequest();
}
@Override
public byte[] getResponse()
{
return currentlyDisplayedItem.getResponse();
}
@Override
public IHttpService getHttpService()
{
return currentlyDisplayedItem.getHttpService();
}
private class Table extends JTable
{
public Table(TableModel tableModel)
{
super(tableModel);
}
@Override
public void changeSelection(int row, int col, boolean toggle, boolean extend)
{
// show the log entry for the selected row
LogEntry logEntry = log.get(row);
requestViewer.setMessage(logEntry.requestResponse.getRequest(), true);
responseViewer.setMessage(logEntry.requestResponse.getResponse(), false);
currentlyDisplayedItem = logEntry.requestResponse;
super.changeSelection(row, col, toggle, extend);
}
}
private static class LogEntry
{
final int tool;
final IHttpRequestResponsePersisted requestResponse;
final URL url;
LogEntry(int tool, IHttpRequestResponsePersisted requestResponse, URL url)
{
this.tool = tool;
this.requestResponse = requestResponse;
this.url = url;
}
}
}
Burp相关展示如下:
大多数情况下,依靠官网的示例进行修改可满足需求。如果追求”代码写的好不好,界面一定要漂亮“的原则,那我建议你换一种语言,用Java写界面想想就头冷。
最近正在写一个集成XSStrike的插件,需要将上面Java代码转换为python代码。或者将XSStrike用Java重写,两个都比较费劲。现在正在一种方式中探索,最无法避免的就是两种语言的数据转换问题。