PHP自動化白盒審計技術與實現(xiàn)
0x00 前言
國內公開的PHP自動化審計技術資料較少,相比之下,國外已經(jīng)出現(xiàn)了比較優(yōu)秀的自動化審計實現(xiàn),比如RIPS是基于token流為基礎進行一系列的代碼分析。傳統(tǒng)靜態(tài)分析技術如數(shù)據(jù)流分析、污染傳播分析應用于PHP這種動態(tài)腳本語言分析相對較少,但是卻是實現(xiàn)白盒自動化技術中比較關鍵的技術點。今天筆者主要介紹一下最近的研究與實現(xiàn)成果,在此拋磚引玉,希望國內更多的安全研究人員將精力投入至PHP自動化審計技術這一有意義的領域中。
0x01 基礎知識
自動化審計的實現(xiàn)方式有多種,比如直接使用正則表達式規(guī)則庫進行定位匹配,這種方法最簡單,但是準確率是***的。最可靠的思路是結合靜態(tài)分析技術領域中的知識進行設計,一般靜態(tài)分析安全工具的流程大多是下圖的形式:

靜態(tài)分析工作所要做的***件事情就是將源碼進行建模,通俗一點講,就是將字符串的源碼轉為方便于我們后續(xù)漏洞分析的中間表示形式,即一組代表此代碼的數(shù)據(jù)結構。建模工作中一般會采用編譯技術領域中的方法,如詞法分析生成token,生成抽象語法樹,生成控制流程圖等。建模工作的優(yōu)劣,直接影響到后續(xù)污染傳播分析和數(shù)據(jù)流分析的效果。
執(zhí)行分析就是結合安全知識,對載入的代碼進行漏洞分析和處理。***,靜態(tài)分析工具要生成判斷結果,從而結束這一階段的工作。
0x02 實現(xiàn)思路
經(jīng)過一段時間的努力,筆者和小伙伴也大致實現(xiàn)了一款針對自動化的靜態(tài)分析工具。具體實現(xiàn)思路正是采用了靜態(tài)分析技術,如果想深入了解實現(xiàn)思路,可以閱讀之前發(fā)過的文章。 在工具中,自動化審計流程如下:

首先載入用戶輸入的待掃描的工程目錄中所有的PHP文件,并對這些PHP文件做判別,如果掃描的PHP文件是Main file,即真正處理用戶請求的PHP文件,那么對這種類型的文件進行漏洞分析。如果不是Main file類型,比如PHP工程中的類定義,工具函數(shù)定義文件,則跳過不做分析。
其次進行全局數(shù)據(jù)的搜集,重點搜集的信息有待掃描的工程中類信息的定義,如類所在的文件路徑、類中的屬性、類中的方法以及參數(shù)等信息。同時對每個文件生成文件摘要,文件摘要中重點搜集各個賦值語句的信息,以及賦值語句中相關變量的凈化信息和編碼信息。
全局初始化之后,進行編譯前端模塊的相關工作,使用開源工具PHP-Parser對待分析的PHP代碼進行抽象語法樹(AST)的構建。在AST的基礎上,使用CFG構建算法構建控制流圖,并實時地生成基本塊的摘要信息。
編譯前端的工作中,如果發(fā)現(xiàn)敏感函數(shù)的調用,就停下來進行污染傳播分析,進行過程間分析、過程內分析,找到對應的污點數(shù)據(jù)。然后基于數(shù)據(jù)流分析過程中搜集的信息,進行凈化信息和編碼信息的判斷,從而判斷是否為漏洞代碼。
如果上一步是漏洞代碼,則轉入漏洞報告模塊進行漏洞代碼段的收集。其實現(xiàn)的基礎是在系統(tǒng)環(huán)境中維護一個單例模式的結果集上下文對象,如果生成一條漏洞記錄,則加入至結果集中。當整個掃描工程結果之后,使用Smarty將結果集輸出到前端,前端做掃描結果的可視化。#p#
0x03 初始化工作
在真實的PHP審計中,遇到敏感函數(shù)的調用,比如mysql_query,我們就會不由自主地去手動分析***個參數(shù),看是否可控。事實上,很多CMS都會將一些數(shù)據(jù)庫查詢的方法進行封裝,使得調用方便且程序邏輯清晰,比如封裝為一個類MysqlDB。這時,在審計中我們就不會搜索mysql_query關鍵字了,而是去找比如db->getOne這種類的調用。
那么問題來了,在自動化程序進行分析的時候,如何獲知db->getOne函數(shù)是個數(shù)據(jù)庫的訪問類方法呢?
這就需要在自動化分析的初期就要對整個工程的所有類與定義的方法進行搜集,以便于程序在分析的時候尋找需要跟進的方法體。
對于類信息和方法信息的搜集,應該作為框架初始化的一部分完成,存儲在單例上下文中:

