代碼審計入門實戰


       前段時間在整理一個PHP函數代碼審計的項目,所以文章也是圍繞PHP的代碼審計來寫,如果有寫的不對的地方,還請大佬們指正。文章開始前,我們先來了解一下PHP是什么。

 

0x01 PHP是什么

 

根據百度百科的描述,PHP是一種在服務器端執行的通用開源腳本語言,主要適用於Web開發。

既然是網站編程語言,自然需要一款工具輔助程序員高效編程。PhpStorm就是一款PHP集成開發工具,可以隨時幫助程序員對代碼進行調整、運行單元測試、且提供可視化debug功能。

當然也可以使用其他工具對PHP程序進行調試,比如Xdebug,一款開放源碼的PHP debug工具,用來跟蹤、調試和分析PHP程序的運行狀況。

大家可以通過學習這個實驗,掌握PhpStorm環境搭建和Xdebug工具的安裝和配置——代碼審計的前期准備

 

 

網傳這么一個段子:如何讓一個論壇的人吵起來?答:PHP是世界上最好的語言。這個梗出自PHP的官方文檔,最早出現在2001年7月的PHP文檔中。雖然有PHP是世界上最好的語言這種說法,但是也有一些因為弱類型語言的安全性問題出現,這就需要安全人員通過代碼審計來檢查PHP程序的安全性。

 

0x02 代碼審計又是什么

代碼審計,顧名思義就是檢查源代碼中的安全缺陷。通過自動化工具或人工審查的方式,對程序源代碼逐條進行檢查和分析,發現源碼缺陷引發的安全漏洞,有時還需要提供代碼修訂措施和修復建議。

軟件的源代碼、程序缺陷可能導致嚴重的安全漏洞,要消除代碼中的漏洞、減少不必要的補丁升級,就需要進行源代碼的安全審計。源代碼審計是對代碼庫和軟件架構的安全性、可靠性進行全面的安全檢查。人工審查已經成為軟件源碼設計、開發和應用的最佳保障,因此做好代碼審計就是從安全的角度對整個代碼質量進行評估。安全人員需要在黑客發現系統漏洞之前,找出應用的安全隱患,並提供相應的安全報告和修復方法,從而提高應用系統的安全性。

除了人工審查的方式,還可以通過一些自動化工具進行代碼審計,下面簡單介紹兩種代碼審計利器:

 

1、  Seay源代碼審計系統

這是一款針對PHP代碼安全審計的系統,基於C#語言開發,主要運行於Windows系統上。這款軟件能夠發現SQL注入、代碼/命令執行、文件包含、文件上傳、拒絕服務、XSS、信息泄露、任意URL跳轉等漏洞。關於Seay源代碼審計系統工具的使用,可以通過下面的實驗進行學習——代碼審計利器-Seay源代碼審計系統

 2、  RIPS

RIPS通過標記和解析所有源代碼文件,自動檢測PHP應用程序中的漏洞。RIPS能夠將PHP源代碼轉換為程序模型,檢測程序流期間用戶輸入可能污染的敏感接收器,即潛在易受攻擊的函數。RIPS工具的使用參考下面的實驗——代碼審計利器-RIPS實踐

 學習了代碼審計的常用工具,相信大家會對代碼審計的方法和步驟有一定的了解,那么接下來簡單總結一下代碼審計的流程:

①  通讀全文代碼:更好地了解程序的架構及業務邏輯,挖掘更多高質量的漏洞;

②  通讀敏感功能點:快速挖掘某種漏洞;

③  正向追蹤可控變量;

④  敏感關鍵字回溯參數傳遞過程。

下面進入文章的重點部分——PHP函數的代碼審計,通過具體實例分析PHP部分函數,包括in_array函數、filter_var函數、escapeshellarg與escapeshellcmd函數、parse_str函數及addslashes函數,學習上述函數缺陷引發的漏洞及利用方式,結合CTF練習掌握PHP函數漏洞審計流程。

 

0x03 PHP函數的代碼審計

項目來自https://github.com/hongriSec/PHP-Audit-Labs

這里我們根據《PHP函數漏洞審計》課程順序進行學習。

 

1、PHP代碼審計之in_array函數

既然是對in_array函數進行具體分析,我們先來了解一下in_array函數的相關定義:

注:后面說到的函數定義均來自PHP手冊,手冊地址:http://php.net/manual/zh/index.php

 $needle變量表示待搜索的值,$haystack表示待搜索的數組。in_array函數用法為:在$haystack數組中搜索$needle變量的值,檢查值是否存在。如果第三個變量$strict的值為true,則in_array函數會進行強檢查,檢查$needle的類型是否和$haystack數組中的相同。

