Burp Suite插件開發-Python
相較於挖SRC,我還是喜歡做一點東西。由於我對JAVA並不是很了解,所以選擇Python來做插件開發。在學習過程中遇到很多坑,寫個文檔用於記錄。由於我也不熟悉UI相關的開發,所以UI相關的類暫時就不寫了
前言
熟讀官方的API文檔
打開導出的 index.html
基礎模板
BURP 插件的開發可以簡單理解為對API類的繼承以及對方法的重寫。
#coding=utf-8
from burp import IBurpExtender
import sys
if sys.version[0] == '2': # 為什么要判斷是不是python2呢?鬼知道后面jython會不會支持python3
reload(sys)
sys.setdefaultencoding("utf-8") #設置系統編碼,否則碰到中文的地方都會報錯(即使是設置了coding也沒用)
class BurpExtender(IBurpExtender): # 所有插件都必須實現BurpExtender類,並且繼承IBurpExtender
def registerExtenderCallbacks(self,callbacks): # 該方法在啟動插件時會自動調用,用於注冊插件
# callbacks是一個IBurpExtenderCallbacks,里面提供了很多基礎方法,如注冊監聽器等
callbacks.setExtenderName("MyExt") # 設置插件名稱
def Myfunc(self,paras): # 自定義方法
pass
上面的代碼實現了一個沒有任何功能的插件
Python基礎知識
在閱讀其他人寫的Burp插件時,常常會在registerExtenderCallbacks
函數中看到self._callbacks = callbacks
,此舉是用於注冊全局變量,方便在其他函數中使用該變量(后面會經常用到),且self
對象只會在插件卸載時才會被釋放
注意:重寫的方法或者自定義的方法第一個參數均為self,因為是類中的方法。且調用自定義方法需要使用 self.Myfunc() 的方式調用
常用API介紹
Burp的API大體可以分為5類:插件入口類、監聽類、工具類、HTTP消息類、UI類
為了后續實操,所以先將會用到的類簡單介紹一下,重點需要注意方法的參數及返回值兩者的數據類型
插件入口類
IBurpExtender
所有擴展都必須實現這個接口。也就是如上方模板所寫,BurpExtender 必須繼承 IBurpExtender 類
該類下面只有一個方法,並只會在啟動插件的時候調用一次。因此常在該方法內執行一些初始化操作,如定義插件名稱、初始化UI、注冊全局變量、注冊監聽器
注意:打開BURP時會啟動插件,所以也會調用一次該方法
registerExtenderCallbacks
void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
可以看到,方法內的參數為一個 IBurpExtenderCallbacks 對象,具體可看下面
IBurpExtenderCallbacks
Burp Suite 使用此接口將一組回調方法傳遞給擴展,擴展可以使用這些方法在 Burp 中執行各種操作。可能不是很好理解,簡單來說就是該類提供了很多方法,用來告訴BURP應該在什么時候干什么事的
變量
查看API文檔首先可以看到,該類下面有一些靜態變量。這些變量常用來判斷HTTP消息是從哪個地方傳過來的,如 Proxy、Repeater、Intruder等,常在IHttpListener的processHttpMessage方法內用到,暫時可以不用管
方法
只涉及常用方法講解
printError
void printError(java.lang.String error)
輸出錯誤信息
printOutput
void printOutput(java.lang.String output)
輸出一般信息
setExtensionName
void setExtensionName(java.lang.String name)
用於設置插件名稱
getProxyHistory
IHttpRequestResponse[] getProxyHistory()
用於獲取 Proxy 模塊內所有的歷史請求,並返回一個 IHttpRequestResponse 類型的數組。如果想在安裝插件的時候自動掃描歷史請求,可以使用這個方法
registerProxyListener
void registerProxyListener(IProxyListener listener)
用於注冊Proxy監聽器,監聽Proxy模塊正在處理的請求和響應的通知。
registerHttpListener
void registerHttpListener(IHttpListener listener)
用於注冊HTTP監聽器,監聽所有模塊(Proxy、Repeater等模塊)正在處理的請求和響應的通知。
registerExtensionStateListener
void registerExtensionStateListener(IExtensionStateListener listener)
用於注冊插件狀態監聽(在卸載插件時需要)
registerContextMenuFactory
void registerContextMenuFactory(IContextMenuFactory factory)
用於為自定義上下文菜單項注冊工廠(注冊右鍵菜單時需要)
還有很多類似的register方法
saveBuffersToTempFiles
IHttpRequestResponsePersisted saveBuffersToTempFiles(IHttpRequestResponse httpRequestResponse)
用於將請求、響應消息對象保存到臨時文件中,減少內存開銷
saveToTempFile
ITempFile saveToTempFile(byte[] buffer)
用於將傳入的buffer保存到臨時文件,如byte類型請求響應等,並返回一個ITempFile對象
saveConfigAsJson
java.lang.String saveConfigAsJson(java.lang.String... configPaths)
用於獲取BURP項目級別的配置,傳入configPaths可獲取指定配置,可配合saveTempfile保存文件
loadConfigFromJson
void loadConfigFromJson(java.lang.String config)
導入項目配置文件
makeHttpRequest
IHttpRequestResponse makeHttpRequest(IHttpService httpService, byte[] request)
IHttpRequestResponse makeHttpRequest(IHttpService httpService, byte[] request, boolean forceHttp1)
byte[] makeHttpRequest(java.lang.String host, int port, boolean useHttps, byte[] request)
byte[] makeHttpRequest(java.lang.String host, int port, boolean useHttps, byte[] request, boolean forceHttp1)
用於 發起 HTTP/1請求
makeHttp2Request
byte[] makeHttp2Request(IHttpService httpService, java.util.List<IHttpHeader> headers, byte[] body)
byte[] makeHttp2Request(IHttpService httpService, java.util.List<IHttpHeader> headers, byte[] body, boolean forceHttp2)
byte[] makeHttp2Request(IHttpService httpService, java.util.List<IHttpHeader> headers, byte[] body, boolean forceHttp2, java.lang.String connectionIdentifier)
用於 發起 HTTP/2請求
getHelpers
IExtensionHelpers getHelpers()
此方法用於獲取 IExtensionHelpers 對象,擴展程序可以使用該對象來執行許多有用的任務。
是一個小工具,里面有很多常用的方法供我們使用,讓插件編寫更加方便
工具類
IExtensionHelpers
該接口包含許多輔助方法,擴展可以使用這些方法來協助 Burp 擴展出現的各種常見任務
analyzeRequest
IRequestInfo analyzeRequest(byte[] request)
IRequestInfo analyzeRequest(IHttpRequestResponse request)
IRequestInfo analyzeRequest(IHttpService httpService, byte[] request)
可以看到,該方法重載了三次。傳入不同的參數類型,最終得到的都是一個IRequestInfo對象(請求對象)
這里有一個坑,第一個跟第二個得到的對象存在一點區別
analyzeResponse
IResponseInfo analyzeResponse(byte[] response)
得到一個IResponseInfo對象(響應對象)
編碼相關
# base64編碼解碼
byte[] base64Decode(byte[] data)
byte[] base64Decode(java.lang.String data)
java.lang.String base64Encode(byte[] data)
java.lang.String base64Encode(java.lang.String data)
# URL編碼解碼
byte[] urlDecode(byte[] data)
java.lang.String urlDecode(java.lang.String data)
byte[] urlEncode(byte[] data)
java.lang.String urlEncode(java.lang.String data)
# 字符與byte相互轉換
java.lang.String bytesToString(byte[] data)
byte[] stringToBytes(java.lang.String data)
buildHttpMessage
byte[] buildHttpMessage(java.util.List<java.lang.String> headers, byte[] body)
構建一個請求、響應消息,常用於攔截請求並自動修改其中的參數時會使用
HTTP消息類
IInterceptedProxyMessage
該類表示一條被Proxy模塊攔截的請求
getClientIpAddress
java.net.InetAddress getClientIpAddress()
獲取被攔截請求的客戶端IP,也就是請求來源IP
getListenerInterface
java.lang.String getListenerInterface()
獲取被攔截請求的Proxy Listener,返回值:127.0.0.1:8080
可用來針對不同來源的請求做特殊化處理,如PC/手機
getMessageInfo
IHttpRequestResponse getMessageInfo()
獲取被攔截的請求/響應消息的IHttpRequestResponse對象
IHttpRequestResponse
此接口用來獲取&更新請求響應
setHighlight
void setHighlight(java.lang.String color)
高亮請求響應,可傳入red、orange、yellow、green、cyan、blue、pink、magenta、gray值,傳空值則表示清除高亮設置
getHighlight
java.lang.String getHighlight()
獲取請求是否高亮,若有則返回對應顏色,若沒有則返回None
setComment
void setComment(java.lang.String comment)
設置備注
getComment
java.lang.String getComment()
獲取備注
setHttpService
void setHttpService(IHttpService httpService)
設置請求的服務器地址,需要傳入一個IHttpService對象
getHttpService
IHttpService getHttpService()
獲取請求的服務器地址IHttpService對象
setRequest
void setRequest(byte[] message)
設置請求,在需要修改請求內容時用到
getRequest
byte[] getRequest()
獲取請求,注意此時是byte數組,一般需要用IExtensionHelpers.analyzeRequest(byte[] request)方法轉成IRequestInfo對象
setResponse
void setResponse(byte[] message)
設置響應,在需要修改響應內容時用到
getResponse
byte[] getResponse()
獲取響應,注意此時是byte數組,一般需要用IExtensionHelpers.analyzeRespnse(byte[] response)方法轉成IResponseInfo對象
IRequestInfo
HTTP請求對象
getMethod
java.lang.String getMethod()
獲取請求方法,GET、POST、PUT······
getUrl
java.net.URL getUrl()
獲取請求的URL,注意此處返回值是一個java.net.URL對象,需要再調用具體的方法獲取URL相關內容
getHeaders
java.util.List<java.lang.String> getHeaders()
以數組方式返回請求行與請求頭,如["GET / HTTP/1.1", "Host: example.com"]
getParameters
java.util.List<IParameter> getParameters()
獲取所有請求參數,包含了JSON數據中的參數,返回一個IParameter類型的數組
getContentType
byte getContentType()
獲取請求頭中的Content-Type
,注意此處返回的是一個整形數字,可以與該類中的靜態變量對比
getBodyOffset
int getBodyOffset()
獲取請求body開始時的偏移量(索引值),常使用如request[analyzedRequest.getBodyOffset():].tostring()
獲取整個請求body
IResponseInfo
HTTP響應對象
getStatusCode
short getStatusCode()
獲取響應狀態碼
getHeaders
java.util.List<java.lang.String> getHeaders()
以數組方式返回請求行與請求頭,如["HTTP/1.1 200 OK", "Content-Length: 1256"]
getCookies
java.util.List<ICookie> getCookies()
獲取服務器返回的Set-Cookie字段的ICookie對象列表
getStatedMimeType
java.lang.String getStatedMimeType()
獲取響應頭中標注的 Content-Type,注意此處返回的是string類型,與 IRequestInfo 中的稍有區別
getInferredMimeType
java.lang.String getInferredMimeType()
獲取BURP自動判斷響應內容的Content-Type
getBodyOffset
int getBodyOffset()
獲取響應body開始時的偏移量(索引值),常使用如response[analyzedResponse.getBodyOffset():].tostring()
獲取整個響應body
IParameter
用於獲取請求參數的詳情,Key和Value等。This interface is used to hold details about an HTTP request parameter,由官方文檔可以看出來,其並未考慮目前前后端分離網站的情況,未提供獲取響應中的參數,因此只能直接寫獲取響應參數的方法
getName
java.lang.String getName()
獲取參數名稱
getValue
java.lang.String getValue()
獲取參數值
getType
byte getType()
檢索參數類型,比如說是JSON內的參數還是URL里傳輸的參數,具體可以該類中的靜態變量對比
參數名索引
int getNameStart()
int getNameEnd()
獲取參數名開始與結束的偏移量(索引)
值索引
int getValueStart()
int getValueEnd()
獲取參數值開始與結束的偏移量(索引)
IHttpHeader
getName
java.lang.String getName()
獲取請求頭名稱
getValue
java.lang.String getValue()
獲取請求頭的值
ICookie
getName
java.lang.String getName()
獲取Cookie名稱
getValue
java.lang.String getValue()
獲取Cookie值
getDomain
java.lang.String getDomain()
獲取Cookie適用的域名
getPath
java.lang.String getPath()
獲取Cookie使用的PATH
getExpiration
java.util.Date getExpiration()
獲取Cookie過期時間
IHttpService
getHost
java.lang.String getHost()
獲取服務器域名
getPort
int getPort()
獲取服務器端口
getProtocol
java.lang.String getProtocol()
獲取服務器使用的協議,HTTP、HTTPS
監聽類
監聽類API只有一個方法,並且會在一定的條件下自動觸發
IProxyListener
Proxy 消息監聽類。使用
IBurpExtenderCallbacks.registerProxyListener()
方法注冊該監聽器后,便可監聽所有流經Proxy的請求與響應
所有請求/響應均會被
processProxyMessage
void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message)
# messageIsRequest 表示此次處理的是請求還是響應
# IInterceptedProxyMessage message 表示一個被攔截HTTP消息對象,可通過該對象的 getMessageInfo()方法獲取到具體的請求、響應詳情信息
方法捕獲到,然后在其中完成用戶自定的處理行為
例如:我們想要查看所有請求中是否有使用shiro的系統,並將該請求高亮顯示,便可以這樣實現
圖片是網上找的,沒太多時間重新搭環境
#coding=utf-8
from burp import IBurpExtender
from burp import IProxyListener
import sys
if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
class BurpExtender(IBurpExtender,IProxyListener):
def registerExtenderCallbacks(self,callbacks):
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("FindShiro")
callbacks.registerProxyListener(self)
def processProxyMessage(self,messageIsRequest,message):
# 我們只用判斷 響應 頭中是否有 rememberMe 字段就可以了
if not messageIsRequest:
RepReq = message.getMessageInfo() # 獲取請求與響應
Rep_B = RepReq.getResponse() # 獲取響應,byte數據類型
Rep = self._helpers.analyzeResponse(Rep_B) # 轉換為IResopnse對象
cookies = Rep.getCookies() # 獲取所有cookie
for cookie in cookies:
if cookie.getName() == "rememberMe": # 判斷cookie中是否有rememberMe,有的話就將請求高亮顯示
RepReq.setHighlight("red")
可以看到,我們甚至都不需要自己寫一些函數來處理請求響應,BURP API提供了很多常用的類與方法
百度主站並沒有shrio哈,此處是為了方便插件測試的時候所以判斷是不是有 BDSVRTM 這個cookie
IHttpListener
HTTP 消息監聽類。使用
IBurpExtenderCallbacks.registerHTTPListener()
方法注冊該監聽器后,便可監聽所有請求與響應,包括Proxy、Repeater、Intruder等等所有的 made by any Burp tool 的流量
請求響應會被
processHttpMessage
void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
# toolFlag表示來源,可在 IBurpExtenderCallbacks 類中看到
方法捕獲到,使用toolFlag判斷來源,如
#coding=utf-8
from burp import IBurpExtender
from burp import IHttpListener
import sys
if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
class BurpExtender(IBurpExtender,IHttpListener):
def registerExtenderCallbacks(self,callbacks):
self._callbacks = callbacks
callbacks.setExtensionName("PlaceYoucame")
callbacks.registerHttpListener(self)
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
if toolFlag == self._callbacks.TOOL_PROXY: # 表示來自Proxy模塊
print("Proxy")
elif toolFlag == self._callbacks.TOOL_REPEATER: # 表示來自Repeater模塊
print("Repeater")
else:
print("Others")
IExtensionStateListener
插件狀態監聽類。通過調用 IBurpExtenderCallbacks.registerExtensionStateListener() 來注冊一個擴展狀態監聽器。 偵聽器將收到擴展狀態更改的通知。 注意:任何啟動后台線程或打開系統資源(例如文件或數據庫連接)的擴展都應該注冊一個偵聽器並在卸載擴展時終止線程/關閉資源。
當插件被卸載時會調用如下方法:
注意:退出BURP時也會調用哦
extensionUnloaded
void extensionUnloaded()
通常我們會在其中完成善后工作,如
#coding=utf-8
from burp import IBurpExtender
from burp import IExtensionStateListener
import sys
if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
class BurpExtender(IBurpExtender,IExtensionStateListener):
def registerExtenderCallbacks(self,callbacks):
callbacks.setExtensionName("Unload")
callbacks.registerExtensionStateListener(self)
print("Link Start!")
def extensionUnloaded(self):
print("主人,你忍心么?還不快插上?")
當然我們也可以做一個自動導入&保存BURP配置的插件
#coding=utf-8
from burp import IBurpExtender
from burp import IExtensionStateListener
import sys
import json
if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
class BurpExtender(IBurpExtender,IExtensionStateListener):
def registerExtenderCallbacks(self,callbacks):
self._callbacks = callbacks
callbacks.setExtensionName("SaveConfig")
callbacks.registerExtensionStateListener(self)
try:
with open("config.json","r") as f:
config = f.read()
callbacks.loadConfigFromJson(config)
except:
pass
def extensionUnloaded(self):
config = self._callbacks.saveConfigAsJson(os.getcwd())
with open("config.json","w") as f:
f.write(config)
從此媽媽再也不用擔心我忘記保存配置了
IScannerListener
過段時間再寫吧
IScopeChangeListener
過段時間再寫吧
UI類
IContextMenuInvocation
當 Burp 使用上下文菜單調用的詳細信息調用擴展提供的 IContextMenuFactory 時,將使用此接口。 自定義上下文菜單工廠可以查詢此接口以獲取調用事件的詳細信息,以確定應顯示哪些菜單項。
getToolFlag
int getToolFlag()
獲取點擊右鍵動作是在哪個模塊內觸發的,Proxy、Repeater等
getInvocationContext
byte getInvocationContext()
獲取點擊右鍵動作是在哪里觸發的(詳細信息),具體可對比
getSelectedMessages
IHttpRequestResponse[] getSelectedMessages()
獲取右鍵內容的的HTTP請求響應(IHttpRequestResponse對象)
getSelectedIssues
IScanIssue[] getSelectedIssues()
獲取右鍵內容的的掃描IScanIssue對象
注意:至於什么時候使用getSelectedMessages或getSelectedIssues,可根據getInvocationContext獲取的結果加以判斷
IContextMenuFactory
注冊右鍵菜單
該類只有一個方法
createMenuItems
java.util.List<javax.swing.JMenuItem> createMenuItems(IContextMenuInvocation invocation)
當用戶在 Burp 內的任何位置調用上下文菜單(右鍵)時,該方法將被 Burp 調用
比方說我想實現一個這樣的右鍵菜單,完成一些自定義的事情
那便可以這樣寫:
#coding=utf-8
from burp import IBurpExtender
from burp import IContextMenuFactory
from javax.swing import JMenuItem
import sys
if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
class BurpExtender(IBurpExtender, IContextMenuFactory):
def registerExtenderCallbacks(self,callbacks):
self._callbacks = callbacks
callbacks.setExtensionName("myMenu")
callbacks.registerContextMenuFactory(self)
def createMenuItems(self, invocation):
if invocation.getToolFlag() == self._callbacks.TOOL_REPEATER or self._callbacks.TOOL_PROXY:
menu = []
menu.append(JMenuItem("PrintHOST", None, actionPerformed=lambda x, y=invocation: self.myFunc(x, y)))
return menu
def myFunc(self,event,invocation):
reqreps = invocation.getSelectedMessages()
for reqrep in reqreps:
print(reqrep.getHttpService().getHost())