淺談PHP自動化程式碼審計技術

發表於2015-04-14

0x00

 

由於部落格實在沒什麼可以更新的了,我就把目前做的事情總結一下,當做一篇部落格,主要是談一談專案中所運用的一些技術。目前市面上有不少PHP的自動化審計工具,開源的有RIPS、Pixy,商業版本的有Fortify。RIPS現在只有第一版,由於不支援PHP物件導向分析,所以現在來看效果不是太理想。Pixy是基於資料流分析的工具,但是隻支援PHP4。而Fortify是商業版本,由於這個限制,對它的研究也就無從談起。國內對於PHP自動審計的研究一般都是公司在做,目前有些工具大多數使用簡單的token流分析或者直接粗暴一些,使用正規表示式來匹配,效果會很一般。

 

0x01

 

今天所要談的技術是基於靜態分析的一種PHP自動化審計的實現思路,也是我的專案中的思路。為了進行更加有效的變數根據和汙點分析,以及很好的應對PHP指令碼中的各種靈活的語法表示,正規表示式效果肯定是不理想的,我所介紹的思路是基於程式碼靜態分析技術和資料流分析技術的審計。

首先,我認為一個有效審計工具至少包含如下的模組:

 

1、編譯前端模組
編譯前端模組主要運用編譯技術中的抽象語法樹構建、控制流圖構建方法,將原始碼檔案轉為適合後端靜態分析的形式。

2、全域性資訊蒐集模組
該模組主要用於對分析的原始碼檔案進行統一的資訊蒐集,比如蒐集該審計工程中有多少類的定義,並對類中的方法名、引數、以及方法定義程式碼塊的起始和終止的行號進行蒐集,用於加快後續的靜態分析的速度。

3、資料流分析模組
該模組不同於編譯技術中的資料流分析演算法,在專案中更注重對PHP語言本身特性的處理。當系統的過程間和過程內分析過程中發現了敏感函式的呼叫,則對該函式中敏感的引數進行資料流分析,即跟蹤該變數的具體變化,為後續汙點分析做準備。

4、漏洞程式碼分析模組
該模組基於資料流分析模組收集的全域性變數、賦值語句等資訊,進行汙點資料分析。主要針對敏感sink中的危險引數,如mysql_query函式中的第一個引數,經過回溯獲取到相應的資料流資訊,如果在回溯過程中發現該引數有使用者控制的跡象,就進行記錄。如果該危險引數有相應的編碼、淨化操作也要進行記錄。通過對危險引數的資料進行跟蹤和分析,完成汙點分析。

 

0x02

有了模組,那麼如何進行有效的流程來實施自動化審計,我使用瞭如下的流程:

 

分析系統經過的大致流程如下:

1、框架初始化

首先進行分析框架的初始化工作,主要是蒐集待分析原始碼工程中的所有使用者自定義類的資訊,包括類名,類屬性,類方法名,類所在的檔案路徑。
這些Record存放在全域性上下文類Context中,該類使用單例模式進行設計,並且常駐記憶體,便於後續的分析使用。

2、判斷Main File

其次判斷每個PHP檔案是否是Main file。在PHP語言中,沒有所謂的main函式,大部分Web中的PHP檔案分為呼叫和定義兩種型別,定義型別的PHP檔案是用來定義一些業務類、工具類、工具函式等,不提供給使用者進行訪問,而是提供給呼叫型別的PHP檔案進行呼叫。而真正處理使用者請求的則是呼叫型別的PHP檔案,比如全域性index.php檔案。靜態分析主要是針對處理使用者請求的呼叫型別的PHP檔案,即Main File。判斷依據為:
在AST解析完成的基礎上,判斷一個PHP檔案中的類定義、方法定義的程式碼行數佔該檔案所有程式碼行數是否超過一個範圍,如果是,則視為定義型別的PHP檔案,否則為Main File,新增到待分析的檔名列表中。

