基於Ruby的Burpsuite外掛開發

wyzsk發表於2020-08-19
作者: 等TA回來 · 2016/04/27 14:30

0x00 前言


BurpSuite是久負盛名的web應用安全測試工具,在安全圈子中被譽為“神器”。它的核心功能是http代理,並在此基礎上提供了豐富的自定義配置選項,協助使用者設計不同的方案進行滲透或者安全監測。此外,除了工具本身提供的功能以外,burpsuite神器提供了一組java編寫的應用介面,透過java或基於java的Jython、Jruby,可以實現許多自定義的功能外掛。在工作中,筆者嘗試使用Ruby進行BurpSuite Extention的外掛開發,這個過程有一些坑需要同特定的方式解決,希望能和大家分享這點經驗。在這裡,不針對具體業務,不討論程式語言優劣,只希望能夠豐富這個生態系統,讓Burp的愛好者們多一種選擇。

0x01開發環境


1 軟體配置

  • Macbook pro OSX 10.10.5
  • Burpsuite free edit 1.6.03
  • Jruby 1.7.19
  • rvm 1.26.10
  • gem 2.0.14

2 安裝

在Unix/Linux環境下安裝Ruby程式語言,很可能會遇到多個版本共存的情況。RVM(Ruby Version Manager)這個小工具很好的解決了這個問題,使用這個工具可以方便的安裝和管理作業系統上覆雜的Ruby版本,強烈推薦新手使用。

2.1 安裝RVM

RVM的安裝步驟非常簡單,透過使用curl命令下載一個指令碼,同時使用‘bash -s’執行,最後重新載入profile檔案即可。官方的安裝說明:https://rvm.io/rvm/install

#!bash
$ curl -sSL https://get.rvm.io|bash -s

正確安裝後會有如下提示

#!bash
In case of problems: http://rvm.io/help and https://twitter.com/rvm_io

  * WARNING: You have '~/.profile' file, you might want to load it,
    to do that add the following line to '/home/wang.zhaofeng/.bash_profile':

      source ~/.profile

此時在命令列下執行

#!bash
$ source ~/.profile 

執行如下命令測試安裝是否成功,如果顯示版本說明,則安裝成功。

#!bash
$ rvm -v

2.2 安裝Jruby

Burpsuite使用Java開發,因此無論使用python還是ruby進行外掛開發,都必須有java實現的直譯器(即Jython和Jruby)。

安裝jruby並建立針對burpsuite的環境變數。

#!bash
$ rvm install jruby
$ rvm --ruby-version use [email protected] --create

執行‘rvm list’ 檢視是否安裝成功。

#!bash
$ rvm list

當看到如下顯示說明安裝成功。

#!bash
rvm rubies

=> jruby-1.7.19 [ x86_64 ]

# Default ruby not set. Try 'rvm alias create default <ruby>'.

# => - current
# =* - current && default
#  * - default

為了穩定和向下相容,我使用了比較陳舊的1.7版,經測試,最新的9.x版也是沒有問題的。(不知道這個版本是怎麼命名的。)

2.3 BurpSuite外掛環境配置

軟體安裝成功後,你可以在個人目錄的.rvm/rubies/jruby-1.7.19目錄下找到你的Jruby目錄,在這個目錄下,會有一個Jruby.jar的檔案,記住它的位置。

如果不能手動找到,可以嘗試執行以下命令:

#!bash
$ rvm env|grep MY_RUBY_HOME

MY_RUBY_HOME將作為這個jar檔案路徑的環境變數名。

#!bash
export MY_RUBY_HOME='/Users/myname/.rvm/rubies/jruby-1.7.19'

單引號中的路徑,就是jruby.jar的存放路徑。

執行以下命令

#!bash
rvm use jruby

讓jruby變成當前的操作環境,就可以找到對應的環境變數。

下面是非常重要的一步!

下面是非常重要的一步!

下面是非常重要的一步!

開啟命令列,輸入以下命令:

#!bash
JRUBY_HOME=/Users/myname/.rvm/rubies/jruby-1.7.19 java -XX:MaxPermSize=1G -Xmx1g -Xms1g -jar YOUR_BURP_PATH/burpsuite_free_v1.6.xx.jar

