*本文原創作者:sarleon,屬Freebuf原創獎勵計劃,未經許可禁止轉載
PHP是現在網站中最為常用的後端語言之一,是一種類型系統 動態、弱類型的面向對象式編程語言。可以嵌入HTML文本中,是目前最流行的web後端語言之一,並且可以和Web Server 如apache和nginx方便的融合。目前,已經佔據了服務端市場的極大佔有量。
但是,弱類型,一些方便的特性由於新手程序員的不當使用,造成了一些漏洞,這篇文章就來介紹一下一些滲透中可以用的特性。
上面都是廢話,下面我們進入正題
1.弱類型的比較==導致的漏洞
註:這些漏洞適用於所有版本的php
先來複習一下基本的語法:php中有如下兩種比較符號:兩個等號和三個等號(這一點和Javascript)有些類似
$a==$b $a===$b
我們來一下php官方手冊的說法
明確的看到,兩個等於號的等於會在比較的時候進行類型轉換的比較。
如果比較一個數字和字元串或者比較涉及到數字內容的字元串,則字元串會被轉換為數值並且比較按照數值來進行。此規則也適用於 switch 語句。當用 === 或 !== 進行比較時則不進行類型轉換,因為此時類型和數值都要比對.
明確的寫出了 如果一個數值和一個字元串比較,那麼會將字元串轉換為數值(而不是相反,將數值轉化為字元串)
然而,php是如何將一個字元串轉化為數值的呢,我們繼續查看php手冊
當一個字元串被當作一個數值來取值,其結果和類型如下:如果該字元串沒有包含 『.』,』e』 或 『E』 並且其數字值在整型的範圍之內(由 PHP_INT_MAX 所定義),該字元串將被當成 integer 來取值。其它所有情況下都被作為 float 來取值。該字元串的開始部分決定了它的值。如果該字元串以合法的數值開始,則使用該數值。否則其值為 0(零)。合法數值由可選的正負號,後面跟著一個或多個數字(可能有小數點),再跟著可選的指數部分。指數部分由 『e』 或 『E』 後面跟著一個或多個數字構成。
這是官方手冊上面的幾個例子
我們大概可以總結出如下的規則:當一個字元串被轉換為數值時
如果一個字元串為 「合法數字+e+合法數字」類型,將會解釋為科學計數法的浮點數
如果一個字元串為 「合法數字+ 不可解釋為合法數字的字元串」類型,將會被轉換為該合法數字的值,後面的字元串將會被丟棄
如果一個字元串為「不可解釋為合法數字的字元串+任意」類型,則被轉換為0! 為0…為0
當然,上面的那些等式對於===都是false的,原本一些應該用===的地方誤用了==,導致了可以注入的地方。
示例代碼 1:利用轉為數字后相等的漏洞
但是我們看到,最終比較兩者的哈希的時候,使用的是等於 而不是 全等於 ,因此可以利用一下這個漏洞
再回頭看一
md5函數
string md5 ( string $str [, bool $raw_output = false ] )
str原始字元串。raw_output如果可選的 raw_output 被設置為 TRUE,那麼 MD5 報文摘要將以16位元組長度的原始二進位格式返回。
可以知道,第二個參數為true的時候,顯示16位的結果,而為false和沒有第二個參數時,為32位的16進位碼(16位的結果是把32位的作為ASCII碼進行解析)
16進位的數據中是含有e的,可以構建使得兩個數字比較的,這裡有一個現成的例子:
md5('240610708') //0e462097431906509019562988736854.md5('QNKCDZO') //0e830400451993494058024219903391
可以看到,這兩個字元串一個只包含數字,一個只包含字母,雖然兩個的哈希不一樣,但是都是一個形式:0e 純數字這種格式的字元串在判斷相等的時候會被認為是科學計數法的數字,先做字元串到數字的轉換。
轉換后都成為了0的好多好多次方,都是0,相等。(大家可以自己嘗試一下)因此
用===可以避免這一漏洞。示例代碼2: 利用 類』a'==0的漏洞
{"key":"your input"}
我們該如何破解?想」a」==0這個漏洞,之用我們使$json->key是一個數字類型的變數就可以,怎麼做到呢?
php的json_decode函數會根據json數據中的數據類型來將其轉換為php中的相應類型的數據,也就是說,如果我們在json中傳一個string類型,那麼該變數就是string,如果傳入的是number,則該變數為number。因此,我們如果傳入一個數字,就可以使之相等。網頁中的表單可能限制了所有的輸入都是string,即使輸入數字,傳入的東西也是
{"key":"0"}
這是一個字元串0,我們需要讓他為數字類型,用burp攔截,把兩個雙引號去掉,變成這樣:
{"key":0}
即可。
值得討論的一點是,在這種方法的漏洞利用中,很難在直接表單類型的POST的數據中使用,這是為什麼呢,這個和HTTP協議有關。首先,我們看一下,在POST給伺服器的數據中,有幾種類型,也就是HTTP header中的Content-Type:
application/x-www-form-urlencoded multipart/form-data application/json application/xml
第一個application/x-www-form-urlencoded,是一般表單形式提交的content-type第二個,是包含文件的表單。第三,四個,分別是json和xml,一般是js當中上傳的.
但是因為在直接的POST的payload當中是無法區分字元串和數字的,因為在其中並沒有引號出現,舉一個抓包的例子
可以看到,payload是放在http包的最後面的,而且都是以沒有引號的形式傳遞的,並沒有辦法區分到底是字元串還是數字。因此,PHP將POST的數據全部保存為字元串形式,也就沒有辦法注入數字類型的數據了而JSON則不一樣,JSON本身是一個完整的字元串,經過解析之後可能有字元串,數字,布爾等多種類型。2. strcmp漏洞
註:這一個漏洞適用與5.3之前版本的php
我們首先看一下這個函數,這個函數是用於比較字元串的函數
int strcmp ( string $str1 , string $str2 )
參數 str1第一個字元串。str2第二個字元串。如果 str1 小於 str2 返回 < 0; 如果 str1 大於 str2 返回 > 0;如果兩者相等,返回 0。
可知,傳入的期望類型是字元串類型的數據,但是如果我們傳入非字元串類型的數據的時候,這個函數將會有怎麼樣的行為呢?實際上,當這個函數接受到了不符合的類型,這個函數將發生錯誤,但是在5.3之前的php中,顯示了報錯的警告信息后,將return 0 !!!! 也就是雖然報了錯,但卻判定其相等了。
這對於使用這個函數來做選擇語句中的判斷的代碼來說簡直是一個致命的漏洞,當然,php官方在後面的版本中修復了這個漏洞,使得報錯的時候函數不返回任何值。但是我們仍然可以使用這個漏洞對使用老版本php的網站進行滲透測試。看一段示例代碼:
對於這段代碼,我們能用什麼辦法繞過驗證呢, 只要我們
$_POST['password']是一個數組或者一個object即可,但是上一個問題的時候說到過,只能上傳字元串類型,那我們又該如何做呢。
其實php為了可以上傳一個數組,會把結尾帶一對中括弧的變數,例如
xxx的name(就是$_POST中的key),當作一個名字為
xxx的數組構造類似如下的request
即可使得上述代碼繞過驗證成功。
3 總結
這一類型的漏洞的特點主要就是利用PHP中 的類型特性來繞過驗證。由於 == 和 === 有著明顯的區分,因此,估計短期內PHP的作者並不會調整對於這兩個符號的策略。
而對於開發市場而言,隨著培訓機構的增多,後端程序員尤其是php後端程序員的門檻越來越低,其水平必定也是良莠不齊,這些二把刀程序員可能帶來更多的此類對於特性的不當使用導致的漏洞,因此這類漏洞仍然是非常具有利用價值的。
總結一下,對於開發人員,需要堅持幾個習慣:
認真閱讀PHP manual,不能以其他語言的經驗來完全帶入php進行編碼
在使用一個運算符或者函數之前,詳細的查看文檔,搞清楚函數在什麼樣的條件下,會有怎樣的行為。
記住保證安全的幾句箴言:任何用戶輸入都是不可信的!對於web應用來說,前端(瀏覽器端)的安全限制只能起到防止一般用戶的誤輸入行為,完全不可能對於黑帽子的行為有任何的防禦作用
因此,在防禦這個漏洞的過程中,保證幾件事情:
基本上就可以完美的防禦這一類的漏洞。
而對於滲透測試人員,在代碼審計的過程中,對於有
==strcmp的比較也應極為敏感 。在黑盒滲透的時候也可以對於代碼進行猜測,結合信息搜集過程中的一些版本特性,利用這些漏洞來繞過驗證。
*本文原創作者:sarleon,屬Freebuf原創獎勵計劃,未經許可禁止轉載