<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/14684

            0x00 前言


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

            0x01開發環境


            1 軟件配置

            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]http://drops.wooyun.org/tools/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 參考資料


            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线