透過命令列啟動BurpSuite,而不是雙擊圖示,這一步非常重要,否則在程式碼中require庫檔案,就會出現無法載入庫檔案的情況。

開啟Burp,在Extender-->Options選項卡最下方的Ruby Environment部門匯入你的Jruby.jar檔案,沒如果沒有報錯,則說說明載入成功。

載入jruby.jar

2.4 測試

Burpsuite官方網站提供了一個測試檔案(點這裡下載

解壓縮後可以在名為ruby的目錄下面看到一個HelloWorld.rb檔案,這個就是要載入的外掛程式碼。

點選Extentders標籤下的Extention選項卡,在上半部分左側點選“Add"

新增外掛

在Extention type處選擇ruby型別,點選Select file選擇解壓出來的helloworld.rb檔案。

新增外掛2

新增到外掛3

回到主頁面點選Next,就會在彈出的控制檯視窗中顯示Hello World字元,此時全部的準備工作都已完成。

hello output burp

0x02 第一個Ruby擴充套件外掛


在開發個人的第一個外掛之前,如果有時間,看一下Burpsuite官方提供的開發者文件可以幫助開發者對介面系列的設計有更全面的認識。地址在:https://portswigger.net/burp/extender/api/index.html[email protected]/tools/?id=14040對一些主要的介面做了介紹,大家可以參考。有了上面這些準備,就可以真正開始著手開發你的Burp外掛了。

Burp的外掛開發是有固定的模式的,為了說明這個模式,請看下面幾行程式碼。

#!ruby
require 'java'
java_import 'burp.IBurpExtender'

class BurpExtender
  include IBurpExtender

  def registerExtenderCallbacks(callbacks)
    callbacks.setExtensionName("Your Extender Name")
  end
end

必須要有第一行require 'java',否則後面引入java介面類會報錯;程式碼第二行引入IBurpExtender這個Burp的java類。Burp的開發者把相關的api封裝在一個類中,在呼叫api之前需要在程式碼頂部引入這個類,同時要include這個module。這段程式碼中,registerExtenderCallbacks這個方法封裝在IBurpExtender這個類裡面。事實上,IBurpExtender這個類中只封裝了僅有的一個方法:registerExtenderCallbacks,它是一個程式的入口,有點類似於main()函式。

官方的介面文件對registerExtenderCallbacks方法做了這樣的說明:

“This method is invoked when the extension is loaded. It registers an instance of the IBurpExtenderCallbacks interface, providing methods that may be invoked by the extension to perform various actions.”

這個方法以“註冊”的方式定義了擴充套件外掛中可用的例項(型別)。每一個Burp外掛都必然包含上面這幾行程式碼,少了一行都不行。

現在,我們設計一個最簡單的功能:在BurpSuite Extender選項卡中的Output對話方塊輸出監聽到的HTTP請求,透過這個實現這個功能來讓讀者體會到Burp外掛開發的大概流程。

我們需要在剛才的程式碼上稍加變化:首先,我們使用一個名叫processHttpMessage()的方法,透過查詢開發手冊,我們發現這個方法封裝在一個名叫IHttpListener的模組中,於是在程式碼開頭新增一行"java_import 'burp.IHttpListener'",同時在BurpExtender類中include這個叫IHttpListener的module,具體做法可見下面的示例程式碼(另一種寫法是在"include IBurpExtender"這行下面另起一行"include IHttpListener")。

然後在"registerExtenderCallbacks"方法中加入一行"callbacks.registerHttpListener(self)",這是告訴引擎,這個外掛被當做一個HTTP監聽器來使用。

在burp.IHttpListener這個模組中,processHttpMessage是僅有的一個方法,我們在BurpExtender這個類中重寫這個方法。官方開發手冊對這個方法有如下定義:

#!cpp
void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)

其中toolFlag代表功能的旗標常數,每個常數代表的元件是固定的,可以在文件中查到;messageIsRequest表示HttpListener監聽到的資料是否是一個請求;messageInfo是一個IHttpRequestResponse物件,它有多個例項方法,詳細的使用方法可以在開發文件中找到。processHttpMessage方法透過引數把HTTP請求或響應的資料傳遞進來,開發者可以根據自己的需要對其進行處理。

