此文接著 《BurpSuite插件開發指南之 Java 篇》 。在此篇中將會介紹如何使用 Python編程語言 開發 BurpSuite 的插件。
《BurpSuite 插件開發指南》系列文章如下:
注:此系列文章是筆者利用業余時間所寫,如有錯誤,望讀者們及時指正,另外此系列文章屬于入門級別的科普文,目的是普及Burp插件的編寫技術。
BurpSuite 是使用 Java 編程語言編寫的,所以想要使用 Python 編程語言開發其插件,就必須借助于 Jython。Jython 本質上是一個 Java 應用程序,它允許 coder 們使用 Java 代碼調用 Python 庫反之,也可以使用 Python 調用 Java 的庫。
有關 Jython 的詳細使用,請讀者參閱 Jython 官網的用戶手冊 和 相關 doc。
類似于 Jython 的 Project 還有 JRuby ,并且 Burp 也支持 ruby 編寫插件,但是無論是用 Python 還是 Ruby 編寫的插件,在執行效率方面遠遠不如原生的 Java 高,所以筆者還是建議使用 Java 編寫插件。
使用 Python 編寫 Burp 插件的時候會遇到各種瑣碎的麻煩。最主要的原因在于,Java 與 Python 編程語言特性上的差異,如:強弱類型,數據類型等以及 Jython 本身與 CPython 的一些不同(具體請看 Jython 官網文檔)。不過在熟悉了 Burp 接口和基本的編寫套路后,一切都會變得很簡單。
國外有牛人編寫了一個 Jython Burp API,封裝了一些功能,可以很方便的獲取 Burp 數據并且可以調試 Jython 代碼。具體使用說明請看 Git 文檔。
注:
加載 Jython Burp API 時會出現 sys 模塊 PS1 PS2 未定義的錯誤。后來 Google 后發現這個是 Jython 本身的一個 Bug,不過官方已有 Issue 會在后續的版本中進行修復。
解決此錯誤的方法如下:
編輯 jython-burp-api/Lib/gds/burp/console/console.py 文件,將 25 26 行直接改為如下代碼即可:
Python 實現接口的方式與 Python 中類的繼承寫法一樣。只是讀者要注意的是:在 Java 中,類是單繼承的,一個子類直接繼承的父類只能有一個,可以通過間接的方式實現多繼承,但 Python 中是可以直接繼承多個類。
Python 編寫 Burp 插件的示例代碼如下:
#!python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
BurpSuite插件開發指南之 Python 篇
'''
# 導入 burp 接口
from burp import IBurpExtender, IProxyListener
# 導入 Java 庫
from java.net import URL
# 按照 Python 類繼承的方式實現相關接口
class BurpExtender(IBurpExtender, IProxyListener):
def registerExtenderCallbacks(self, callbacks):
# code here
pass
def processProxyMessage(self, messageIsRequest, message):
pass
在 Burp 加載 Python 編寫的插件時,會經常遇到如下圖所示的錯誤:
Burp 官網也給出了解決 java.lang.OutOfMemoryError: PermGen space 錯誤的辦法。
在啟動 Burp 時設置 JVM 的 XX 參數即可,如: java -XX:MaxPermSize=1G -jar burp.jar
不過即使是使用上述參數啟動 Burp,在多次加載 Python 編寫的插件后,還是會出現 Burp 卡死的現象。
Python 編寫的插件文件后綴為 py 文件,不能由 Burp 直接加載,所以在加載前需要先設置 Jython 的路徑。
在 Jython 官方下載頁面選擇 Jython 獨立 jar 包。下載好后,按照下圖所示設置:
加載 Python 插件的方式如下圖:
本實例使用 Python 調用 Java 的 swing 圖形控件庫并綁定相關事件。最終結果如下圖:
示例代碼如下:
#!python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
BurpSuite插件開發指南之 Python 篇
'''
# 導入 burp 接口
from burp import IBurpExtender, ITab
# 導入 Java 庫
from javax.swing import JPanel
from javax.swing import JButton
class BurpExtender(IBurpExtender, ITab):
def registerExtenderCallbacks(self, callbacks):
self._cb = callbacks
self._hp = callbacks.getHelpers()
self._cb.setExtensionName('BurpPython')
print 'hello burp!'
self.mainPanel = JPanel()
# 初始化一個 JButton 并綁定單擊事件
self.testBtn = JButton('Click Me!', actionPerformed=self.testBtn_onClick)
self.mainPanel.add(self.testBtn)
self._cb.customizeUiComponent(self.mainPanel)
self._cb.addSuiteTab(self)
def testBtn_onClick(self, event):
print 'testBtn clicked!'
# 實現 ITab 接口的 getTabCaption() 方法
def getTabCaption(self):
return 'BurpPython'
def getUiComponent(self):
return self.mainPanel
相比較 Java 編寫 GUI 插件,如果要實現比較復雜的 GUI,使用 Python 編寫還是比較輕松的事情,不用關心太多的參數及參數類型,綁定事件也更加簡單。
本小節會使用一個工具集成右鍵菜單的 Burp 插件舉例說明 Python 編寫 Burp 插件的套路。
注:
讀者可以在此插件的基礎上修改為任何你想要執行的命令或程序 并指定不同的參數,如:使用 請求原始數據配合 SQLMap 進行SQLi 測試。另外在使用該插件過程時,可以將輸出設置為系統控制臺輸出,如下圖所示:
代碼和配置文件內容如下:
#!python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
BurpSuite插件開發指南之 Python 篇
'''
import os
import sys
import json
import thread
import traceback
# 導入 burp 相關接口
from burp import IBurpExtender
from burp import IContextMenuFactory
# 導入 Java 相關庫
from javax.swing import JMenu
from javax.swing import JMenuItem
reload(sys)
sys.setdefaultencoding('utf-8')
class BurpExtender(IBurpExtender, IContextMenuFactory):
def registerExtenderCallbacks(self, callbacks):
self.messages = []
self.menusConf = {}
self.callbacks = callbacks
self.helpers = callbacks.getHelpers()
self.callbacks.issueAlert('toolKits is ready ...')
self.callbacks.setExtensionName('toolKits')
self.callbacks.registerContextMenuFactory(self)
def loadMenus(self):
self.menus = []
self.mainMenu = JMenu("toolKits")
self.menus.append(self.mainMenu)
try:
with open('toolKits/toolKits.conf') as fp:
self.menusConf = json.loads(fp.read())
except:
self.mainMenu.add(JMenuItem(u'加載配置出錯!'))
else:
for tool in self.menusConf:
# 遍歷配置,創建子菜單項,并添加事件綁定
menu = JMenuItem(tool['name'],
None,
actionPerformed=lambda x: self.eventHandler(x))
self.mainMenu.add(menu)
def createMenuItems(self, invocation):
# 將加載的過程放在 createMenuItems 接口方法中
# 可以在不重新加載該插件的情況下,動態加載配置
self.loadMenus()
self.messages = invocation.getSelectedMessages()
# 只在指定的 Burp 標簽的右鍵菜單顯示
# ctx = invocation.getInvocationContext()
# if not ctx in [0, 1, 2, 3, 4, 5, 6]:
# return None
return self.menus if self.menus else None
def eventHandler(self, x):
'''
通過獲取當前點擊的子菜單的 text 屬性,確定當前需要執行的 command
啟動線程執行命令
'''
try:
menuName = x.getSource().text
for tool in self.menusConf:
if tool['name'] == menuName:
commands = [tool['command'].replace(
'{#}', val) for val in self.getValue(tool['param'])]
[thread.start_new_thread(self.execCommand, (command,))
for command in commands]
except:
print traceback.print_exc()
def getHost(self, message):
return message.getHttpService().getHost()
# 獲取 Url 注意此處若通過 meesage.getRequest() 是獲取不到的
def getUrl(self, meesage):
return str(self.helpers.analyzeRequest(meesage).getUrl())
# 通過配置中的 參數值 分別獲取不同值
def getValue(self, paramType):
if paramType == 'host':
return set([self.getHost(message) for message in self.messages])
elif paramType == 'url':
return set([self.getUrl(message) for message in self.messages])
# 執行命令處理方法
def execCommand(self, command):
try:
print '[I] 正在執行命令: {command}, 請稍后...'.format(command=command)
res = '---------- 命令 {command} 執行結果: ---------- {res}'.format(
command=command, res=os.popen(command).read())
print res
except:
print traceback.print_exc()
該插件有一個配置文件,格式為 JSON 格式(Jython 2.7.0 不支持 yaml):
#!javascript
[{
"name": "Nmap 掃描端口",
"param": "host",
"command": "nmap -T4 {#}"
},
{
"name": "SQLMap 檢查注入",
"param": "url",
"command": "python /opt/sqlmap/sqlmap.py -u {#} --dbs"
}]