Burp Suite插件开发-Python
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())