我們使用get_request()方法獲取請求物件(getRequest是這個方法的別名)。此時Http包的資料是不能直接輸出的,在除錯的過程中,筆者對這個物件使用了methods方法獲取它的所有例項方法,最後使用了to_s方法對資料的內容直接輸出(需要注意的是,不同版本的jruby的to_s操作可能會有所不同)。這麼做的原因是,get_request()獲取到的資料物件不是文字,而是一個hash的子類,透過to_s方法把物件轉換成String輸出。

程式碼部分:

#!ruby
require 'java'
java_import 'burp.IBurpExtender'
java_import 'burp.IHttpListener'


class BurpExtender

  include IBurpExtender,IHttpListener

  def registerExtenderCallbacks(callbacks)
    @callbacks = callbacks
    @stdout    = java.io.PrintWriter.new(callbacks.getStdout(), true)

    callbacks.setExtensionName("Your Extender Name")
    callbacks.registerHttpListener(self)

  end

  def processHttpMessage(toolFlag, messageIsRequest, messageInfo)
    if messageIsRequest
      @stdout.println(messageInfo.get_request().to_s)
    end

  end

end

將上述程式碼儲存為一個ruby檔案以後載入到BurpExtender中,在命令列中使用如下命令進行測試

#!bash
curl -x 127.0.0.1:8009 http://www.xxx.com -I

效果如圖所示:

test output

0x03 建立一個HttpMessageEditorTab例項


在HTTP History或者Repeater標籤下的每一組請求響應中都有若干個Tab介面,分別叫做Raw、Headers、Hex(在響應部分還有HTML和Render),這樣的每一個Tab都都被稱作一個Http Message Editor Tab,我們可以根據自己的需要自定義一組新的Tab,並設計每一個Tab的功能。

如前所述,我們構建新的Tab需要引入burp.IMessageEditorTab和burp.IMessageEditorTabFactory這兩個java類,所以首先在程式碼的開始部分使用java_import引入這兩個類。

#!ruby
require 'java'

java_import 'burp.IBurpExtender'
java_import 'burp.IMessageEditorTab'
java_import 'burp.IMessageEditorTabFactory'

在registerExtenderCallbacks方法的定義中將這個外掛註冊成為一個MessageEditorTabFactory,即新增一行:callbacks.registerMessageEditorTabFactory(self)。然後定義createNewInstance方法,根據開發文件的定義,這個方法需要返回一個例項化後的IMessageEditorTab物件。於是我們在這裡初始化了一個叫MakeTabs的類,後面我們只需要按照介面規範重新定義這個類即可。

#!ruby
class BurpExtender
  include IBurpExtender, IMessageEditorTab, IMessageEditorTabFactory

  def   registerExtenderCallbacks(callbacks)

    @callbacks = callbacks

    callbacks.setExtensionName("MessageEditorTab")

    @stdout = java.io.PrintWriter.new(callbacks.getStdout(), true)

    callbacks.registerHttpListener(self)
    callbacks.registerMessageEditorTabFactory(self)
  end

  def createNewInstance(controller, editable)
    MakeTabs.new(@callbacks, editable)
  end
end

下面,我們需要按照IMessageEditorTab的介面規範定義,逐個定義這個類中所有的七個方法(不含initailize方法)。至於這個Tab要實現什麼樣的功能,我們借鑑burpsuite擴充套件開發之Python中的例子,讓它顯示格式化後的json字串,透過這個非常簡單的例子讓讀者明白程式碼的邏輯。實際上,依託ruby程式語言本身的能力和BurpExtender豐富的介面,可以延伸出豐富的功能擴充套件。

initialize方法,這裡根據需要傳入callback和editable兩個引數。@stderr@stdout兩個類變數是用於除錯的錯誤輸出和標準輸出。@helper是一個輔助模組的物件,接觸它可以完成對http資料的分析。@txt_input控制文字的顯示。@editable是一個布林值。@callbacks是IBurpExtenderCallbacks物件。

getTabCaption方法返回的文字(String型別)就是Tab顯示的名稱。

getUiComponent方法返回java.awt.Component物件,[email protected]_input.get_component()獲得。