3、AST抽象語法樹的構建

本專案基於PHP語言本身進行開發,對於其AST的構建,我們參考目前比較優秀的PHP AST構建的實現————PHP Parser。
該開源專案基於PHP語言本身進行開發,可以對PHP的大多數結構如if、while、switch、陣列宣告、方法呼叫、全域性變數等語法結構進行解析。可以很好的完成本專案的編譯前端處理的一部分工作。

4、CFG流圖構建

使用CFGGenerator類中的CFGBuilder方法。方法定義如下:

 

具體思路是採用遞迴構建CFG。首先輸入遍歷AST獲取的nodes集合,遍歷中對集合中的元素(node)進行型別判斷,如判斷是否是分支、跳轉、結束等語句,並按照node的型別進行CFG的構建。
這裡對於分支語句、迴圈語句的跳轉條件(conditions)要儲存至CFG中的邊(Edge)上,方便資料流分析。

5、資料流資訊的收集

對於一段程式碼塊,最有效的並且值得收集的資訊是賦值語句、函式呼叫、常量(const define)、註冊的變數(extract parse_str)。
賦值語句的作用就是為了後續進行變數跟蹤,在實現中,我使用了一種結構來表示賦值的value以及location。而其他的資料資訊是基於AST來判別和獲取的。比如函式呼叫中,判斷變數是否受到轉義、編碼等操作,或者呼叫的函式是否是sink(如mysql_query)。

6、變數淨化、編碼資訊處理

 

$clearsql = addslashes($sql) ;
賦值語句,當右邊是過濾函式時(使用者自定義過濾函式或者內建過濾函式),則呼叫函式的返回值被淨化,即$clearsql的淨化標籤加上addslashes。
發現函式呼叫,判斷函式名是否是配置檔案中進行配置的安全函式。
如果是,則將淨化標籤新增至location的symbol中。

7、過程間分析

如果在審計中,發現使用者函式的呼叫,這時候必須要進行過程間的分析,在分析的工程中定位到具體方法的程式碼塊,帶入變數進行分析。
難點在於,如何進行變數回溯、如何應對不同檔案中的相同名稱的方法、如何支援類方法的呼叫分析、如何儲存使用者自定義的sink(比如在myexec中呼叫exec函式,如果沒有經過有效的淨化,那麼myexec也要視為危險函式)、如何對使用者自定義的sink進行分類(如SQLI XSS XPATH等)。

處理流程如下:

 

8、汙點分析

有了上面的過程,最後要進行的就是汙點分析,主要針對系統中內建的一些風險函式,比如可能導致xss的echo。並且要對危險函式中的危險引數做有效的分析,這些分析包括判斷是否進行了有效的淨化(比如轉義、正則匹配等),以及制定演算法來回溯前面該變數的賦值或者其他變換。這無疑對安全研究人員工程能力的一個考驗,也是自動化審計最重要的階段。

 

0x03

通過上面的介紹,你可以看到要實現一款自己的自動化審計工具所要趟的坑是很多的。我的嘗試中也是遇到了N多的困難,並且靜態分析確實帶有一定的侷限性,比如動態分析中輕易可以獲得的字串變換的過程,在靜態分析中就難以實現,這不是技術上能夠突破的,而是靜態分析本身的侷限性導致的,所以單純的靜態分析如果想要做到誤報和漏報很低,畢竟引入一些動態的思想,比如對eval中的程式碼進行模擬,對字串變化函式以及正規表示式進行處理等。還有就是對於一些基於MVC框架的,比如CI框架,程式碼很分散,比如資料淨化的程式碼放在input類的擴充套件中,像這種PHP應用,我認為很難做到一個通用的審計框架,應該要單獨對待。

以上只是粗略的把我當前的嘗試(目前沒有完全實現)拿來share,畢竟大學狗不是專業人員,希望可以拋磚引玉,使得越來越多的安全研究人員關注這一領域。

相關文章