1. 程式人生 > >PHP程式碼審計Day學習筆記5-8

PHP程式碼審計Day學習筆記5-8

文章目錄

來自先知社群-紅日安全-程式碼審計小組的PHP程式碼審計的專案本階段的內容題目均來自 PHP SECURITY CALENDAR 2017

Day5 - escapeshellarg與escapeshellcmd使用不當

前言

mail()

bool mail (
    string $to ,
    string $subject ,
    string $message [,
    string $additional_headers [,
    string $additional_parameters ]]
)

php內建函式

  • to,指定郵件接收者,即接收人
  • subject,郵件的標題
  • message,郵件的正文內容
  • additional_headers,指定郵件傳送時其他的額外頭部,如傳送者From,抄送CC,隱藏抄送BCC
  • additional_parameters,指定傳遞給傳送程式sendmail的額外引數。

eg:

使用了-X指定了日誌檔案,導致木馬檔案的寫入

FILTER_VALIDATE_EMAIL

filter_var()函式的選項,確保是否是有效的電子郵箱地址

escapeshellcmd()

escapeshellcmd — shell 元字元轉義
**功能:**對字串中可能會欺騙 shell 命令執行任意命令的字元進行轉義。

escapeshellarg()

escapeshellarg — 把字串轉碼為可以在 shell 命令裡使用的引數
**功能:**將給字串增加一個單引號並且能引用或者轉碼任何已經存在的單引號
案例

題目

漏洞解析

  • 第3行filter_var()對郵箱檢查,時由於底層正則表示式的原因,我們通過重疊單引號和雙引號,欺騙 filter_val() 使其認為我們仍然在雙引號中,這樣我們就可以繞過檢測
    舉例
  • PHP的 mail()函式在底層實現中,呼叫了 escapeshellcmd() 函式,但第7行又呼叫了escapeshellarg().escapeshellcmd()escapeshellarg() 一起使用,會造成特殊字元逃逸
    eg:

    詳細分析一下這個過程:

1.傳入的引數是

127.0.0.1' -v -d a=1

2.由於escapeshellarg先對單引號轉義,再用單引號將左右兩部分括起來從而起到連線的作用。所以處理之後的效果如下:

'127.0.0.1 -v -d a=1'

3.接著escapeshellcmd函式對第二步處理後字串中的 以及 a=1’ 中的單引號進行轉義處理,結果如下所示:

'127.0.0.1''' -v -d a=1'

4.由於第三步處理之後的payload中的 被解釋成了 而不再是轉義字元,所以單引號配對連線之後將payload分割為三個部分,具體如下所示:

所以這個payload可以簡化為 curl 127.0.0.1/ -v -d a=1',即向 127.0.0.1發起請求,POST 資料為 a=1’ 。

總結

考察繞過 filter_var() 函式的郵件名檢測,通過 mail 函式底層實現中呼叫的 escapeshellcmd() 函式處理字串,再結合 escapeshellarg() 函式,最終實現引數逃逸,導致 遠端程式碼執行 。

Day6 - 正則使用不當導致的路徑穿越問題

前言

preg_relace()

preg_replace — 執行一個正則表示式的搜尋和替換
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int KaTeX parse error: Expected 'EOF', got '&' at position 19: …it = -1 [, int &̲count ]] )
搜尋subject中匹配pattern的部分, 以replacement進行替換。

題目

題目叫福斯特模式,程式碼如下

漏洞解析

  • 第21行的preg_replace()呼叫中使用的正則表示式中的非轉義連字元-。如果連字元未轉義,則將其用作範圍指示符,從而導致 替換在點(46)和下劃線(95)之間的範圍內,並且不是a到z的任何字元。 因此,點和斜槓沒有過濾,可以用於目錄遍歷,例如使用查詢引數action = delete&data = … / … / config.php。

Day7 - parse_str函式缺陷

前言

pares_str()

parse_str — 將字串解析成多個變數
void parse_str ( string KaTeX parse error: Expected 'EOF', got '&' at position 25: …tring [, array &̲result ] )

引數 說明
encoded_string 輸入的字串
result 設定了第二個變數 result, 變數將會存入到這個陣列。

題目

題目叫做鍾,程式碼如下:

漏洞解析

  • 變數覆蓋漏洞,不安全的使用了parse_str().第21行的釣魚,非常類似於註冊全域性變數,因此我們可以控制5到8行的$config變數

payload

http://host/?config[dbhost]=10.0.0.5&config[dbuser]=root&config[dbpass]=root&config[dbname]=malicious&id=1.

Day8 - preg_replace函式之命令執行

前言

prep_replace()

preg_replace — 執行一個正則表示式的搜尋和替換
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int KaTeX parse error: Expected 'EOF', got '&' at position 19: …it = -1 [, int &̲count ]] )
搜尋subject中匹配pattern的部分, 以replacement進行替換。

  • $pattern 存在 /e 模式修正符,允許程式碼執行,將 $replacement 當做php程式碼來執行

反向引用

對一個正則表示式模式或部分模式 兩邊新增圓括號 將導致相關 匹配儲存到一個臨時緩衝區 中,所捕獲的每個子匹配都按照在正則表示式模式中從左到右出現的順序儲存。緩衝區編號從 1 開始,最多可儲存 99 個捕獲的子表示式。每個緩衝區都可以使用 ‘n’ 訪問,其中 n 為一個標識特定緩衝區的一位或兩位十進位制數。

題目

題目叫蠟燭,程式碼如下

漏洞解析

  • 這道題目考察的是 preg_replace() 函式使用 /e模式,導致程式碼執行的問題。我們發現在上圖程式碼 第11行 處,將 GET 請求方式傳來的引數用在了 complexStrtolower 函式中,而變數 $regex 和 $value 又用在了存在程式碼執行模式的 preg_replace 函式中。所以,我們可以通過控制 preg_replace 函式第1個、第3個引數,來執行程式碼

payload

官方的

/?.*={${phpinfo()}}

不能用,因為如果GET請求的引數名存在非法字元,PHP會將其替換成下劃線,.*變成_*
可用的

S*=${phpinfo()}

詳細分析見: 深入研究preg_replace與程式碼執行