isEnabled方法返回一個布林值,可以根據條件判斷當前Tab是否顯示。

setMessage返回void型別,當isEnable返回true時,可以透過在這個方法中操作@txt_input物件控制窗體的輸出內容。假設這裡的response是一段Json資料,那麼我們透過ruby json庫的方法將其格式化輸出並丟棄響應頭,當資料流是request的時候不顯示窗體。另外,因為要用到ruby內建的json庫,所以需要在"require 'java"下面加上一行"require 'json'"。

getMessage返回byte型別,意為當前顯示的文字,可以透過@txt_input.getText()物件獲得。

isModifed返回一個布林值,它的作用是判斷當前的文字內容是否被改變過。

getSelectedData返回byte型別,它透過@txt_input.getSelectedText()獲得。

完整的程式碼:

#!ruby
require 'java'
require 'json'

java_import 'burp.IBurpExtender'
java_import 'burp.IMessageEditorTab'
java_import 'burp.IMessageEditorTabFactory'

class BurpExtender
  include IBurpExtender, IMessageEditorTabFactory

  def   registerExtenderCallbacks(callbacks)

    @callbacks = callbacks

    callbacks.setExtensionName("JsonFormater")

    @stdout = java.io.PrintWriter.new(callbacks.getStdout(), true)


    callbacks.registerMessageEditorTabFactory(self)
  end

  def createNewInstance(controller, editable)
    MakeTabs.new(@callbacks, editable)
  end
end

class MakeTabs
  include IMessageEditorTab

  def initialize(callbacks, editable)
    @stderr = callbacks.get_stderr()
    @helper = callbacks.get_helpers()
    @txt_input = callbacks.create_text_editor()
    @editable = editable
    @stdout = java.io.PrintWriter.new(callbacks.getStdout(), true)
    @callbacks = callbacks
  end

  def getTabCaption
    "MyTab"
  end

  def getUiComponent
    @txt_input.get_component()

  end

  def isEnabled(content, isRequest)
    not isRequest
  end

  def setMessage(content, isRequest)
    unless isRequest
      @txt_input.text = "HTTP Reponse is nil or empty.".to_java_bytes  if content.nil? or content.empty?
      lines= content.to_s.split("\n")
      body = ""
      lines.each_with_index{|each,index|  body = each if each.chomp =~ /^\{.*\}$/  }
      if body.size > 0
        body   = body.chomp
        begin
          hash = JSON.parse body
          body = JSON.pretty_generate hash
        rescue
          @stderr.write(" error: #{$!} at#{$@}\n".to_java_bytes)
        end
        @txt_input.text = body.to_java_bytes
      end
    end
    true
  end

  def getMessage
    return @txt_input.getText
  end

  def isModifed
    return @txt_input.text_modified?
  end

end

將上述程式碼儲存至一個ruby檔案中並在BurpExtender選項卡中載入,就可以看到如下效果:

正常的響應json資料

格式化後的json資料

0x04 總結


具備一定ruby程式設計基礎的小夥伴看懂上面的這兩段程式碼並不是難事,實際上筆者最初的想法是把自己在工作中寫的一個工具作為案例來分享,但最終放棄了這個想法。原因是考慮到這個工具跟工作的場景結合比較緊密,通用性不強所以不便於移植,另一方面這個工具程式碼量比較大,結構也較本文中講解的程式碼複雜一些,如果要詳細講解清楚可能要佔用大量篇幅,反而可能把重要的資訊淹沒。

從我個人折騰Extender的經驗來看,程式碼的編碼是一項基本功,而最初上手開始編寫外掛的時候,一些針對BurpSuite擴充套件功能的基本操作可能成為一個瓶頸——我甚至認為,這篇文章最重點的內容並不是後面的程式碼,而是第一節環境配置,因為筆者花費在從坑裡爬出來的時間甚至比寫程式碼的時間還要久。所以這裡分享的內容並沒有太大難度,僅僅是我認為可能會阻擋很多入門選手的門檻。當然這裡也只是介紹了BurpExtender使用技巧的一小部分,更多高階的用法需要在時間中慢慢積累經驗。

大家有什麼問題歡迎一起交流探討。

0x05 參考資料


本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章