chrome 插件
chrome 插件是向 chrome 瀏覽器添加或修改功能的瀏覽器拓展程序。一般通過 JavaScript, html 以及 css 就可以編寫 chrome 插件了。市面上有很多非常優秀的 chrome 插件擁有非常多的用戶。chrome 插件的編寫也比較簡單,基本上你熟悉一點前端知識,然后熟悉一下 chrome 插件的 api,你就可以編寫 chrome 插件。
Chrome 插件的安裝,如果你沒有發布在 Chrome 商店的話(因為網絡原因,可能沒辦法直接從商店下載),可以通過開發者模式安裝 Chrome 插件。或者你也可以注冊 Chrome 插件的開發者賬號(只需要 5 美元,就可以發布 20 個插件)。
簡單地介紹了一下 Chrome 插件的開發,咱們主要還是聊一下關于 Chrome 插件關于被動掃描器的方面的內容。對于 Chrome 插件,主要是通過插件的能力去獲取經過瀏覽器的流量,并將流量轉發給后端來進行處理。
Chrome 插件關于網絡流量的處理地 API 主要有兩個:chrome.devtools.network?以及?chrome.devtools.network。但是前者使用的時候需要打開 Chrome 開發者工具,這個有一點不太方面,所以選擇了后者,這也是對于被動流量獲取一種常見的方式。
Chrome 插件中的 webrequest API 是以相應的事件驅動的,其中請求的生命周期圖如下,主要有7個事件。只需要監聽關鍵事件進行處理就可以滿足被動掃描器獲取流量的需求了。
其實這些事件不難理解,基本通過事件的名稱就可以知道事件的含義了,主要就是請求發送前,發送請求頭之前,發送請求頭等等事件。對于不同的事件,可以獲取的流量數據也是不盡相同的。
首先,考慮一下,對于被動掃描器來說,哪些流量數據是比較關心的。被動掃描器主要是通過收集業務的正常流量來進行測試,提高測試的效率,并能取得比主動掃描器更好的效果。那么一般來說,被動掃描器最關心的就是請求的 URL 以及請求頭了,如果是 POST 請求,還需要請求體。對于掃描器來說,響應頭和響應體則沒那么重要,其實可以通過響應狀態過濾一下,一般只需要能夠正常響應的請求頭以及請求體即可。
對于被動掃描器上述的需求,chrome.webrequest 中的 onBeforeRequest 以及 onSendHeaders 這兩個事件可以滿足需求。通過前者,可以獲取請求體。通過后者則可以獲取請求頭。不過在使用 onSendHeaders 的時候,有好幾點需要注意:
兼容問題
從 Chrome 79 開始,必須要在 opt_extraInfoSpec 中指定 extraHeaders 才可以獲取 Origin 請求頭。從 Chrome 72 開始,必須要在 opt_extraInfoSpec 中指定 extraHeaders 才可以獲取 以下請求頭:
Accept-Language
Accept-Encoding
Referer
Cookie
毫無疑問,這些請求頭都是有價值的。為了獲取這些請求頭,你必須在 opt_extraInfoSpec 中指定 extraHeaders 才可以獲取相應的請求頭。同時,注意做兼容性檢查,因為之前的版本的是不需要指定的,如果你在之前版本的瀏覽器也指定了屬性,就會產生報錯。
const?headers?=?version?>=?72???["requestHeaders",?"extraHeaders"]?:?["requestHeaders"]; chrome.webRequest.onSendHeaders.addListener( beforeSendHeaderHandler,?requestFilters,?headers )
requestBody 的格式問題
可以通過 onBeforeRequest 事件來獲取 POST 請求中的請求體。但有一點注意,chrome.webrequest 中把請求體進行了解析,所以你獲取的不是原生的請求體。請求體位于 requestBody 中的 fromData,而 formData 其實是有兩種形式,一種是鍵值對形式的字典,這種一般對于請求體類型為?multipart/form-data?或者?application/x-www-form-urlencoded?而言,一般即為?a=xxx&b=xxx&c=xxx?這種形式;另外一種則是原生的字節,這個官方的 API 文檔沒有直接提到,你需要自己手工解析數據。
const?postbody?=?decodeURIComponent(String.fromCharCode.apply(null,?????????????????????????????????????? new?Uint8Array(details.requestBody.raw[0].bytes)));
使用 RequestFilter 去過濾請求
如果你希望在事件中可以過濾特定的請求地址或者請求的資源類型,那么就可能需要使用到 RequestFilter 了。RequestFilter 里面有4個屬性,比較重要的屬性就是 urls 以及 types,通過這兩個屬性就可以過濾特定的請求 URL 以及資源類型。
但是注意一點是,RequestFilter 是在注冊事件的時候配置的參數的,不可以后續直接修改。不過有一種方法是先移除監聽事件,再添加新的事件。
if?(!chrome.webRequest.onSendHeaders.hasListener(beforeSendHeaderHandler))?{ ??chrome.webRequest.onSendHeaders.addListener( ????beforeSendHeaderHandler,?requestFilters,?headers ??) }
Burp 插件篇
Burp 是滲透測試中不可缺少的工具之一,而 Burp 插件也讓測試者如虎添翼,達到事半功倍的效果。同時,開發 Burp 插件也是為了彌補一些系統無法在 Chrome 中使用的場景來進一步地補充。
Burp 插件開發的資料網上不是特別的豐富,之前也寫過一篇文章“如何寫一個 Burp 插件”。其實開發 Burp 插件比較簡單,只要遵守基本的規范,然后學習一下 API 的使用,基本就可以完成 Burp 插件的開發了。反倒是如果希望在 Burp 插件中開發 GUI 有點困難,因為使用 J**A 來寫 GUI 比較麻煩,畢竟不能像 C# 那樣,妥妥拽拽就搞定了,不過這也不是本文的重點。
其實在 Burp 中的 Extender 標簽頁中的 APIs 就可以看到提供的 API 接口。基本上每個函數都有參數說明的注釋,不過其實學習 Burp 插件的最好的方法就是拿一個現成的插件代碼看一下,就可以很好地理解這些 API 的作用了。
public?interface?IhttpListener{????/** ?????*?This?method?is?invoked?when?an?HTTP?request?is?about?to?be?issued,?and ?????*?when?an?HTTP?response?has?been?received. ?????* ?????*?@param?toolFlag?A?flag?indicating?the?Burp?tool?that?issued?the?request. ?????*?Burp?tool?flags?are?defined?in?the ?????*?<code>IBurpExtenderCallbacks</code>?interface. ?????*?@param?messageIsRequest?Flags?whether?the?method?is?being?invoked?for?a ?????*?request?or?response. ?????*?@param?messageInfo?Details?of?the?request?/?response?to?be?processed. ?????*?Extensions?can?call?the?setter?methods?on?this?object?to?update?the ?????*?current?message?and?so?modify?Burp's?behavior. ?????*/ ????void?processHttpMessage(int?toolFlag,????????????boolean?messageIsRequest, ????????????IHttpRequestResponse?messageInfo); }
在這,以我開發的 Burp 插件?r-forwarder-burp?為例,使用 J**A 開發。在開發 Burp 插件需要注意幾點。必須定義一個 BurpExtender 類,并且必須實現 IBurpExtender,如果還需要其他 API 可以實現多個其它接口,J**A 中的類是可以實現多個接口的。另外還需要重寫父類中的 registerExtenderCallbacks 方法。同樣,針對被動掃描器的需求,在 Burp 插件中我們最主要涉及的接口是 IHttpListener 接口。這個主要涉及到 HTTP
在 processHttpMessage 方法中,主要涉及到以上3個參數。toolFlag 主要指的是和請求相關的 Burp 工具,比如 Proxy 以及 Repeater。可以在 IBurpExtenderCallbacks 接口中看到相應的定義。
messageIsRequest 則表示是請求還是響應,而我們只關心請求部分。通過解析 messageInfo 則可以獲取請求頭以及請求體數據。
public?Map<string>?getHeaders(IHttpRequestResponse?messageInfo)?{???? Map<string>?headers?=?new?HashMap(); ????IRequestInfo?analyzeRequest?=?helpers.analyzeRequest(messageInfo); ????List<string>?h?=?analyzeRequest.getHeaders();????for?(String?h1:?h)?{???????? if?(h1.startsWith("GET")?||?h1.startsWith("POST"))?{????????????continue; ????????}?else?{????????????String[]?header?=?h1.split(":",?2); ????????????headers.put(header[0],?header[1].trim()); ????????} ????}????return?headers; } private?String?getBody(IHttpRequestResponse?messageInfo)?{ ????IRequestInfo?requestInfo?=?helpers.analyzeRequest(messageInfo); ????int?bodyOffset?=?requestInfo.getBodyOffset(); ????byte[]?byteRequest?=?messageInfo.getRequest(); ????byte[]?byteBody?=?Arrays.copyOfRange(byteRequest,?bodyOffset,?byteRequest.length);???? return?new?String(byteBody); }</string></string></string>
上面是簡單開發的內容方面的介紹,其它方面可以直接看源代碼了解更多,尤其是 GUI 開發的部分。另外想說明的一點就是如何打 jar 包。通過 maven-assembly-plugin 插件可以很方便地打包,只需要配置如下,然后通過?mvn package?即可進行打包。
<plugin> ????<groupid>org.apache.maven.plugins</groupid> ????<artifactid>maven-assembly-plugin</artifactid> ????<executions> ????????<execution> ????????????<phase>package</phase> ????????????<goals> ????????????????<goal>single</goal> ????????????</goals> ????????</execution> ????</executions> ????<configuration> ????????<descriptorrefs> ????????????<descriptorref>jar-with-dependencies</descriptorref> ????????</descriptorrefs> ????</configuration></plugin>
另外注意如果使用了外部依賴的時候,需要配置?jar-with-dependencies,這樣在打包的時候就可以把依賴的 jar 包一并打進去。最后,成品的 jar 包安裝之后就可以使用了。
其實,我認為在 Burp 插件開發過程中最重要的部分就是調試了。通過調試可以快速提高開發效率。以 ide idea 為例,只需要以下幾步就可以進行插件開發地調試:
1.配置 debug 配置項,點擊 IDE 右上角就可以新增配置項。
在終端中通過上述的配置項啟動 burp 插件。
java?-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005?-jar?burpsuite_community_v2.1.0.jar
2.在 Burp 中通過上面的方式安裝打包好的插件。
3.在 IDE 中相應的代碼打上斷點,并打開 debug 就可以進行調試了。
總結