同時,需要識別分析的PHP文件是否是真正處理用戶請求的文件,因為有些CMS中,一般會將封裝好的類寫入單獨的文件中,比如將數(shù)據(jù)庫操作類或者文件操作類封裝到文件中。對于這些文件,進行污染傳播分析是沒有意義的,所以在框架初始化的時候需要進行識別,原理很簡單,分析調用類型語句和定義類型語句的比例,根據(jù)閾值進行判別,錯誤率很小。
***,對每個文件進行摘要操作,這一步的目的是為了后續(xù)分析時碰到require,include等語句時進行文件間分析使用。主要收集變量的賦值、變量的編碼、變量的凈化信息。
0x04 用戶函數(shù)處理
常見的web漏洞,一般都是由于危險參數(shù)用戶可控導致的,這種漏洞稱之為污點類型漏洞,比如常見的SQLI,XSS等。 PHP內置的一些函數(shù)本身是危險的,比如echo可能會造成反射型XSS。然而真實代碼中,沒人會直接調用一些內置的功能函數(shù),而是進行再次封裝,作為自定義的函數(shù),比如:
1234function myexec($cmd)
{
exec($cmd) ;
}
在實現(xiàn)中,我們的處理流程是:
利用初始化中獲取的上下文信息,定位到相應的方法代碼段
分析這個代碼片段,查找到危險函數(shù)(這里是exec)
定位危險函數(shù)中的危險參數(shù)(這里是cmd)
如果在分析期間沒有遇到凈化信息,說明該參數(shù)可以進行傳染,則映射到用戶函數(shù)myexec的***個參數(shù)cmd,并將這個用戶自定義函數(shù)當做危險函數(shù)存放至上下文結構中
遞歸返回,啟動污點分析過程
總結為一句話,我們就是跟入到相應的類方法、靜態(tài)方法、函數(shù)中,從這些代碼段中查詢是否有危險函數(shù)和危險參數(shù)的調用,這些PHP內置的危險函數(shù)和參數(shù)位置都是放在配置文件中的進行配置完成的,如果這些函數(shù)和參數(shù)一旦被發(fā)現(xiàn),且判斷危險參數(shù)并沒有被過濾,則將該用戶自定義函數(shù)作為用戶自定義危險函數(shù)。一旦后續(xù)的分析中發(fā)現(xiàn)調用這些函數(shù),則立即啟動污點分析。#p#
0x05 處理變量的凈化和編碼
在真實的審計過程中,一旦發(fā)現(xiàn)危險參數(shù)是可控的,我們就會迫不及待地去尋找看程序員有沒有對該變量進行有效的過濾或者編碼,由此判斷是否存在漏洞。 自動化審計中,也是遵循這個思路。在實現(xiàn)中,首先要對每一個PHP中的安全函數(shù)進行統(tǒng)計和配置,在程序分析時,對每一條數(shù)據(jù)流信息,都應該進行回溯收集必要的凈化和編碼信息,比如:
12345$a = $_GET['a'] ;
$a = intval($a) ;
echo $a ;
$a = htmlspecialchars($a) ;
mysql_query($a) ;
上面的代碼片段看起來有些怪異,但只是作為演示使用。從代碼片段可以看出,變量a經(jīng)過了intval和htmlspecialchars兩個凈化處理,根據(jù)配置文件,我們順利的收集到了這些信息。這時,要進行一次回溯,目的是將當前代碼行向上的凈化和編碼信息進行歸并。 比如在第三行時,變量a的凈化信息只有一條intval,但是第五行時,要求將變量a的凈化信息歸并,收集為一個list集合intval和htmlspecialchars,方法就是收集到前驅代碼中的所有數(shù)據(jù)流的信息,并進行回溯。

