自動(dòng)化審計(jì)的實(shí)現(xiàn)方式有多種,比如直接使用正則表達(dá)式規(guī)則庫進(jìn)行定位匹配,這種方法最簡單,但是準(zhǔn)確率是最低的。最可靠的思路是結(jié)合靜態(tài)分析技術(shù)領(lǐng)域中的知識(shí)進(jìn)行設(shè)計(jì),一般靜態(tài)分析安全工具的流程大多是下圖的形式:
靜態(tài)分析工作所要做的第一件事情就是將源碼進(jìn)行建模,通俗一點(diǎn)講,就是將字符串的源碼轉(zhuǎn)為方便于我們后續(xù)漏洞分析的中間表示形式,即一組代表此代碼 的數(shù)據(jù)結(jié)構(gòu)。建模工作中一般會(huì)采用編譯技術(shù)領(lǐng)域中的方法,如詞法分析生成token,生成抽象語法樹,生成控制流程圖等。建模工作的優(yōu)劣,直接影響到后續(xù) 污染傳播分析和數(shù)據(jù)流分析的效果。
執(zhí)行分析就是結(jié)合安全知識(shí),對載入的代碼進(jìn)行漏洞分析和處理。最后,靜態(tài)分析工具要生成判斷結(jié)果,從而結(jié)束這一階段的工作。
經(jīng)過一段時(shí)間的努力,筆者和小伙伴也大致實(shí)現(xiàn)了一款針對自動(dòng)化的靜態(tài)分析工具。具體實(shí)現(xiàn)思路正是采用了靜態(tài)分析技術(shù),如果想深入了解實(shí)現(xiàn)思路,可以閱讀之前發(fā)過的文章。 在工具中,自動(dòng)化審計(jì)流程如下:
在真實(shí)的PHP審計(jì)中,遇到敏感函數(shù)的調(diào)用,比如mysql_query,我們就會(huì)不由自主地去手動(dòng)分析第一個(gè)參數(shù),看是否可控。事實(shí)上,很多CMS都會(huì)將一些數(shù)據(jù)庫查詢的方法進(jìn)行封裝,使得調(diào)用方便且程序邏輯清晰,比如封裝為一個(gè)類MysqlDB。這時(shí),在審計(jì)中我們就不會(huì)搜索mysql_query關(guān)鍵字了,而是去找比如db->getOne這種類的調(diào)用。
那么問題來了,在自動(dòng)化程序進(jìn)行分析的時(shí)候,如何獲知db->getOne函數(shù)是個(gè)數(shù)據(jù)庫的訪問類方法呢?
這就需要在自動(dòng)化分析的初期就要對整個(gè)工程的所有類與定義的方法進(jìn)行搜集,以便于程序在分析的時(shí)候?qū)ふ倚枰M(jìn)的方法體。
對于類信息和方法信息的搜集,應(yīng)該作為框架初始化的一部分完成,存儲(chǔ)在單例上下文中:
同時(shí),需要識(shí)別分析的PHP文件是否是真正處理用戶請求的文件,因?yàn)橛行〤MS中,一般會(huì)將封裝好的類寫入單獨(dú)的文件中,比如將數(shù)據(jù)庫操作類或者文 件操作類封裝到文件中。對于這些文件,進(jìn)行污染傳播分析是沒有意義的,所以在框架初始化的時(shí)候需要進(jìn)行識(shí)別,原理很簡單,分析調(diào)用類型語句和定義類型語句 的比例,根據(jù)閾值進(jìn)行判別,錯(cuò)誤率很小。
最后,對每個(gè)文件進(jìn)行摘要操作,這一步的目的是為了后續(xù)分析時(shí)碰到require,include等語句時(shí)進(jìn)行文件間分析使用。主要收集變量的賦值、變量的編碼、變量的凈化信息。
常見的web漏洞,一般都是由于危險(xiǎn)參數(shù)用戶可控導(dǎo)致的,這種漏洞稱之為污點(diǎn)類型漏洞,比如常見的SQLI,XSS等。 PHP內(nèi)置的一些函數(shù)本身是危險(xiǎn)的,比如echo可能會(huì)造成反射型XSS。然而真實(shí)代碼中,沒人會(huì)直接調(diào)用一些內(nèi)置的功能函數(shù),而是進(jìn)行再次封裝,作為自 定義的函數(shù),比如:
function myexec($cmd)
在實(shí)現(xiàn)中,我們的處理流程是:
總結(jié)為一句話,我們就是跟入到相應(yīng)的類方法、靜態(tài)方法、函數(shù)中,從這些代碼段中查詢是否有危險(xiǎn)函數(shù)和危險(xiǎn)參數(shù)的調(diào)用,這些PHP內(nèi)置的危險(xiǎn)函數(shù)和參 數(shù)位置都是放在配置文件中的進(jìn)行配置完成的,如果這些函數(shù)和參數(shù)一旦被發(fā)現(xiàn),且判斷危險(xiǎn)參數(shù)并沒有被過濾,則將該用戶自定義函數(shù)作為用戶自定義危險(xiǎn)函數(shù)。 一旦后續(xù)的分析中發(fā)現(xiàn)調(diào)用這些函數(shù),則立即啟動(dòng)污點(diǎn)分析。
在真實(shí)的審計(jì)過程中,一旦發(fā)現(xiàn)危險(xiǎn)參數(shù)是可控的,我們就會(huì)迫不及待地去尋找看程序員有沒有對該變量進(jìn)行有效的過濾或者編碼,由此判斷是否存在漏洞。 自動(dòng)化審計(jì)中,也是遵循這個(gè)思路。在實(shí)現(xiàn)中,首先要對每一個(gè)PHP中的安全函數(shù)進(jìn)行統(tǒng)計(jì)和配置,在程序分析時(shí),對每一條數(shù)據(jù)流信息,都應(yīng)該進(jìn)行回溯收集必 要的凈化和編碼信息,比如:
$a = $_GET[‘a’] ;
$a = intval($a) ;
echo $a ;
$a = htmlspecialchars($a) ;
mysql_query($a) ;
上面的代碼片段看起來有些怪異,但只是作為演示使用。從代碼片段可以看出,變量a經(jīng)過了intval和htmlspecialchars兩個(gè)凈化處 理,根據(jù)配置文件,我們順利的收集到了這些信息。這時(shí),要進(jìn)行一次回溯,目的是將當(dāng)前代碼行向上的凈化和編碼信息進(jìn)行歸并。 比如在第三行時(shí),變量a的凈化信息只有一條intval,但是第五行時(shí),要求將變量a的凈化信息歸并,收集為一個(gè)list集合intval和 htmlspecialchars,方法就是收集到前驅(qū)代碼中的所有數(shù)據(jù)流的信息,并進(jìn)行回溯。
細(xì)節(jié)部分是,當(dāng)用戶同時(shí)對同一個(gè)變量調(diào)用了如base64_encode和base64_decode兩個(gè)函數(shù),那么這個(gè)變量的base64編碼會(huì) 被消除。同樣,如果同時(shí)進(jìn)行轉(zhuǎn)義和反轉(zhuǎn)義也要進(jìn)行消除。但是如果調(diào)用順序不對或者只進(jìn)行了decode,那么你懂的,相當(dāng)危險(xiǎn)。
為了尋找出所有的危險(xiǎn)sink點(diǎn)的參數(shù)(traceSymbol),將向前回溯與當(dāng)前Block相連的所有的基本塊,具體過程如下:
當(dāng)traceSymbol映射到了一個(gè)靜態(tài)字符串、數(shù)字等類型的靜態(tài)對象或者當(dāng)前的基本塊沒有入口邊時(shí),算法就停止。如果traceSymbol是變量或者數(shù)組,就要檢查是否在超全局?jǐn)?shù)組中。
污點(diǎn)分析在過程間分析處理內(nèi)置和用戶定義函數(shù)過程中開始,如果程序分析時(shí)遇到了敏感的函數(shù)調(diào)用,則使用回溯或者從上下文中獲取到危險(xiǎn)參數(shù)節(jié)點(diǎn),并開始進(jìn)行污點(diǎn)分析。通俗講,就是進(jìn)行危險(xiǎn)參數(shù)是否可能導(dǎo)致漏洞的判別。污點(diǎn)分析工作在代碼TaintAnalyser中進(jìn)行實(shí)現(xiàn),獲取到危險(xiǎn)參數(shù)后,具體步驟如下:
目前的效果
我們對simple-log_v1.3.12進(jìn)行了測試性掃描,結(jié)果是:
Total : 76 XSS : 3 SQLI : 62 INCLUDE : 5 FILE : 3 FILEAFFECT : 1
測試代碼都是一些比較明顯的漏洞,且沒有使用MVC框架,什么字符截?cái)喑缘艮D(zhuǎn)義符這種,目前的技術(shù)還真的支持不了,不過也是可以掃出一些了。從測試 過程來看,bug層出不窮,主要是前期實(shí)現(xiàn)時(shí),很多語法結(jié)構(gòu)與測試用例沒有考慮進(jìn)去,加上算法幾乎都是遞歸的,所以很容易就造成無限遞歸導(dǎo)致Apache 跪掉。
所以目前的代碼真的只能算是試驗(yàn)品,代碼的健壯性需要無數(shù)次重構(gòu)和大量的測試來實(shí)現(xiàn),筆者已經(jīng)沒有太多時(shí)間維護(hù)。
靜態(tài)分析領(lǐng)域中,很多安全研究人員都是做C/C++/反編譯匯編等方向,目前腳本語言領(lǐng)域也急需技術(shù)力量投入進(jìn)去,因?yàn)檫@是一件很有意義的事情。
回到坑上面來,筆者和小伙伴們的實(shí)現(xiàn)中,有個(gè)重大的問題就是不支持MVC框架。這些MVC如CI框架,數(shù)據(jù)流很難進(jìn)行統(tǒng)一捕捉,因?yàn)榭蚣芊庋b度很高。所以針對不同的框架估計(jì)需要不同的分析方式。
目前的狀況是,可以識(shí)別一些簡單的漏洞,代碼不夠健壯存在諸多bug。
最后,talk is cheap, show me the code. 實(shí)現(xiàn)代碼在github上可以找到。
代碼分享出來的目的是供有志于或者已經(jīng)投身于該領(lǐng)域的安全研究人員進(jìn)行研究與討論,目前還達(dá)不到隨便拿出一個(gè)CMS就能跑的效果,望大家不要有所幻想。