BurpSuite 作為一款 Web 安全測試的利器,得益于其強大的代理,操控數據的功能,在 Web 安全測試過程中,為我們省下了不少時間和精力專注于漏洞的挖掘和測試。更重要的是 BurpSuite 提供了插件開發接口,Web 安全測試人員可以根據實際需求自己開發出提高安全測試效率的插件,雖然 BApp Store 上面已經提供了很多插件,其中也不乏優秀好用的插件,但是作為一名專業的 Web 安全測試人員,有必要熟練掌握 BurpSuite 插件開發技術。
國內針對 BurpSuite 插件開發的技術文檔并不是很多,也不夠全面,《BurpSuite 插件開發指南》系列文章作為 BurpSuite 非官方非權威開發指南,希望在這塊填補一些空白。文章中難免有紕漏,望讀者自行驗證并及時指出。
《BurpSuite 插件開發指南》系列文章如下:
在第一篇文章中,筆者將會介紹 BurpSuite 所提供的開發接口的功能和參數說明,但是不會對 BurpSuite 本身所提供的功能作太多說明。剩余兩篇則會分別使用案例闡述 Java 和 Python 進行插件開發的技術。其中會用到 Java 的 Swing 包進行 GUI 編程。
BurpSuite 插件擴展開發所支持的編程語言有 Java 和 Python,使用這兩種開發語言開發的插件在功能上并沒有太多區別,使用原生的 Java 開發的插件要比 Python 開發的插件,加載和執行效率更高。所以,筆者推薦使用 Java 作為插件開發的首選語言。
SDK:
注:導出 SDK 包文件操作步驟:“Extender — APIs - Save interface files(左下角)”
開發 IDE:
注:推薦使用 Eclipse 作為插件開發 IDE, 除了 Java 之外,Eclipse 對 JPython 也有很好的支持。
輔助工具:
學習資料:
BurpSuite 官方并未提供詳細的開發文檔,只有少量的開發實例,可以在此找到。不過,BurpSuite 官方建立了插件開發者社區和博客,可以幫助開發者解答疑問。
在 烏云Drops 上也有兩篇專門介紹 BurpSuite 插件開發的文章,讀者可以參考:
相較于閱讀許多參考資料從零開始編寫插件,筆者更推薦在熟悉了開發文檔后直接在已有的源碼中“照貓畫虎”進行插件的開發。現有的插件,讀者可以在 BApp Store 中找到,Python 版本的插件可以直接查看源碼進行學習,Java 版本的插件可以在反編譯后查看其源碼進行學習。
注:安裝了 BApp Store 中的插件后,會在 BurpSuite 所在目錄下的 bapps 文件夾中找到插件文件。
插件開發“方法論”:
插件型的應用程序在設計時就一定會考慮插件的開發,因此,主程序與插件之間必然有一種“約定”,在開發插件的過程中,只需按照程序開發者設計的這種“約定”開發就好了,讀者在閱讀參考官方開發文檔時只需注意以下三點:
在關注上述三點以后,就可以把多個接口方法聯系在一起,組織起一定的邏輯,這樣,開發的思路就順理成章了。
本節將對 BurpSuite 的官方文檔做簡要的說明,并給出簡單的 Demo code,各個接口具體的使用實例,可以在筆者后續的兩篇文章中看到。
public interface IBurpExtender
所有的擴展必須實現此接口,實現的類名必須為“BurpExtender”。在 burp 包中,必須申明為 public ,并且必須提供一個默認的構造器。
此接口實現了以下方法:
#!java
void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
此方法將在擴展加載后被調用,它注冊了一個 IBurpExtenderCallbacks 接口的實例,IBurpExtenderCallbacks 接口提供了許多在開發插件過程中常用的一些操作。
參數說明:
Demo code:
#!java
package burp;
public class BurpExtender implements IBurpExtender{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
// TODO here
}
}
public interface IBurpExtenderCallbacks
此接口中實現的方法和字段在插件開發過程中會經常使用到。 Burp Suite 利用此接口向擴展中傳遞了許多回調方法,這些回調方法可被用于在 Burp 中執行多個操作。當擴展被加載后,Burp 會調用 registerExtenderCallbacks() 方法,并傳遞一個 IBurpExtenderCallbacks 的實例。擴展插件可以通過這個實例調用很多擴展 Burp 功能必需的方法。如:設置擴展插件的屬性,操作 HTTP 請求和響應以及啟動其他掃描功能等等。
此接口提供了很多的方法和字段,在此不一一列舉,具體的說明可以在 burp SDK 中的 IBurpExtenderCallbacks.java 或 https://portswigger.net/burp/extender/api/burp/IBurpExtenderCallbacks.html 中查看。
Demo code:
#!java
package burp;
public class BurpExtender implements IBurpExtender{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in"); //設置擴展名稱為 “Her0in”
}
}
public interface IContextMenuFactory
Burp 的作者在設計上下文菜單功能中采用了工廠模式的設計模式,擴展可以實現此接口,然后調用 IBurpExtenderCallbacks.registerContextMenuFactory() 注冊自定義上下文菜單項的工廠。
此接口提供了如下方法:
#!java
java.util.List<javax.swing.JMenuItem> createMenuItems(IContextMenuInvocation invocation)
當用戶在 Burp 中的任何地方調用一個上下文菜單時,Burp 則會調用這個工廠方法。此方法會根據菜單調用的細節,提供應該被顯示在上下文菜單中的任何自定義上下文菜單項。
參數說明:
返回值:
此工廠方法將會返回需要被顯示的自定義菜單項的一個列表(包含子菜單,checkbox 菜單項等等), 若無菜單項顯示,此工廠方法會返回 null。
Demo code:
#!java
package burp;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
public class BurpExtender implements IBurpExtender, IContextMenuFactory{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in");
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("Her0in");
jMenu.add(menuItem);
listMenuItems.add(jMenu);
return listMenuItems;
}
}
public interface IContextMenuInvocation
此接口被用于獲取當 Burp 調用擴展提供的 IContextMenuFactory 工廠里的上下文菜單時的一些細節,如能獲取到調用了擴展提供的自定義上下文菜單的 Burp 工具名稱(在 IBurpExtenderCallbacks 中定義)或功能組件的名稱(在 IContextMenuInvocation 中定義)。
此接口提供了如下方法:
#!java
// 此方法可被用于獲取本地Java輸入事件,并作為上下文菜單調用的觸發器
java.awt.event.InputEvent getInputEvent()
// 此方法被用于獲取上下文中被調用的菜單
byte getInvocationContext()
// 此方法被用于獲取用戶選中的 Scanner 問題的細節
IScanIssue[] getSelectedIssues()
// 此方法被用于獲取當前顯示的或用戶選中的 HTTP 請求/響應的細節
IHttpRequestResponse[] getSelectedMessages()
// 此方法被用于獲取用戶所選的當前消息的界限(消息需可適用)
int[] getSelectionBounds()
// 此方法被用于獲取調用上下文菜單的 Burp 工具
int getToolFlag()
Demo code:
#!java
package burp;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
public class BurpExtender implements IBurpExtender, IContextMenuFactory{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in");
callbacks.registerContextMenuFactory(this);
}
@Override
public List<JMenuItem> createMenuItems(final IContextMenuInvocation invocation) {
List<JMenuItem> listMenuItems = new ArrayList<JMenuItem>();
// 菜單只在 REPEATER 工具的右鍵菜單中顯示
if(invocation.getToolFlag() == IBurpExtenderCallbacks.TOOL_REPEATER){
//子菜單
JMenuItem menuItem;
menuItem = new JMenuItem("子菜單測試");
//父級菜單
JMenu jMenu = new JMenu("Her0in");
jMenu.add(menuItem);
listMenuItems.add(jMenu);
}
return listMenuItems;
}
}
public interface ICookie
此接口用于獲取 HTTP cookie 的一些信息。
#!java
// 此方法用于獲取 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()
public interface IExtensionHelpers
此接口提供了很多常用的輔助方法,擴展可以通過調用 IBurpExtenderCallbacks.getHelpers 獲得此接口的實例。
開發插件常用的幾個方法如下:
#!java
// 此方法會添加一個新的參數到 HTTP 請求中,并且會適當更新 Content-Length
byte[] addParameter(byte[] request, IParameter parameter)
// 此方法用于分析 HTTP 請求信息以便獲取到多個鍵的值
IRequestInfo analyzeRequest(byte[] request)
// 此方法用于分析 HTTP 響應信息以便獲取到多個鍵的值
IResponseInfo analyzeResponse(byte[] response)
// 構建包含給定的 HTTP 頭部,消息體的 HTTP 消息
byte[] buildHttpMessage(java.util.List<java.lang.String> headers, byte[] body)
// 對給定的 URL 發起 GET 請求
byte[] buildHttpRequest(java.net.URL url)
// bytes 到 String 的轉換
java.lang.String bytesToString(byte[] data)
// String 到 bytes 的轉
java.lang.String bytesToString(byte[] data)
public interface IExtensionStateListener
擴展可以實現此接口,然后調用 IBurpExtenderCallbacks.registerExtensionStateListener() 注冊一個擴展的狀態監聽器。在擴展的狀態發生改變時,監聽器將會收到通知。注意:任何啟動后臺線程或打開系統資源(如文件或數據庫連接)的擴展插件都應該注冊一個監聽器,并在被卸載后終止線程/關閉資源。
此接口只提供了一個方法:
#!java
void extensionUnloaded()
在插件被 unload (卸載)時,會調用此方法,可以通過重寫此方法,在卸載插件時,做一些善后處理工作。
Demo code:
#!java
package burp;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender, IExtensionStateListener{
private PrintWriter stdout;
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
this.stdout = new PrintWriter(callbacks.getStdout(), true);
callbacks.setExtensionName("Her0in");
// 先注冊擴展狀態監聽器
callbacks.registerExtensionStateListener(this);
}
// 重寫 extensionUnloaded 方法
@Override
public void extensionUnloaded() {
// TODO
this.stdout.println("extensionUnloaded ...");
}
}
public interface IHttpListener
擴展可以實現此接口。通過調用 IBurpExtenderCallbacks.registerHttpListener() 注冊一個 HTTP 監聽器。Burp 里的任何一個工具發起 HTTP 請求或收到 HTTP 響應都會通知此監聽器。擴展可以得到這些交互的數據,進行分析和修改。
此接口提供了如下方法:
#!java
void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
如果在開發插件的時候需要獲取到所有的 HTTP 數據包,包括通過 Repeater 工具自定義修改的請求,則必須實現此接口,重寫該方法。
參數說明:
#!java
// 指示了發起請求或收到響應的 Burp 工具的 ID,所有的 toolFlag 定義在 IBurpExtenderCallbacks 接口中。
int toolFlag
// 指示該消息是請求消息(值為True)還是響應消息(值為False)
messageIsRequest
// 被處理的消息的詳細信息,是一個 IHttpRequestResponse 對象
messageInfo
Demo code:
#!java
package burp;
public class BurpExtender implements IBurpExtender, IHttpListener{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in");
callbacks.registerHttpListener(this);
}
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest,
IHttpRequestResponse messageInfo) {
// TODO here
}
}
public interface IHttpRequestResponse
此接口用于檢索和更新有關 HTTP 消息的詳細信息。
注意:setter 方法通常只能在消息被被處理之前使用,因為它是一個寫操作,因此在只讀的上下文中也是不可用的。與響應細節相關的 getter 方法只能用在請求發出后使用。
#!java
// 獲取用戶標注的注釋信息
java.lang.String getComment()
// 獲取用戶標注的高亮信息
java.lang.String getHighlight()
// 獲取請求/響應的 HTTP 服務信息
IHttpService getHttpService()
// 獲取 HTTP 請求信息
byte[] getRequest()
// 獲取 HTTP 響應信息
byte[] getResponse()
// 更新用戶標注的注釋信息
void setComment(java.lang.String comment)
// 更新用戶標注的高亮信息
void setHighlight(java.lang.String color)
// 更新 請求/響應的 HTTP 服務信息
void setHttpService(IHttpService httpService)
// 更新 HTTP 請求信息
void setRequest(byte[] message)
// 更新 HTTP 響應信息
void setResponse(byte[] message)
public interface IHttpRequestResponsePersisted extends IHttpRequestResponse
此接口是 IHttpRequestResponse 接口的一個子接口,該接口用于使用 IBurpExtenderCallbacks.saveBuffersToTempFiles() 將一個IHttpRequestResponse 對象的請求和響應消息保存到臨時文件。
此接口只提供了一個方法:
#!java
void deleteTempFiles()
注:此方法已經過時了,并且不會執行任何操作。
public interface IHttpRequestResponseWithMarkers extends IHttpRequestResponse
此接口是 IHttpRequestResponse 接口的一個子接口,此接口用于那些已被標記的 IHttpRequestResponse 對象,擴展可以使用 IBurpExtenderCallbacks.applyMarkers() 創建一個此接口的實例,或提供自己的實現。標記可用于各種情況,如指定Intruder 工具的 payload 位置,Scanner 工具的插入點或將 Scanner 工具的一些問題置為高亮。
此接口提供了兩個分別操作請求和響應的方法:
#!java
// 獲取帶有標記的請求信息的詳細信息
java.util.List<int[]> getRequestMarkers()
// 獲取帶有標記的請求信息的詳細信息
java.util.List<int[]> getResponseMarkers()
這兩個方法的返回值均為一個整型數組列表,分別表示請求消息/響應消息標記偏移的索引對。列表中的每一項目都是一個長度為 2 的整型數組(int2)包含標記開始和結束的偏移量。如果沒有定義請求/響應標記,返回 null。
public interface IHttpService
此接口用于提供關于 HTTP 服務信息的細節。
此接口提供了如下方法:
#!java
// 返回 HTTP 服務信息的主機名或 IP 地址
java.lang.String getHost()
// 返回 HTTP 服務信息的端口
int getPort()
// 返回 HTTP 服務信息的協議
java.lang.String getProtocol()
Demo code:
#!java
package burp;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender, IHttpListener{
private PrintWriter stdout;
public IBurpExtenderCallbacks iCallbacks;
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
this.stdout = new PrintWriter(callbacks.getStdout(), true);
callbacks.setExtensionName("Her0in");
callbacks.registerHttpListener(this);
}
@Override
public void processHttpMessage(int toolFlag, boolean messageIsRequest,
IHttpRequestResponse messageInfo) {
IHttpService iHttpService = messageInfo.getHttpService();
this.stdout.println(iHttpService.getHost());
this.stdout.println(iHttpService.getPort());
this.stdout.println(iHttpService.getProtocol());
}
}
public interface IInterceptedProxyMessage
注:
在 BurpSuite 的 Proxy 工具下的 Options 標簽里有幾個自定義消息攔截和響應的功能。讀者可以在熟悉了這幾個功能后,再了解此接口的作用。
此接口不能被擴展實現,它表示了已被 Burp 代理攔截的 HTTP 消息。擴展可以利用此接口注冊一個 IProxyListener 以便接收代理消息的細節。
此接口提供了以下方法:
#!java
// 獲取被攔截的請求消息的客戶端 IP 地址
java.net.InetAddress getClientIpAddress()
// 獲取當前定義的攔截操作類型,具體的類型可以在本接口中看到
int getInterceptAction()
// 獲取 Burp Proxy 處理攔截消息監聽器的名稱
java.lang.String getListenerInterface()
// 獲取被攔截的消息的詳細信息
IHttpRequestResponse getMessageInfo()
// 獲取請求/響應消息的唯一引用號
int getMessageReference()
// 設置更新攔截操作
void setInterceptAction(int interceptAction)
Demo code:
#!java
package burp;
import java.io.PrintWriter;
public class BurpExtender implements IBurpExtender, IProxyListener{
private PrintWriter stdout;
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in");
this.stdout = new PrintWriter(callbacks.getStdout(), true);
callbacks.registerProxyListener(this);
}
@Override
public void processProxyMessage(boolean messageIsRequest,
IInterceptedProxyMessage message) {
// 只操作請求消息
if(messageIsRequest){
// 獲取并打印出客戶端 IP
this.stdout.println(message.getClientIpAddress());
// Drop 掉所有請求
message.setInterceptAction(IInterceptedProxyMessage.ACTION_DROP);
// TODO here
}
}
}
public interface IIntruderAttack
此接口用于操控 Intruder 工具的攻擊詳情。
此接口提供了以下方法:
#!java
// 獲取攻擊中的 HTTP 服務信息
IHttpService getHttpService()
// 獲取攻擊中的請求模版
byte[] getRequestTemplate()
public interface IIntruderPayloadGenerator
此接口被用于自定義 Intruder 工具的 payload 生成器。當需要發起一次新的 Intruder 攻擊時,擴展需要注冊一個 IIntruderPayloadGeneratorFactory 工廠并且必須返回此接口的一個新的實例。此接口會將當前插件注冊為一個 Intruder 工具的 payload 生成器。
#!java
此接口提供了如下方法:
// 此方法由 Burp 調用,用于獲取下一個 payload 的值
byte[] getNextPayload(byte[] baseValue)
// 此方法由 Burp 調用,用于決定 payload 生成器是否能夠提供更多 payload
boolean hasMorePayloads()
// 此方法由 Burp 調用,用于重置 payload 生成器的狀態,這將導致下一次調用 getNextPayload() 方法時會返回第一條 payload
void reset()
Demo code:
#!java
package burp;
public class BurpExtender implements IBurpExtender, IIntruderPayloadGeneratorFactory{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in");
// 將當前插件注冊為一個 Intruder 工具的 payload 生成器
callbacks.registerIntruderPayloadGeneratorFactory(this);
}
@Override
public String getGeneratorName() {
// 設置 payload 生成器名稱
return "自定義 payload 生成器";
}
@Override
public IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack) {
// 返回一個新的 payload 生成器的實例
return new IntruderPayloadGenerator();
}
// 實現 IIntruderPayloadGenerator 接口,此接口提供的方法是由 Burp 來調用的
class IntruderPayloadGenerator implements IIntruderPayloadGenerator{
@Override
public boolean hasMorePayloads() {
// TODO here
return false;
}
@Override
public byte[] getNextPayload(byte[] baseValue) {
// TODO here
return null;
}
@Override
public void reset() {
// TODO here
}
}
}
在 Burp 加載了上述插件后,可以按照下圖標紅的步驟操作,即可看到自定義的 payload 生成器:
public interface IIntruderPayloadGeneratorFactory
擴展可以實現此接口,并且可以調用 IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory() 注冊一個自定義的 Intruder 工具的 payload 生成器。
該接口提供了以下方法:
#!java
// 此方法由 Burp 調用,用于創建一個 payload 生成器的新實例。當用戶發動一次 Intruder 攻擊時,將會使用該方法返回的 payload 生成器的實例
IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack)
// 此方法由 Burp 調用,用于獲取 payload 生成器的名稱
java.lang.String getGeneratorName()
public interface IIntruderPayloadProcessor
擴展可以實現此接口,并且可以調用 IBurpExtenderCallbacks.registerIntruderPayloadProcessor() 注冊一個自定義 Intruder 工具的 payload 的處理器。此接口會將當前插件注冊為一個 Intruder 工具的 payload 處理器。
該接口提供了以下方法:
#!java
// 此方法由 Burp 調用,用于獲取 payload 處理器的名稱
java.lang.String getProcessorName()
// 此方法由 Burp 調用,當處理器每次應用 payload 到一次 Intruder 攻擊時,Burp 都會調用一次此方法
byte[] processPayload(byte[] currentPayload, byte[] originalPayload, byte[] baseValue)
// processPayload 方法說明:
參數說明:
返回值:
返回已被處理過的 payload 的值。 如果返回 null 意味著當前的 payload 將被跳過,并且此次攻擊將被直接移動到下一個 payload 。
Demo code:
#!java
package burp;
public class BurpExtender implements IBurpExtender, IIntruderPayloadProcessor{
@Override
public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks){
callbacks.setExtensionName("Her0in");
// 將當前插件注冊為一個 Intruder 工具的 payload 處理器
callbacks.registerIntruderPayloadProcessor(this);
}
// 此方法由 Burp 調用
@Override
public String getProcessorName() {
// 設置自定義 payload 處理器的名稱
return "自定義 payload 處理器";
}
// 此方法由 Burp 調用,且會在每次使用一個 payload 發動攻擊時都會調用一次此方法
@Override
public byte[] processPayload(byte[] currentPayload, byte[] originalPayload,
byte[] baseValue) {
// TODO here
return null;
}
}
在 Burp 加載了上述插件后,可以按照下圖標紅的步驟操作,即可看到自定義的 payload 生成器:
public interface IMenuItemHandler
此接口已過時,不推薦再使用,請使用IContextMenuFactory代替。
擴展可以實現此接口,并且通過調用 IBurpExtenderCallbacks.registerMenuItem() 方法注冊一個自定義的上下文菜單項。
此接口提供了如下方法:
#!java
// 當用戶單擊一個已經在 Burp 中注冊過的上下文菜單項時 Burp 會調用一次此方法。不過,此方法已經過時
void menuItemClicked(java.lang.String menuItemCaption, IHttpRequestResponse[] messageInfo)
本文由 Her0in 原創并首發于烏云drops,轉載請注明出處