0x00 前言
之前我們簡單介紹了一下掃描器中爬蟲的部分,接下來將繼續(xù)介紹掃描器中一些我們認(rèn)為比較有趣的技巧。
0x01 編碼/解碼/協(xié)議
在很久以前有人提問 AMF 格式的請求怎么進(jìn)行檢測,或者有什么工具可以檢測。
既然我們要講解的是 Web 漏洞掃描器,那么就先假設(shè)是 AMF over HTTP (這里并不需要你了解 AMF,你只需要知道 AMF 是一種數(shù)據(jù)格式類型就行)。
假設(shè)我們需要測試一個 AMF 格式數(shù)據(jù)的 SQL 注入問題,那么按照通常的思路就是在 SQL 注入模塊中:
1.先解析 HTTP 中 AMF 格式數(shù)據(jù)
2.然后在測試參數(shù)中填寫 payload
3.重新封裝 AMF 格式數(shù)據(jù)
4.發(fā)送 HTTP 請求
偽代碼如下:
1req = {"method": "POST", "url": "http://fatezero.org", "body": "encoded data"}
2data = decode_amf(req["body"])
3for key, value in data.items():
4 d = copy.deepcopy(data)
5 d[key] = generate_payload(value)
6 body = encode_amf(d)
7 requests.request(method=req["method"], url=req["url"], body=body)
整個流程下來沒什么問題,但是如果又來了一個 X 協(xié)議(X over HTTP),那么我們就得繼續(xù)修改 SQL 注入模塊以便支持這種 X 協(xié)議,但是掃描器中可不是只有 SQL 注入檢測模塊,還有其他同類模塊,難道每加一個新協(xié)議我還得把所有檢測模塊都改一遍?
所以我們需要把這些協(xié)議解析和封裝單獨抽出來放在一個模塊中。
偽代碼如下:
1# utils.py
2def decode(data):
3 if is_amf(data):
4 data = decode_amf(data)
5
6 if is_X(data):
7 data = decode_X(data)
8
9 # 遞歸 decode
10 for i in data:
11 data[i] = decode(data[i])
12
13 return data
14
15
16# detect_module.py
17req = {"method": "POST", "url": "http://fatezero.org", "body": "encoded data"}
18data = decode(req["body"])
19for key, value in data.items():
20 d = copy.deepcopy(data)
21 d[key] = generate_payload(value)
22 body = encode(d)
23 requests.request(method=req["method"], url=req["url"], body=body)
上面的遞歸 decode 主要是為了解碼某種格式的數(shù)據(jù)里面還有另外一種格式的數(shù)據(jù),雖然看起來這種場景比較少見,但是仔細(xì)想一下 multipart 帶著 json,json 里的字符串是另外一個 json 字符串,是不是又覺得這種情況也并不少見。
那 encode/decode 剝離出來就可以了嗎?請注意到上面?zhèn)未a使用了 requests. request 發(fā)送請求,那如果某天需要我們?nèi)y試 websocket 協(xié)議,是不是又得在檢測模塊中多加一套 websocket client 發(fā)送請求?
所以我們也需要將具體的網(wǎng)絡(luò)操作給剝離出來,具體的協(xié)議類型直接由上面來處理,檢測模塊只需要關(guān)注具體填寫的 payload。
偽代碼如下:
1for key, value in x.items():
2 data.reset()
3 x[key] = generate_payload(value)
4 x.do() # 負(fù)責(zé)將數(shù)據(jù)重新組裝成原來的格式,并按照原始協(xié)議發(fā)送
5
6 # check
因為每個檢測模塊的檢測依據(jù)大致就幾種:
- 返回內(nèi)容
- 消耗時間 (time based)
- 另外一條信道的數(shù)據(jù) (比方說 DNSlog)
所以即便是我們將網(wǎng)絡(luò)操作剝離出來也不會影響檢測的效果。
在編寫檢測模塊的時候,編寫者可以不用關(guān)心基礎(chǔ)協(xié)議是什么,怎么對數(shù)據(jù)編碼解碼,只用關(guān)心根據(jù) value 生成 payload 并填寫到相對應(yīng)的 key 中。
假如某天出現(xiàn)了這么一種流行編碼格式 http://www.a.com/key1, value1,key2,value2,那我們所有的檢測模塊也無需修改,僅僅需要在上一層再添加一套 encode/decode 操作即可。假如某天出現(xiàn)了一種比較流行的協(xié)議,我們也僅需要在上一層提供一套 client 即可。檢測模塊的工作就僅僅剩下生成并填寫 payload。
0x02 PoC 分類
在 2014 年的時候,我做了大量的競品分析,包括使用逆向工程逆向商業(yè)的 Acunetix WVS, HP Webinspect, IBM AppScan, Netsparker 掃描邏輯,也包括閱讀開源的 w3af, arachni 代碼。
如果不談掃描質(zhì)量,只關(guān)注整體項目設(shè)計以及產(chǎn)品中使用到的猥瑣技巧,那么其中最讓我眼前一亮的當(dāng)屬 AWVS,接下來我將詳細(xì)介紹一下我從 AWVS 中學(xué)習(xí)到的 PoC 分類。
PoC 分類:
類型描述PerServer用于檢測 Web Server 級別中存在的漏洞,比方說各種中間件,Web 框架的漏洞PerFile用于檢測某個文件中是否存在漏洞,比如對應(yīng)文件的備份,Bash RCE 等PerFolder用于檢測某個目錄中是否存在漏洞,比如敏感信息的泄漏,路徑中的 SQL 注入等PerScheme用于檢測某個參數(shù)中是否存在漏洞,比如 SQL 注入,XSS 等PostCrawl在爬蟲結(jié)束之后啟動,直接使用爬蟲的資源進(jìn)行檢測PostScan在掃描結(jié)束之后啟動,用于檢測二階注入,存儲 XSS等WebApps用于檢測比較常用的 Web 應(yīng)用的漏洞
大致的流程圖如下:
在獲取到爬蟲資產(chǎn),對相關(guān)資產(chǎn)格式化之后,便下發(fā)到各個不同類型的 PoC 中進(jìn)行檢測,這樣做的好處是分類明確,覆蓋大多數(shù)檢測階段,也避免為了減少重復(fù)請求的下發(fā)而需要額外記錄中間狀態(tài)的行為。
0x03 IAST
AWVS 有個比較有趣的功能 AcuMonitor ,也就大家熟知的 dnslog、反連平臺。在 2014 年看到 AWVS 的這個功能時,就建議 WooYun 出個類似的功能,也就是 cloudeye,tangscan 也就算是國內(nèi)比較早使用這種技術(shù)的掃描器,當(dāng)然后續(xù)又出現(xiàn)了各種類似 cloudeye 的項目,自然而然也出現(xiàn)了各種使用該技術(shù)的掃描器。
不過今天我們不打算繼續(xù)介紹 AcuMonitor,而是介紹另外一個也很有趣的功能 AcuSensor 。AcuSensor 就是IAST,只要稍微了解過 Web 漏洞掃描器的,都應(yīng)該會知道 IAST 是干啥的。那為什么我要單獨拎出來講這個呢?
主要是因為 AcuSensor 的實現(xiàn)方式非常有趣。AcuSensor 提供了 JAVA、 .NET、php 這三個語言版本,其中比較有趣的是 PHP 版本的實現(xiàn)。
PHP版本的AcuSensor使用方法是下載一個acu_phpaspect.php文件,再通過 auto_prepend_file 加載這個文件, 眾所周知,PHP 是不能改直接 hook PHP 內(nèi)置函數(shù)的,那么單單依靠一個 PHP 腳本,AcuSensor是如何做到類似 IAST 功能的呢?
很簡單,直接替換所有關(guān)鍵函數(shù)。嗯,真的就那么簡單。
我們來詳細(xì)介紹一下這個過程,在 acu_phpaspect.php 中:
1.獲取用戶實際請求的文件內(nèi)容
2.檢查一下有沒有相關(guān) cache,如果有 cache 那么直接加載執(zhí)行 cache,然后結(jié)束
3.使用 token_get_all獲取所有 token遍歷每一個 token,對自己感興趣的函數(shù)或者語句使用自己定義的函數(shù)進(jìn)行 wrap 并替換
4.將替換后的內(nèi)容保存到 cache 中并使用 eval 執(zhí)行
5.__halt_compiler中斷編譯
舉個具體的例子:
1<?php
2
3$link = NULL;
4$sql = "select * from user where user_id=".$_GET["id"];
5
6MySQLi_prepare($link, $sql);
經(jīng)過 acu_phpaspect.php 轉(zhuǎn)換之后:
1<?php
2
3$link = NULL;
4$sql = "select * from user where user_id=".$_GET[_AAS91("hello.php", 4, "$_GET", "id")];
5
6_AAS86("hello.php",6,"mysqli_prepare",Array($link, $sql));
整個過程簡單粗暴有效,這樣做的優(yōu)點在于:
- 實現(xiàn)簡單,只需要編寫 PHP 即可
- 安裝簡單,無需安裝擴(kuò)展,只需修改配置文件可以
- 兼容性強(qiáng),比較容易兼容性各種環(huán)境,各種版本 PHP
如果有意向去做 IAST 或者想做類似我的 prvd http://github. com/fate0/prvd項目,但又不太喜歡寫 PHP 擴(kuò)展,那么我強(qiáng)烈建議你完整的看一遍 PHP 版本 AcuSensor 的實現(xiàn)。如果對自己實現(xiàn)的檢測邏輯效率比較自信的話,甚至可以基于這個原理直接實現(xiàn)一個 PHP 版本的 RASP 項目。
0x04 限速
在 Web 漏洞掃描器中,無論作為乙方的商業(yè)產(chǎn)品、甲方的自研產(chǎn)品,限速都是一個至關(guān)重要的功能,甚至可以說如果你的掃描器沒有限速功能,那壓根就不能上線使用。接下來我們將介紹一下在掃描器中限速的幾種方法。
1.代理
使用代理做限速功能,將所有執(zhí)行掃描任務(wù)的 worker 的測試流量全轉(zhuǎn)發(fā)proxy 服務(wù)器上:
由 proxy 服務(wù)器統(tǒng)一調(diào)度發(fā)送測試請求頻率,直接使用 proxy 方案優(yōu)點是可以兼容之前沒做限速功能的掃描器,缺點是所有基于 time based 的檢測均無效(當(dāng)然也可以讓 proxy 返回真正的響應(yīng)時間來進(jìn)行判斷,不過仍需要修改檢測模塊),也不允許在檢測模塊中加入超時設(shè)置。
2.雙重隊列
另外一種方法是使用雙重隊列實現(xiàn)限速功能,流程圖如下:
1.worker1 從隊列中取到名為 target1 的任務(wù)
2.worker1 從 target1 隊列中取出和 target1 相關(guān)的任務(wù)
3.默認(rèn)單并發(fā)執(zhí)行和 target1 相關(guān)任務(wù),根據(jù)設(shè)置的 QPS 限制,主動 sleep 或者增加并發(fā)
這種方案的缺點是掃描器設(shè)計之初的時候就得使用這種方法,優(yōu)點是每個并發(fā)可以穩(wěn)定的和遠(yuǎn)程服務(wù)器保持鏈接,也不影響掃描功能。
0x05 漏洞檢測
實際上這一節(jié)并不會講具體某個漏洞檢測方法,只是簡單談一下漏掃模塊每個階段該干的事情。
項目之初,沒有相關(guān)積累,那么可以選擇看一下 AWVS 的檢測代碼,雖然說網(wǎng)上公開的是 10.5 的插件代碼,但其實從 8.0 到 11 的插件代碼和 10.5 的也差不多,無非新增檢測模塊,修復(fù)誤漏報的情況,也可以多看看 SQLMap 代碼,看看檢測邏輯,但是千萬不要學(xué)習(xí)它的代碼風(fēng)格。從這些代碼中可以學(xué)習(xí)到非常多的小技巧,比如動態(tài)頁面檢測,識別 404 頁面等??创a很容易理解相關(guān)的邏輯,但我們需要去理解為什么代碼這樣處理,歷史背景是什么,所以多用 git blame。
到了中期,需要提升漏洞檢測的精準(zhǔn)度,漏洞檢測的精準(zhǔn)度是建立在各種 bad case 上,誤報的 case 比較容易收集和解決,漏報的 case 就需要其他資源來配合。作為甲方如果有漏洞收集平臺,那么可以結(jié)合白帽子以及自己部門滲透團(tuán)隊提交的漏洞去優(yōu)化漏報情況。
如果掃描器是自己的一個開源項目的話,那么就必須適當(dāng)?shù)耐茝V自己的項目,讓更多的人去使用、反饋,然后才能繼續(xù)完善項目,從而繼續(xù)推廣自己的項目,這是一個循環(huán)的過程。總而言之,提升漏洞檢測的精準(zhǔn)度需要兩個條件:1. bad case,2. 維護(hù)精力
到了后期,各種常規(guī)的漏洞檢測模塊已經(jīng)實現(xiàn)完成,也有精力持續(xù)提升檢測精準(zhǔn)度,日常漏洞 PoC 也有人員進(jìn)行補(bǔ)充。
那么事情就結(jié)束了么?
不,依舊有很多事情我們可以去做,掃描器的主要目標(biāo)是在不影響業(yè)務(wù)的情況下,不擇手段的發(fā)現(xiàn)漏洞,所以除了常規(guī)的資產(chǎn)收集方式之外,我們還可以從公司內(nèi)部各處獲取資產(chǎn)相關(guān)的數(shù)據(jù),比方說從 HIDS 中獲取所有的端口數(shù)據(jù)、系統(tǒng)數(shù)據(jù),從流量中或業(yè)務(wù)方日志中獲取 url 相關(guān)數(shù)據(jù)等。
當(dāng)然除了完善資產(chǎn)收集這塊,還有輔助提升檢測效果的事情,比如說上面提到的 AcuSensor,這部分事情可以結(jié)合公司內(nèi)部的 RASP 做到同樣效果,還有分析 access log、數(shù)據(jù)庫 log 等事情??偟膩碚f,做漏掃沒有什么條條框框限制,只要能發(fā)現(xiàn)漏洞就行。
以上都是和技術(shù)相關(guān)的事情,做漏掃需要處理的事情也不僅僅只有技術(shù),還需要去搞定詳細(xì)可操作的漏洞描述及其解決方案,匯報可量化的指標(biāo)數(shù)據(jù),最重要的是擁有有理有據(jù)、令人信服的甩鍋技巧