Brup插件開發手記
前言
在一些攻防演練中,像Shiro、Fastjson等常見高危漏洞一直被高頻利用。但在一些情況下,這些漏洞通過幾輪的洗刷下來出現的頻率會逐漸變少。在打點的時候,一些平時並不會去測試的漏洞可能就能在某次中進行利用,並且借助打下一個內網據點。而我本身在真實做法中並不會刻意的去找一些漏洞,如springboot 的一些rce漏洞。遇到的Springboot會比較多,而挨個測時間成本就高了。即便大型掃描器對於這些都能夠實現,但個人並不喜歡上這些大型掃描器。 所以在此萌生了一個念頭,編寫burp的插件掛着后台進行被動掃描。模塊化開發,添加多個漏洞進行被動掃描。寫好以后后面則是賭運氣(陽壽)的時候了。
開發規范
所有的burp插件都必須實現IBurpExtender這個接口
實現類的包名稱必須是burp
實現類的名稱必須是BurpExtender
實現類比較是public的
實現類必須有默認構造函數(public,無參),如果沒有定義構造函數就是默認構造函數
動態調試
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Xbootclasspath/p:burp-loader-keygen-BurpPro.jar -jar burpsuite_pro_v1.7.37.jar
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Xbootclasspath/p:BurpHelper.jar -jar burpsuite_pro_v1.7.37.jar
編寫
package burp;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender{
public String NANE= "T_Scan";
public String VERISON ="V1.0";
public PrintWriter stdout;
public IBurpExtenderCallbacks callbacks;
public Tags tags;
@Override
public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
tags = new Tags(callbacks,NANE);
this.stdout = new PrintWriter(callbacks.getStdout(), true);//獲取當前拓展插件得輸出流
this.stdout.println("===================================");
this.stdout.println(String.format("%s" , new Object[] { NANE }));
this.stdout.println(String.format("%s", new Object[] { VERISON }));
this.stdout.println("nice0e3");
this.stdout.println("nice0e3");
this.stdout.println("nice0e3");
this.stdout.println("nice0e3");
this.stdout.println("nice0e3");
this.stdout.println("nice0e3");
this.stdout.println("===================================");
callbacks.setExtensionName(NANE);//設置當前擴展的顯示名稱,該名稱將顯示在Extender工具的用戶界面中。
}
}
public class Tags implements ITab{
public String tagName;
public IBurpExtenderCallbacks callbacks;
public JPanel jPanel;
public JButton jButton;
public Tags(final IBurpExtenderCallbacks callbacks, String name) {
this.callbacks = callbacks;
this.tagName = name;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jPanel = new JPanel();
JButton jButton = new JButton("xxxx");
// 將按鈕添加到 主面板 jPanelMain 中.
jPanel.add(jButton);
// 設置自定義組件並添加標簽
callbacks.customizeUiComponent(jPanel);//根據Burp的UI樣式自定義UI組件,包括字體大小、顏色、表格行距等。
callbacks.addSuiteTab(Tags.this);//此方法用於向Burp套件主窗口添加自定義選項卡。
}
});
}
//burp使用該方法獲取選項卡名字
public String getTabCaption() {
return "T_scan";
}
//Burp 使用此方法獲取顯示時應用作自定義選項卡內容的組件。
public Component getUiComponent() {
return jPanel;
}
};
代碼其實很簡單,在BurpExtender
的registerExtenderCallbacks
方法對Tags進行調用即可。
接下來就是對GUI和Payload的發送,結果的輸出。三大部分內容。
消息編輯器
package burp;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.annotation.Target;
public class Tags implements ITab,IMessageEditorController{
public String tagName;
public IBurpExtenderCallbacks callbacks;
public JPanel jPanel;
JTabbedPane jTabbedPane;
IMessageEditor request;
IMessageEditor response;
JTabbedPane tabs;
public JTabbedPane jTabbedPane1;
public JSplitPane splitPanel;
public Tags(final IBurpExtenderCallbacks callbacks, String name) {
this.callbacks = callbacks;
this.tagName = name;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jPanel = new JPanel();
splitPanel = new JSplitPane(0);
splitPanel.setDividerLocation(0.5D);
jTabbedPane = new JTabbedPane();
jTabbedPane1 = new JTabbedPane();
JButton giao = new JButton("giao");
jTabbedPane1.add(giao);
request = callbacks.createMessageEditor(Tags.this, false);
response = callbacks.createMessageEditor(Tags.this,false);
jTabbedPane.addTab("Request",request.getComponent());
jTabbedPane.addTab("Response",response.getComponent());
splitPanel.add(jTabbedPane);
splitPanel.setTopComponent(jTabbedPane1);
splitPanel.setBottomComponent(jTabbedPane);
jPanel.add(splitPanel);
// // 設置自定義組件並添加標簽
callbacks.customizeUiComponent(jPanel);//根據Burp的UI樣式自定義UI組件,包括字體大小、顏色、表格行距等。
callbacks.addSuiteTab(Tags.this);
}
});
}
//burp使用該方法獲取選項卡名字
public String getTabCaption() {
return "T_scan";
}
//Burp 使用此方法獲取顯示時應用作自定義選項卡內容的組件。
public Component getUiComponent() {
return jPanel;
}
@Override
public IHttpService getHttpService() {
return null;
}
@Override
public byte[] getRequest() {
return new byte[0];
}
@Override
public byte[] getResponse() {
return new byte[0];
}
};
Tags.this.mjSplitPane = new JSplitPane(0);
Tags.this.Utable = new Tags.URLTable(Tags.this);
// JTable jTable = new JTable();
Tags.this.UscrollPane = new JScrollPane(Tags.this.Utable);
// Tags.this.UscrollPane = new JScrollPane(jTable);
Tags.this.HjSplitPane = new JSplitPane();
Tags.this.HjSplitPane.setDividerLocation(0.5D);
Tags.this.Ltable = new JTabbedPane();
Tags.this.HRequestTextEditor = Tags.this.callbacks.createMessageEditor(Tags.this, false);
Tags.this.Ltable.addTab("Request", Tags.this.HRequestTextEditor.getComponent());
Tags.this.Rtable = new JTabbedPane();
Tags.this.HResponseTextEditor = Tags.this.callbacks.createMessageEditor(Tags.this, false);
Tags.this.Rtable.addTab("Response", Tags.this.HResponseTextEditor.getComponent());
Tags.this.HjSplitPane.add(Tags.this.Ltable, "left");
Tags.this.HjSplitPane.add(Tags.this.Rtable, "right");
Tags.this.mjSplitPane.add(Tags.this.UscrollPane, "left");
Tags.this.mjSplitPane.add(Tags.this.HjSplitPane, "right");
Tags.this.callbacks.customizeUiComponent(Tags.this.mjSplitPane);
Tags.this.callbacks.addSuiteTab(Tags.this);
插件入口和幫助接口類:
IBurpExtender、IBurpExtenderCallbacks、 IExtensionHelpers、IExtensionStateListener
HTTP處理接口類:
ICookie
、IHttpRequestResponsePersisted
、IHttpRequestResponseWithMarkers
、IHttpService
、 IRequestInfo
、IParameter
、IResponseInfo
Burp工具組件接口類
IIntruderPayloadGenerator
、IIntruderPayloadGeneratorFactory
、 IIntruderPayloadProcessor
、IProxyListener
、IScanIssue
、IScannerCheck
、 IScannerInsertionPoint
、IScannerInsertionPointProvider
、IScannerListener
、IScanQueueItem
、IScopeChangeListener
寫插件常用的接口
IBurpExtender
registerExtenderCallbacks
是IBurpExtender接口的實現類,與Burp的其他組件(Scanner Intruder Spider)及通信對象連接(HttpRequestResponse HttpService SessionHandlingAction)之間的連接。所有拓展插件必須實現這個接口。
IHttpRequestResponse
此接口用於檢索和更新有關HTTP消息的詳細信息。注意:setter方法通常只能在處理消息之前使用,而不能在只讀上下文中使用。與響應細節相關的getter方法只能在發出請求之后使用
方法
java.lang.String getComment()
This method is used to retrieve the user-annotated comment for this item, if applicable.
java.lang.String getHighlight()
This method is used to retrieve the user-annotated highlight for this item, if applicable.
IHttpService getHttpService()
This method is used to retrieve the HTTP service for this request / response.
byte[] getRequest()
This method is used to retrieve the request message.
byte[] getResponse()
This method is used to retrieve the response message.
void setComment(java.lang.String comment)
This method is used to update the user-annotated comment for this item.
void setHighlight(java.lang.String color)
This method is used to update the user-annotated highlight for this item.
void setHttpService(IHttpService httpService)
This method is used to update the HTTP service for this request / response.
void setRequest(byte[] message)
This method is used to update the request message.
void setResponse(byte[] message)
This method is used to update the response message.
IHttpListener
可通過調用IBurpExtenderCallbacks.registerHttpListener()
注冊一個HTTP監聽器,Burp的任何一個接口發起HTTP請求或者收到HTTP響應都會通知此監聽器。該接口可得到這些交互數據,進行分析和修改。
方法如下:
void processHttpMessage(int toolFlag,boolean messageIsRequest,IHttpRequestResponse messageInfo);
發起HTTP請求或者收到HTTP響應都會通知此監聽器
參數:
//toolFlag:指示了發起請求或收到響應的 Burp 工具的 ID,所有的 toolFlag 定義在 IBurpExtenderCallbacks 接口中。
//messageIsRequest:指示該消息是請求消息(值為True)還是響應消息(值為False)
//messageInfo:被處理的消息的詳細信息,是一個 IHttpRequestResponse 對象
IContextMenuFactory
用來實現菜單效果
public class BurpExtender implements IBurpExtender, IContextMenuFactory{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
//插件名稱
callbacks.setExtensionName("T_SCAN");
//注冊菜單
callbacks.registerContextMenuFactory(this);
}
@Override
public List<JMenuItem> createMenuItems(final IContextMenuInvocation invocation) {
List<JMenuItem> listMenuItems = new ArrayList<JMenuItem>();
//子菜單
JMenuItem menuItem;
menuItem = new JMenuItem("子菜單");
//父級菜單
JMenu jMenu = new JMenu("父菜單");
//菜單操作
jMenu.add(menuItem);
listMenuItems.add(jMenu);
return listMenuItems;
}
ICookie
方法:
// 此方法用於獲取 Cookie 的域
java.lang.String getDomain()
// 此方法用於獲取 Cookie 的過期時間
java.util.Date getExpiration()
// 此方法用於獲取 Cookie 的名稱
java.lang.String getName()
// 此方法用於獲取 Cookie 的路徑
java.lang.String getPath()
// 此方法用於獲取 Cookie 的值
java.lang.String getValue()
IExtensionHelpers
此接口提供很多常用的輔助方法,可通過調用IBurpExtenderCallbacks.getHelpers
獲得此接口的實例。
方法:
byte[] addParameter(byte[] request, IParameter parameter) //添加參數到指定的請求中,並更新Content-Length
IRequestInfo analyzeRequest(byte[] request) //分析request的請求信息
IResponseInfo analyzeResponse(byte[] response) //分析response的響應消息
byte[] buildHttpMessage(java.util.List<java.lang.String> headers, byte[] body) //構建請求包,返回響應報
byte[] buildHttpRequest(java.net.URL url) //向指定的url發起get請求
java.lang.String bytesToString(byte[] data) //bytes到String的轉換
java.lang.String bytesToString(byte[] data) //String到bytes的轉換
IHttpRequestResponse
java.lang.String getComment() //獲取用戶的標注信息
java.lang.String getHighlight() //獲取用戶標注的高亮信息
IHttpService getHttpService() //獲取請求響應的http服務信息
byte[] getRequest() // 獲取 HTTP 請求信息
byte[] getResponse() // 獲取 HTTP 響應信息
void setHttpService(IHttpService httpService) //更新請求/響應HTTP服務信息
void setRequest(byte[] message) // 更新 HTTP 請求信息
void setResponse(byte[] message) // 更新 HTTP 響應信息
IHttpService
此接口用於提供關於 HTTP 服務信息的細節
方法:
java.lang.String getHost()
int getPort()
java.lang.String getProtocol()
IInterceptedProxyMessage
該接口不能被擴展實現,它表示已被Burp代理攔截的HTTP消息。我們可以利用接口注冊一個IProxyListener以得到代理消息的細節。
callbacks.registerProxyListener(this); //注冊代理監聽器
IHttpRequestResponse message1 = message.getMessageInfo(); //請求的詳細信息
int action = message.getInterceptAction(); //當前的攔截操作類型
//獲取客戶端的Ip,即代理Ip
stdout.println(message.getClientIpAddress());
//獲取當前的攔截操作類型
stdout.println(action);
//獲取請求的詳細信息
stdout.println(message1);
// Drop 掉所有請求
//message.setInterceptAction(IInterceptedProxyMessage.ACTION_DROP);
Itab
此接口用於使用IBurpExtenderCallbacks.addSuiteTab()
等方法向Burp提供將添加到Burp UI的自定義選項卡的詳細信息。
IScannerCheck
擴展可以實現此接口,然后調用IBurpExtenderCallbacks.registerScannerCheck()
來注冊自定義掃描程序檢查。執行掃描時,Burp將要求支票對基本請求執行主動或被動掃描,並報告發現的任何掃描儀問題。
方法:
int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue)
當自定義掃描程序檢查報告了同一URL路徑的多個問題時,掃描程序調用此方法。
java.util.List<IScanIssue> doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint)
掃描器為每個被主動掃描的插入點調用此方法。
java.util.List<IScanIssue> doPassiveScan(IHttpRequestResponse baseRequestResponse)
掃描器為被動掃描的每個基本請求/響應調用此方法。
IScanIssue
此接口用於檢索掃描儀問題的詳細信息。擴展可以通過注冊IsCannelListener或調用IBurpExtenderCallbacks.getScanIssues()獲取問題的詳細信息。擴展還可以通過注冊IScannerCheck或調用IBurpExtenderCallbacks.addScanIssue()並提供自己的此接口實現來添加自定義掃描程序問題。請注意,由擴展生成的問題描述和其他文本受HTML白名單的約束,該白名單只允許格式化標記和簡單的超鏈接。
IRequestInfo
此接口用於檢索有關HTTP請求的關鍵詳細信息。擴展可以通過調用IExtensionHelpers.AnalyzerRequest()
為給定請求獲取IRequestInfo對象。
而這個IExtensionHelpers實例對象可靠callbacks.getHelpers()
獲取。
IMessageEditorController
IMessageEditor使用此接口獲取有關當前顯示消息的詳細信息。創建Burp的HTTP消息編輯器實例的擴展可以選擇性地提供IMessageEditorController的實現,當編輯器需要關於當前消息的進一步信息(例如,將其發送到另一個Burp工具)時,它將調用IMessageEditorController。通過IMessageEditorTabFactory提供自定義編輯器選項卡的擴展將為它們生成的每個選項卡實例接收對IMessageEditorController對象的引用,如果選項卡需要有關當前消息的更多信息,則可以調用該引用。
IHttpService getHttpService()
此方法用於檢索當前消息的HTTP服務。
byte[] getRequest()
此方法用於檢索與當前消息關聯的HTTP請求(它本身可能是響應)。
byte[] getResponse()
此方法用於檢索與當前消息(本身可能是請求)關聯的HTTP響應。
AbstractTableModel
java提供的AbstractTableModel
是一個抽象類,這個類幫我們實現大部份的TableModel
方法,除了getRowCount()
,getColumnCount()
,getValueAt()
這三個方法外。因此我們的主要任務就是去實現這三個方法.利用這個抽象類就可以設計出不同格式的表格
void addTableModelListener(TableModelListener l):使表格具有處理TableModelEvent的能力.當表格的Table Model有所變化時,會發出TableModelEvent事件信息。
int findColumn(String columnName):尋找在行名稱中是否含有columnName這個項目.若有,則返回其所在行的位置;反之則返回-1表示未找到。
void fireTableCellUpdated(int row, int column):通知所有的Listener在這個表格中的(row,column)字段的內容已經改變了。
void fireTableChanged(TableModelEvent e):將所收的事件通知傳送給所有在這個table model中注冊過的TableModelListeners。
void fireTableDataChanged():通知所有的listener在這個表格中列的內容已經改變了.列的數目可能已經改變了,因此JTable可能需要重新顯示此表格的結構。
void fireTableRowsDeleted(int firstRow, int lastRow):通知所有的listener在這個表格中第firstrow行至lastrow列已經被刪除了。
void fireTableRowsUpdated(int firstRow, int lastRow):通知所有的listener在這個表格中第firstrow行至lastrow列已經被修改了。
void fireTableRowsInserted(int firstRow, int lastRow):通知所有的listener在這個表格中第firstrow行至lastrow列已經被加入了。
void fireTableStructureChanged():通知所有的listener在這個表格的結構已經改變了.行的數目,名稱以及數據類型都可能已經改變了
Class getColumnClass(int columnIndex):返回字段數據類型的類名稱。
String getColumnName(int column):若沒有設置列標題則返回默認值,依次為A,B,C,...Z,AA,AB,..;若無此column,則返回一個空的String
Public EventListener[] getListeners(Class listenerType):返回所有在這個table model所建立的listener中符合listenerType的listener,並以數組形式返回。
boolean isCellEditable(int rowIndex, int columnIndex):返回所有在這個table model所建立的listener中符合listenerType形式的listener,並以數組形式返回。
void removeTableModelListener(TableModelListener l):從TableModelListener中移除一個listener。
void setValueAt(Object aValue, int rowIndex, int columnIndex):設置某個cell(rowIndex,columnIndex)的值
建立一個監聽器來獲取每次請求的參數
package burp.Vuln;
import burp.*;
import java.io.PrintWriter;
import java.util.List;
public class test implements IHttpListener {
PrintWriter stdout;
IBurpExtenderCallbacks callbacks;
public test(IBurpExtenderCallbacks callbacks, PrintWriter stdout) {
this.stdout = stdout;
this.callbacks = callbacks;
callbacks.registerHttpListener(this);
}
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
IExtensionHelpers helpers = this.callbacks.getHelpers();
IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo);
List<IParameter> parameters = iRequestInfo.getParameters();
for (IParameter parameter : parameters) {
String name = parameter.getName();
String value = parameter.getValue();
this.stdout.println(name+"="+value);
}
}
}
漏洞探測返回結果
public Cas_Vuln(IBurpExtenderCallbacks callbacks, PrintWriter stdout) {
this.stdout = stdout;
this.callbacks = callbacks;
callbacks.registerHttpListener(this);
}
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
IExtensionHelpers helpers = this.callbacks.getHelpers();
IRequestInfo iRequestInfo = helpers.analyzeRequest(messageInfo);
url = iRequestInfo.getUrl();
method = iRequestInfo.getMethod();
List<IParameter> parameters = iRequestInfo.getParameters();
for (IParameter parameter : parameters) {
this.parameter = parameter;
String parameterName = parameter.getName();
boolean is_vuln = Is_Vuln(messageInfo, this.stdout, parameterName);
if (is_vuln){
Vuln_Data vuln_data = new Vuln_Data();
vuln_data.setId(BurpExtender.Vuln_Data.size());
vuln_data.setUrl(GetURL(messageInfo));
vuln_data.setExtensionMethod("null");
vuln_data.setRequestMethod(method);
vuln_data.setIssue(Vuln_Name);
BurpExtender.Vuln_Data.add(vuln_data);
BurpExtender.tags.fireTableDataChanged();
this.stdout.println(GetURL(messageInfo)+" "+"IS_Vuln:"+is_vuln);
}
this.stdout.println(GetURL(messageInfo)+" "+"IS_Vuln:"+is_vuln);
}
}
內容實時顯示在界面中
AbstractTableModel
中代碼
@Override
public int getRowCount() {
return BurpExtender.Vuln_Data.size();
}
@Override
public int getColumnCount() {
return 8;
}
@Override
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case 0:
return "#";
case 1:
return "extensionMethod";
case 2:
return "requestMethod";
case 3:
return "url";
case 4:
return "statusCode";
case 5:
return "issue";
case 6:
return "startTime";
case 7:
return "endTime";
}
return null;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Vuln_Data vuln_data = BurpExtender.Vuln_Data.get(rowIndex);
switch (columnIndex) {
case 0:
return vuln_data.getId();
case 1:
return vuln_data.getExtensionMethod();
case 2:
return vuln_data.getRequestMethod();
case 3:
return vuln_data.getUrl();
case 4:
return vuln_data.getStatusCode();
case 5:
return vuln_data.getIssue();
case 6:
return new Date();
case 7:
return new Date();
}
return null;
}
public class URLTable extends JTable {
public URLTable(TableModel tableModel) {
super(tableModel);
}
}
}
調用BurpExtender.tags.fireTableDataChanged();
對該界面進行刷新。
最后要做的就是request/response中顯示對應的包。
request/response消息實現
每次漏洞掃描中將每次對應的IHttpRequestResponse
的requestResponse
對象也給存儲起來。在changeSelection
方法進行設置HRequestTextEditor.setMessage
。
public class URLTable extends JTable {
public URLTable(TableModel tableModel) {
super(tableModel);
}
public void changeSelection(int row, int col, boolean toggle, boolean extend) {
Vuln_Data vuln_data = BurpExtender.Vuln_Data.get(convertRowIndexToModel(row));
Tags.this.HRequestTextEditor.setMessage(vuln_data.requestResponse.getRequest(), true);
Tags.this.HResponseTextEditor.setMessage(vuln_data.requestResponse.getResponse(), false);
Tags.this.currentlyDisplayedItem= vuln_data.requestResponse;
super.changeSelection(row, col, toggle, extend);
}
實現IMessageEditorController
接口,重寫一下三個方法即可。
@Override
public IHttpService getHttpService() {
return currentlyDisplayedItem.getHttpService();
}
@Override
public byte[] getRequest() {
return currentlyDisplayedItem.getRequest();
}
@Override
public byte[] getResponse() {
return currentlyDisplayedItem.getResponse();
}