in_array函數為什么會存在漏洞呢,原因是程序員在使用in_array函數進行數據處理時,未使用第三個參數進行嚴格匹配,比如Piwigo軟件2.7.1版本就是因為in_array函數的不安全使用,導致SQL注入漏洞發生。

查看源碼/picture.php文件可以發現,當$_GET[‘action’]為rate時,會調用文件/include/function_rate.inc.php文件中的rate_picture方法,漏洞就存在於這個方法中:

         而在rate_picture方法中,使用了in_array函數對$rate變量進行檢測,判斷$rate是否在$conf['rate_items']中:

由於functions_rate.inc.php文件中沒有將in_array函數的第三個參數設置為true,所以會進行弱比較,可以將$rate的值設置成1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#進行繞過。

那么SQL語句就變成:

INSERT INTO piwigo_rate(user_id,anonymous_id,element_id,rate,date) VALUES (2,'192.168.2',1,1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#,NOW())

這樣就可以進行盲注了。

直接利用sqlmap進行漏洞利用:

python sqlmap.py -u "http://10.1.1.79/piwigo/picture.php?/1/category/1&action=rate" --data "rate=1" --dbs

 還可以用sqlmap獲取目標數據庫的表及敏感數據。

總結一下in_array函數的審計流程:

      大家可以學習in_array函數缺陷引發的相關漏洞及其利用方式——PHP代碼審計之in_array函數

2、PHP代碼審計之filter_var函數

       filter_var函數定義:

$variable變量表示待過濾的變量(變量的值在過濾前,會被轉換成字符串),$filter變量表示要應用的過濾器的ID,$options變量代表一個選項的關聯數組或按位區分的標識。filter_var函數返回過濾后的數據,過濾失敗則返回false。

這里以一個CTF練習介紹filter_var函數缺陷引發的漏洞可以怎么利用,題目考察filter_var函數的繞過與遠程命令執行。題目源碼為:

程序使用exec函數來執行curl命令,很容易讓人聯系到命令執行。可以看到用於拼接命令的$site_info變量是從用戶傳來的url參數,經過filter_var和parse_url兩個函數過濾而來。之后又規定當url參數的值以hetianlab.com結尾時,才會執行exec函數。

這就需要繞過filter_var和parse_url函數,並且需要滿足$site_info['host']的值以hetianlab.com結尾,然后通過執行系統命令獲取flag。

Payload為/ctf/index.php?url=demo://%22;ls;%23;hetianlab.com:80/

      直接用cat f1agi3hEre.php命令過不了filter_var函數檢測,因為包含空格,使用如下payload獲取flag:/ctf/index.php?url=demo://%22;cat<f1agi3hEre.php;%23;hetianlab.com:80/

       總結一下filter_var函數的審計流程如下入,也可以學習實驗——PHP代碼審計之filter_var函數

 

3、PHP代碼審計之escapeshellarg與escapeshellcmd函數

先看一下兩個函數的相關定義,escapeshellarg函數:

$arg變量表示需要被轉碼的參數,函數返回值為轉碼之后的字符串。

escapeshellcmd函數:

$command變量代表要轉義的命令,函數返回值為轉義后的字符串。

以一個由PHP內置函數mail,結合escapeshellarg與escapeshellcmd函數所引發的命令執行漏洞為例,代碼如下:

第4行代碼的作用是確保只使用有效的電子郵件地址$email,filter_var函數用於使用特定是過濾器過濾一個變量。PHP的mail函數在底層實現中,調用了escapeshellcmd函數,對用戶輸入的郵箱地址進行檢測。即使存在特殊符號,也會被escapeshellcmd函數處理轉義,無法達到命令執行的目的。第6行代碼的作用是處理$email傳入的數據,而escapeshellarg和escapeshellcmd兩個函數一起使用,會造成特殊字符逃逸,導致遠程代碼執行。

       PHPMailer命令執行漏洞(CVE-2016-10033)也是利用escapeshellarg和escapeshellcmd兩個函數結合使用,導致了單引號逃逸。具體的漏洞分析和利用過程可以通過這個實驗進行學習——PHP代碼審計之escapeshellarg與escapeshellcmd函數

 

     總結一下escapeshellarg與escapeshellcmd函數的審計流程:

4、PHP代碼審計之parse_str函數

同樣先了解parse_str函數的相關定義:

$encoded_string變量代表輸入的字符串,如果設置了第二個變量result,變量將會以數組元素的形式存入數組,作為替代。

下面簡述parse_str函數缺陷引發的變量覆蓋漏洞,代碼如下:

        由於第22行parse_str()調用,其行為非常類似於注冊全局變量,通過提交類似config[dbhost]=127.0.0.1這樣的數據,因此可以控制getUser()中第6到第9行的全局變量$config。如果目標存在登錄驗證的過程,就可以通過變量覆蓋的方法,遠程連接我們自己的MySQL服務器,從而繞過登錄驗證進行下一步攻擊。一個簡單的例子

直接覆蓋了原有的變量$b。

parse_str函數還有一個有意思的CTF練習,首先利用PHP哈希比較缺陷,構造請求參數,使其經過哈希之后,值是以’OE’開頭。缺陷就是如果兩個不同的密碼經過哈希之后,其哈希值都是以'OE'開頭,PHP將會認為它們結果都為0。請求后頁面會出現‘flag is here’,點擊跳轉至flag.php。題目真正的考察點在於flag.php存在一個refer判斷,判斷refer是否存在,如果存在則展示上傳頁面,否則返回‘you can not see this page’。接下來的部分存在時間競爭問題,需要在寫入too slow之前訪問之前寫入的文件,才能獲取flag。

此題的解法是開burp的200線程不斷發包,在start attack之前寫一個腳本不斷請求寫入文件的路徑,是變量覆蓋與競爭條件漏洞的結合利用。

CTF練習地址——PHP代碼審計之parse_str函數

      總結一下parse_str函數的審計流程:

 

 

5、PHP代碼審計之addslashes函數

addslashes函數相關定義:

$str表示要轉義的字符,當我們要往數據庫中輸入數據時,例如將名字O’reilly插入到數據庫中,就需要對其進行轉義。

以一個用戶登錄程序為例,考察通過SQL注入繞過登錄驗證,代碼如下:

第29行通過POST方式傳入user和passwd兩個參數,通過isValid函數判斷是否合法。isValid函數主要功能代碼在第10~20行,調用sanitizeInput方法對user和passwd進行相關處理。sanitizeInput方法主要功能代碼在第22~25行,針對輸入的數據調用addslashes函數進行處理,然后對處理后的內容進行長度判斷,長度大於20則只截取前20個字符。

      這道題已經過濾了單引號,正常情況是沒有注入了,為什么還能導致注入呢?原因實際上出在第24行substr函數這里,下面簡單看一下substr函數的定義:

substr函數的參數說明:string表示輸入的字符串(至少有一個字符),如果start為非負數,返回的字符串從string的start位置開始,從0開始計算;如果start為負數,返回的字符串從string結尾處向前數,從第start個字符開始。

注:如果string的長度小於start,將返回false。

length:如果length為正數,返回的字符串將從start處開始,最多包括length個字符(取決於string的長度);如果length為負數,string末尾處的length個字符將會被省略(若start是負數則從字符串尾部算起);如果length為0、false或null,則返回一個空字符串;如果沒有提供length,返回的字符串將從start位置開始,直到字符串結尾。

關於substr函數一個簡單的例子:

 28.png

substr中的參數0代表從位置為0的字符開始計算,2代表返回的字符串將從0(start)處開始最多包括2(length)個字符。

再回到題目中,我們知道反斜杠可以取消特殊字符的用法,而注入想要通過單引號閉合,必然會引入反斜杠。將官方提供的payload帶入題目中,拼接第15~17行代碼中的SQL語句:select count(p) from user u where user = ’1234567890123456789\’ AND password = ‘$pass’

這里的SQL語句由於反斜杠的原因,user=’1234567890123456789\’最后這個單引號會失去它的作用,我們讓password=or 1=1#,那么最后的SQL語句為:select count(p) from user u where user = ’1234567890123456789\’ AND password = ‘or 1=1#’

此時user值為1234567890123456789\’ AND password =,可以保證帶入數據庫執行的結果為true,就能順利地通過驗證。因此使用形如user=1234567890123456789’&passwd= or 1=1#的payload即可逃逸出\(反斜杠)注入。

       蘋果CMS視頻分享程序8.0版本也曾爆出過SQL注入漏洞,程序調用addslashes函數對反斜杠進行轉義處理,但是對用戶請求的參數又會進行url解碼,因此可以使用雙url編碼繞過addslashes函數,觸發漏洞。

具體的漏洞分析過程可以學習——PHP代碼審計之addslashes函數

29.png

總結一下addslashes函數的審計流程:

30.png

 

0x04 總結

除了上面提到的,PHP還有很多常見危險函數,也有相關的實驗方便大家了解PHP常見的危險函數,以及使用這些函數可能導致的漏洞——PHP常見危險函數

31.png

代碼審計重在分析、重在堅持。文章看到最后,相信大家對代碼審計這個名詞不再陌生,學習完上述所有的實驗,再上GitHub找幾個代碼審計的案例源碼練練手,差不多就算入門了。安全學習貴在實踐、貴在總結

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM