BurpSuite外掛開發指南之 Python 篇

wyzsk發表於2020-08-19
作者: Her0in · 2016/06/06 11:41

此文接著 《BurpSuite外掛開發指南之 Java 篇》 。在此篇中將會介紹如何使用 Python程式語言 開發 BurpSuite 的外掛。

《BurpSuite 外掛開發指南》系列文章如下:

注:此係列文章是筆者利用業餘時間所寫,如有錯誤,望讀者們及時指正,另外此係列文章屬於入門級別的科普文,目的是普及Burp外掛的編寫技術。

0x00 Jython 簡介


BurpSuite 是使用 Java 程式語言編寫的,所以想要使用 Python 程式語言開發其外掛,就必須藉助於 Jython。Jython 本質上是一個 Java 應用程式,它允許 coder 們使用 Java 程式碼呼叫 Python 庫反之,也可以使用 Python 呼叫 Java 的庫。

有關 Jython 的詳細使用,請讀者參閱 Jython 官網的使用者手冊 和 相關 doc。

類似於 Jython 的 Project 還有 JRuby ,並且 Burp 也支援 ruby 編寫外掛,但是無論是用 Python 還是 Ruby 編寫的外掛,在執行效率方面遠遠不如原生的 Java 高,所以筆者還是建議使用 Java 編寫外掛。

0x01 Python 編寫 Burp 外掛


Python 編寫 Burp 外掛輔助工具

Jython Burp API

使用 Python 編寫 Burp 外掛的時候會遇到各種瑣碎的麻煩。最主要的原因在於,Java 與 Python 程式語言特性上的差異,如:強弱型別,資料型別等以及 Jython 本身與 CPython 的一些不同(具體請看 Jython 官網文件)。不過在熟悉了 Burp 介面和基本的編寫套路後,一切都會變得很簡單。

國外有牛人編寫了一個 Jython Burp API,封裝了一些功能,可以很方便的獲取 Burp 資料並且可以除錯 Jython 程式碼。具體使用說明請看 Git 文件。

Jython Burp API Git 地址

注:

載入 Jython Burp API 時會出現 sys 模組 PS1 PS2 未定義的錯誤。後來 Google 後發現這個是 Jython 本身的一個 Bug,不過官方已有 Issue 會在後續的版本中進行修復。

解決此錯誤的方法如下:

編輯 jython-burp-api/Lib/gds/burp/console/console.py 檔案,將 25 26 行直接改為如下程式碼即可:

Python 編寫 Burp 外掛 注意事項

Python 匯入相關庫

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

PermGen space 錯誤

在 Burp 載入 Python 編寫的外掛時,會經常遇到如下圖所示的錯誤:

Burp 官網也給出瞭解決 java.lang.OutOfMemoryError: PermGen space 錯誤的辦法。
在啟動 Burp 時設定 JVM 的 XX 引數即可,如: java -XX:MaxPermSize=1G -jar burp.jar

不過即使是使用上述引數啟動 Burp,在多次載入 Python 編寫的外掛後,還是會出現 Burp 卡死的現象。

Burp 載入 Python 編寫的外掛的方法

Python 編寫的外掛檔案字尾為 py 檔案,不能由 Burp 直接載入,所以在載入前需要先設定 Jython 的路徑。

在 Jython 官方下載頁面選擇 Jython 獨立 jar 包。下載好後,按照下圖所示設定:

載入 Python 外掛的方式如下圖:

Python 編寫 Burp GUI 外掛例項

本例項使用 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 編寫還是比較輕鬆的事情,不用關心太多的引數及引數型別,繫結事件也更加簡單。

0x02 Python 編寫 Burp 外掛例項之 工具整合選單


本小節會使用一個工具整合右鍵選單的 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"
}]
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章