《php漏洞:檢查相等時(shí)的應(yīng)避免的陷阱》要點(diǎn):
本文介紹了php漏洞:檢查相等時(shí)的應(yīng)避免的陷阱,希望對(duì)您有用。如果有疑問(wèn),可以聯(lián)系我們。
相關(guān)主題:PHP安全
PHP是一門(mén)弱類(lèi)型的語(yǔ)言。很方便學(xué)習(xí)和應(yīng)用,但在特定時(shí)候,正是因?yàn)槿躅?lèi),所以會(huì)導(dǎo)致有程序的缺陷。
1.弱類(lèi)型的比較==導(dǎo)致的漏洞
注:這些漏洞適用于所有版本的php
先來(lái)復(fù)習(xí)一下基本的語(yǔ)法:php中有如下兩種比較符號(hào):兩個(gè)等號(hào)和三個(gè)等號(hào)(這一點(diǎn)和Javascript)有些類(lèi)似
$a==$b
$a===$b
明確的看到,兩個(gè)等于號(hào)的等于會(huì)在比較的時(shí)候進(jìn)行類(lèi)型轉(zhuǎn)換的比較。
如果比較一個(gè)數(shù)字和字符串或者比較涉及到數(shù)字內(nèi)容的字符串,則字符串會(huì)被轉(zhuǎn)換為數(shù)值并且比較按照數(shù)值來(lái)進(jìn)行。此規(guī)則也適用于 switch 語(yǔ)句。當(dāng)用 === 或 !== 進(jìn)行比較時(shí)則不進(jìn)行類(lèi)型轉(zhuǎn)換,因?yàn)榇藭r(shí)類(lèi)型和數(shù)值都要比對(duì).
明確的寫(xiě)出了 如果一個(gè)數(shù)值和一個(gè)字符串比較,那么會(huì)將字符串轉(zhuǎn)換為數(shù)值(而不是相反,將數(shù)值轉(zhuǎn)化為字符串)
然而,php是如何將一個(gè)字符串轉(zhuǎn)化為數(shù)值的呢,
當(dāng)一個(gè)字符串被當(dāng)作一個(gè)數(shù)值來(lái)取值,其結(jié)果和類(lèi)型如下:如果該字符串沒(méi)有包含 ‘.’,’e’ 或 ‘E’ 并且其數(shù)字值在整型的范圍之內(nèi)(由 PHP_INT_MAX 所定義),該字符串將被當(dāng)成 integer 來(lái)取值。其它所有情況下都被作為 float 來(lái)取值。該字符串的開(kāi)始部分決定了它的值。如果該字符串以合法的數(shù)值開(kāi)始,則使用該數(shù)值。否則其值為 0(零)。合法數(shù)值由可選的正負(fù)號(hào),后面跟著一個(gè)或多個(gè)數(shù)字(可能有小數(shù)點(diǎn)),再跟著可選的指數(shù)部分。指數(shù)部分由 ‘e’ 或 ‘E’ 后面跟著一個(gè)或多個(gè)數(shù)字構(gòu)成。
這是官方手冊(cè)上面的幾個(gè)例子
<?php $foo = 1 + "10.5"; // $foo is float (11.5) $foo = 1 + "-1.3e3"; // $foo is float (-1299) $foo = 1 + "bob-1.3e3"; // $foo is integer (1) $foo = 1 + "bob3"; // $foo is integer (1) $foo = 1 + "10 Small Pigs"; // $foo is integer (11) $foo = 4 + "10.2 Little Piggies"; // $foo is float (14.2) $foo = "10.0 pigs " + 1; // $foo is float (11) $foo = "10.0 pigs " + 1.0; // $foo is float (11) ?>
我們大概可以總結(jié)出如下的規(guī)則:當(dāng)一個(gè)字符串被轉(zhuǎn)換為數(shù)值時(shí)
如果一個(gè)字符串為 “合法數(shù)字+e+合法數(shù)字”類(lèi)型,將會(huì)解釋為科學(xué)計(jì)數(shù)法的浮點(diǎn)數(shù)
如果一個(gè)字符串為 “合法數(shù)字+ 不可解釋為合法數(shù)字的字符串”類(lèi)型,將會(huì)被轉(zhuǎn)換為該合法數(shù)字的值,后面的字符串將會(huì)被丟棄
如果一個(gè)字符串為“不可解釋為合法數(shù)字的字符串+任意”類(lèi)型,則被轉(zhuǎn)換為0! 為0…為0
當(dāng)然,上面的那些等式對(duì)于===都是false的,原本一些應(yīng)該用===的地方誤用了==,導(dǎo)致了可以注入的地方。
示例代碼 1:利用轉(zhuǎn)為數(shù)字后相等的漏洞
這是一個(gè)ctf的題目,非常有趣,可以看到,要求給出兩字符串,一個(gè)是純數(shù)字型,一個(gè)只能出現(xiàn)字符,使兩個(gè)的md5哈希值相等,然而這種強(qiáng)碰撞在密碼學(xué)上都是無(wú)法做到的。
但是我們看到,最終比較兩者的哈希的時(shí)候,使用的是等于 而不是 全等于 ,因此可以利用一下這個(gè)漏洞
再回頭看一 md5() 函數(shù):
string md5 ( string $str [, bool $raw_output = false ] )
str原始字符串。raw_output如果可選的 raw_output 被設(shè)置為 TRUE,那么 MD5 報(bào)文摘要將以16字節(jié)長(zhǎng)度的原始二進(jìn)制格式返回。
可以知道,第二個(gè)參數(shù)為true的時(shí)候,顯示16位的結(jié)果,而為false和沒(méi)有第二個(gè)參數(shù)時(shí),為32位的16進(jìn)制碼(16位的結(jié)果是把32位的作為ASCII碼進(jìn)行解析)
16進(jìn)制的數(shù)據(jù)中是含有e的,可以構(gòu)建使得兩個(gè)數(shù)字比較的,這里有一個(gè)現(xiàn)成的例子:
md5('240610708')
//0e462097431906509019562988736854.md5('QNKCDZO')
//0e830400451993494058024219903391
可以看到,這兩個(gè)字符串一個(gè)只包含數(shù)字,一個(gè)只包含字母,雖然兩個(gè)的哈希不一樣,但是都是一個(gè)形式:0e 純數(shù)字這種格式的字符串在判斷相等的時(shí)候會(huì)被認(rèn)為是科學(xué)計(jì)數(shù)法的數(shù)字,先做字符串到數(shù)字的轉(zhuǎn)換。
轉(zhuǎn)換后都成為了0的好多好多次方,都是0,相等。(大家可以自己嘗試一下)因此用===可以避免這一漏洞。
示例代碼2: 利用 類(lèi)’a'==0的漏洞:
這次這個(gè)例子是傳入一個(gè)JSON的數(shù)據(jù),JSON在RESTful的網(wǎng)站中是很常用的一種數(shù)據(jù)傳輸?shù)母袷健_@個(gè)表單會(huì)把一個(gè)name為key的input的數(shù)據(jù)作為json傳到服務(wù)端
{"key":"your input"}
我們?cè)撊绾纹平猓肯搿盿”==0這個(gè)漏洞,之用我們使$json->key是一個(gè)數(shù)字類(lèi)型的變量就可以,怎么做到呢?
php的json_decode()函數(shù)會(huì)根據(jù)json數(shù)據(jù)中的數(shù)據(jù)類(lèi)型來(lái)將其轉(zhuǎn)換為php中的相應(yīng)類(lèi)型的數(shù)據(jù),也就是說(shuō),如果我們?cè)趈son中傳一個(gè)string類(lèi)型,那么該變量就是string,如果傳入的是number,則該變量為number。因此,我們?nèi)绻麄魅胍粋€(gè)數(shù)字,就可以使之相等。網(wǎng)頁(yè)中的表單可能限制了所有的輸入都是string,即使輸入數(shù)字,傳入的東西也是
{"key":"0"}
這是一個(gè)字符串0,我們需要讓他為數(shù)字類(lèi)型,用burp攔截,把兩個(gè)雙引號(hào)去掉,變成這樣: {"key":0} 即可。
值得討論的一點(diǎn)是,在這種方法的漏洞利用中,很難在直接表單類(lèi)型的POST的數(shù)據(jù)中使用,這是為什么呢,這個(gè)和HTTP協(xié)議有關(guān)。首先,我們看一下,在POST給服務(wù)器的數(shù)據(jù)中,有幾種類(lèi)型,也就是HTTP header中的Content-Type:
application/x-www-form-urlencoded multipart/form-data application/json application/xml
第一個(gè)application/x-www-form-urlencoded,是一般表單形式提交的content-type第二個(gè),是包含文件的表單。第三,四個(gè),分別是json和xml,一般是js當(dāng)中上傳的.
但是因?yàn)樵谥苯拥腜OST的payload當(dāng)中是無(wú)法區(qū)分字符串和數(shù)字的,因?yàn)樵谄渲胁](méi)有引號(hào)出現(xiàn),舉一個(gè)抓包的例子
可以看到,payload是放在http包的最后面的,而且都是以沒(méi)有引號(hào)的形式傳遞的,并沒(méi)有辦法區(qū)分到底是字符串還是數(shù)字。因此,PHP將POST的數(shù)據(jù)全部保存為字符串形式,也就沒(méi)有辦法注入數(shù)字類(lèi)型的數(shù)據(jù)了而JSON則不一樣,JSON本身是一個(gè)完整的字符串,經(jīng)過(guò)解析之后可能有字符串,數(shù)字,布爾等多種類(lèi)型。
2. strcmp漏洞
注:這一個(gè)漏洞適用與5.3之前版本的php
我們首先看一下這個(gè)函數(shù),這個(gè)函數(shù)是用于比較字符串的函數(shù)
int strcmp ( string $str1 , string $str2 )
參數(shù) str1第一個(gè)字符串。str2第二個(gè)字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果兩者相等,返回 0。
可知,傳入的期望類(lèi)型是字符串類(lèi)型的數(shù)據(jù),但是如果我們傳入非字符串類(lèi)型的數(shù)據(jù)的時(shí)候,這個(gè)函數(shù)將會(huì)有怎么樣的行為呢?實(shí)際上,當(dāng)這個(gè)函數(shù)接受到了不符合的類(lèi)型,這個(gè)函數(shù)將發(fā)生錯(cuò)誤,但是在5.3之前的php中,顯示了報(bào)錯(cuò)的警告信息后,將return 0 !!!! 也就是雖然報(bào)了錯(cuò),但卻判定其相等了。
這對(duì)于使用這個(gè)函數(shù)來(lái)做選擇語(yǔ)句中的判斷的代碼來(lái)說(shuō)簡(jiǎn)直是一個(gè)致命的漏洞,當(dāng)然,php官方在后面的版本中修復(fù)了這個(gè)漏洞,使得報(bào)錯(cuò)的時(shí)候函數(shù)不返回任何值。但是我們?nèi)匀豢梢允褂眠@個(gè)漏洞對(duì)使用老版本php的網(wǎng)站進(jìn)行滲透測(cè)試。看一段示例代碼:
對(duì)于這段代碼,我們能用什么辦法繞過(guò)驗(yàn)證呢, 只要我們$_POST['password']是一個(gè)數(shù)組或者一個(gè)object即可,但是上一個(gè)問(wèn)題的時(shí)候說(shuō)到過(guò),只能上傳字符串類(lèi)型,那我們又該如何做呢。
其實(shí)php為了可以上傳一個(gè)數(shù)組,會(huì)把結(jié)尾帶一對(duì)中括號(hào)的變量,例如 xxx[]的name(就是$_POST中的key),當(dāng)作一個(gè)名字為xxx的數(shù)組構(gòu)造類(lèi)似如下的request,即可使得上述代碼繞過(guò)驗(yàn)證成功。
3 總結(jié):
這一類(lèi)型的漏洞的特點(diǎn)主要就是利用PHP中 的類(lèi)型特性來(lái)繞過(guò)驗(yàn)證。由于 == 和 === 有著明顯的區(qū)分,因此,估計(jì)短期內(nèi)PHP的作者并不會(huì)調(diào)整對(duì)于這兩個(gè)符號(hào)的策略。
而對(duì)于開(kāi)發(fā)市場(chǎng)而言,隨著培訓(xùn)機(jī)構(gòu)的增多,后端程序員尤其是php后端程序員的門(mén)檻越來(lái)越低,其水平必定也是良莠不齊,這些二把刀程序員可能帶來(lái)更多的此類(lèi)對(duì)于特性的不當(dāng)使用導(dǎo)致的漏洞,因此這類(lèi)漏洞仍然是非常具有利用價(jià)值的。
總結(jié)一下,對(duì)于開(kāi)發(fā)人員,需要堅(jiān)持幾個(gè)習(xí)慣:
認(rèn)真閱讀PHP manual,不能以其他語(yǔ)言的經(jīng)驗(yàn)來(lái)完全帶入php進(jìn)行編碼
在使用一個(gè)運(yùn)算符或者函數(shù)之前,詳細(xì)的查看文檔,搞清楚函數(shù)在什么樣的條件下,會(huì)有怎樣的行為。
記住保證安全的幾句箴言:任何用戶輸入都是不可信的!對(duì)于web應(yīng)用來(lái)說(shuō),前端(瀏覽器端)的安全限制只能起到防止一般用戶的誤輸入行為,完全不可能對(duì)于黑帽子的行為有任何的防御作用。
因此,在防御這個(gè)漏洞的過(guò)程中,保證幾件事情:
在所有可能的地方,都使用===來(lái)代替==
對(duì)于用戶輸入做過(guò)濾和類(lèi)型檢查
盡量使用新版本的php,apache
基本上就可以完美的防御這一類(lèi)的漏洞。
而對(duì)于滲透測(cè)試人員,在代碼審計(jì)的過(guò)程中,對(duì)于有==,strcmp的比較也應(yīng)極為敏感 。在黑盒滲透的時(shí)候也可以對(duì)于代碼進(jìn)行猜測(cè),結(jié)合信息搜集過(guò)程中的一些版本特性,利用這些漏洞來(lái)繞過(guò)驗(yàn)證。
轉(zhuǎn)載請(qǐng)注明本頁(yè)網(wǎng)址:
http://www.fzlkiss.com/jiaocheng/77.html