細節(jié)部分是,當用戶同時對同一個變量調用了如base64_encode和base64_decode兩個函數(shù),那么這個變量的base64編碼會被消除。同樣,如果同時進行轉義和反轉義也要進行消除。但是如果調用順序不對或者只進行了decode,那么你懂的,相當危險。
0x06 變量回溯和污點分析
1、變量回溯
為了尋找出所有的危險sink點的參數(shù)(traceSymbol),將向前回溯與當前Block相連的所有的基本塊,具體過程如下:
循環(huán)當前基本塊的所有入口邊,查找沒有經(jīng)過凈化的traceSymbol并且查找基本塊DataFlow屬性中,traceSymbol的名字。
如果一旦找到,那么就替換成映射的symbol,并且將該符號的所有凈化信息和編碼信息都復制過來。然后,追蹤會在所有的入口邊上進行。
***,CFG上不同路徑上的結果會返回。
當traceSymbol映射到了一個靜態(tài)字符串、數(shù)字等類型的靜態(tài)對象或者當前的基本塊沒有入口邊時,算法就停止。如果traceSymbol是變量或者數(shù)組,就要檢查是否在超全局數(shù)組中。
2、污點分析
污點分析在過程間分析處理內置和用戶定義函數(shù)過程中開始,如果程序分析時遇到了敏感的函數(shù)調用,則使用回溯或者從上下文中獲取到危險參數(shù)節(jié)點,并開始進行污點分析。通俗講,就是進行危險參數(shù)是否可能導致漏洞的判別。污點分析工作在代碼TaintAnalyser中進行實現(xiàn),獲取到危險參數(shù)后,具體步驟如下:
首先,在當前基本塊中尋找危險參數(shù)的賦值情況,尋找DataFlow的右邊節(jié)點中是否存在用戶輸入source,比如$_GET $_POST等超全局數(shù)組。并使用不同類型漏洞判別的插件類判斷這些節(jié)點是否是安全的。
如果當前基本塊中沒有尋找到source,則進入本文件多基本塊間分析過程。首先獲取當前基本塊的所有前驅基本塊,其中前驅基本塊中包含平行結構(if-else if-else),或者非平行結構(普通語句)。并進行危險變量分析,如果當前循環(huán)的基本塊中沒有前驅節(jié)點,則分析算法結束。
如果基本塊間分析沒有找到漏洞,則進行***的文件間分析。載入當前基本塊之前的包含文件摘要,遍歷這些文件摘要做出判斷。
如果上述步驟中,出現(xiàn)漏洞,則進入漏洞報告模塊。否則,系統(tǒng)繼續(xù)往下進行代碼分析。

0x07 目前的效果

我們對simple-log_v1.3.12進行了測試性掃描,結果是:
Total : 76 XSS : 3 SQLI : 62 INCLUDE : 5 FILE : 3 FILEAFFECT : 1
測試代碼都是一些比較明顯的漏洞,且沒有使用MVC框架,什么字符截斷吃掉轉義符這種,目前的技術還真的支持不了,不過也是可以掃出一些了。從測試過程來看,bug層出不窮,主要是前期實現(xiàn)時,很多語法結構與測試用例沒有考慮進去,加上算法幾乎都是遞歸的,所以很容易就造成***遞歸導致Apache跪掉。
所以目前的代碼真的只能算是試驗品,代碼的健壯性需要無數(shù)次重構和大量的測試來實現(xiàn),筆者已經(jīng)沒有太多時間維護。
0x08 總結
靜態(tài)分析領域中,很多安全研究人員都是做C/C++/反編譯匯編等方向,目前腳本語言領域也急需技術力量投入進去,因為這是一件很有意義的事情。
回到坑上面來,筆者和小伙伴們的實現(xiàn)中,有個重大的問題就是不支持MVC框架。這些MVC如CI框架,數(shù)據(jù)流很難進行統(tǒng)一捕捉,因為框架封裝度很高。所以針對不同的框架估計需要不同的分析方式。
目前的狀況是,可以識別一些簡單的漏洞,代碼不夠健壯存在諸多bug。
***,talk is cheap, show me the code. 實現(xiàn)代碼在github上可以找到。
代碼分享出來的目的是供有志于或者已經(jīng)投身于該領域的安全研究人員進行研究與討論,目前還達不到隨便拿出一個CMS就能跑的效果,望大家不要有所幻想。