php作為受歡迎的開源腳本語言,php是世界上最好的語言,越來越多的應用于Web開發領域。php屬于弱類型語言,即定義變量的時候不用聲明它是什么類型。作為一個程序員,弱類型確實給程序員書寫代碼帶來了很大的便利,但是也帶來了一些不必要的問題。
0x01 ==和===的問題
==是比較運算,它不會去檢查條件式的表達式的類型
===是恒等,它會檢查查表達式的值與類型是否相等。
NULL,0,"0",array()使用==和false比較時,都是會返回true的,而使用===卻不會
比較操作
一個數字和一個字符串進行比較,PHP會把字符串轉換成數字再進行比較。PHP轉換的規則的是:若字符串以數字開頭,則取開頭數字作為轉換結果,若無則輸出0。例如:123abc轉換后應該是123,而abc則為0,0==0這當然是成立的啦!所以,0 =='abc'是成立的。當有一個對比參數是整數的時候,會把另外一個參數強制轉換為整數。
Hash比較
"0e132456789"=="0e7124511451155" //true
"0e123456abc"=="0e1dddada" //false
"0e1abc"=="0" //true
在進行比較運算時,如果遇到了0e\d+這種字符串,就會將這種字符串解析為科學計數法。所以上面例子中2個數的值都是0因而就相等了。如果不滿足0e\d+這種模式就不會相等。
md5比較
<?php
$a = md5('240610708'); // = 0e462097431906509019562988736854
$b = md5('QNKCDZO'); // = 0e830400451993494058024219903391
var_dump($a == $b);
?>
返回結果bool(true)
240610708 和 QNKCDZO md5值類型相似,但并不相同,在“==”相等操作符的運算下,結果返回了true。這是個經典的漏洞,只需要找到md5值為0exxx(xxx全為數字,共30位),這里我提供4個都可以通過的值:240610708、QNKCDZO、aabg7XSs、aabC9RqS
擴展:
先注冊密碼為240610708的用戶A。
然后用密碼QNKCDZO嘗試登錄用戶A。
倘若成功登錄,則證明此網站采用了不完備的加密體制md5一次加密。
先注冊密碼為0E33455555的用戶A。
然后用密碼0E234230570345嘗試登錄用戶A。
倘若成功登錄,則證明此網站采用了明文進行存儲密碼!
0x02 轉換問題
類型轉換
string轉int:intval()函數
var_dump(intval('2')) //2
var_dump(intval('3abcd')) //3
var_dump(intval('abcd')) //0
說明intval()轉換的時候,會將從字符串的開始進行轉換知道遇到一個非數字的字符。即使出現無法轉換的字符串,intval()不會報錯而是返回0。
十六進制轉換
"0x1e240"=="123456" //true
"0x1e240"==123456 //true
"0x1e240"=="1e240" //false
當其中的一個字符串是0x開頭的時候,ox開頭表示16進制,PHP會將此字符串解析成為十進制然后再進行比較,0×1e240解析成為十進制就是123456,所以與int類型和string類型的123456比較都是相等。
這樣在不讓輸入數字但是后面還要和一串數字比較的情況下可以使用這種方法,將后面要比較的數字轉為16進制,這樣就可以繞過。
0x03 數組問題
if (isset($_GET['name']) and isset($_GET['password'])) {
if ($_GET['name'] == $_GET['password'])
print 'Your password can not be your name.';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
name和password的值不能相同,其次,sha1加密之后的name和password的值又必須完全相同 我們知道,在php中,$a[] = 1;代表著$a[x] = 1;所以name[] = 1和password[]= 2相比較,可以跳過第一個判斷,而如果使用sha1對一個數組進行加密,返回的將是NULL,NULL===NULL,這是成立的,所以構造兩個數組繞過
0x04 常見函數問題
md5()
string md5 ( string $str [, bool $raw_output = false ] )
md5()中需要傳入的是一個string類型的參數,當我們傳遞一個array時,它是不會報錯的,函數無法求出array的MD5值,這樣導致任意兩個array的MD5值都相等,從而繞過輸入數值的判斷,在ctf代碼審計中經常遇見。
strcmp()
int strcmp ( string $str1 , string $str2 )
進行字符串長度的比較,傳入兩個string的參數。如果str1小于str2,返回-1,相等返回0,否則返回1。strcmp函數比較字符串的本質是將兩個變量轉換為ascii,然后進行減法運算,然后根據運算結果來決定返回值。如果傳入的參數為數字或數組
例1:
$array=[1,2,3];
var_dump(strcmp($array,'123')); //null,在某種意義上null也就是相當于false。
例2:
if (isset($_GET['password'])) {
if (strcmp($_GET['password'], $flag) == 0)
die('Flag: '.$flag);
else
print 'Invalid password';
}
此處使用strcmp()對pass和flag進行判斷,如果==0,則輸出flag。但是strcmp()函數只有在相等的情況下返回0。那么我們傳入一個數組,它會返回NULL,而判斷使用了==,而NULL==0是bool(true),這樣就成功繞過。
switch()
如果switch是數字類型的case的判斷時,switch會將其中的參數轉換為int類型。
in_array()
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
如果strict參數沒有提供,那么in_array就會使用松散比較來判斷$needle是否在$haystack中。當strince的值為true時,in_array()會比較needls的類型和haystack中的類型是否相同。
$array=[0,1,2,'3'];
var_dump(in_array('abc', $array)); //true
var_dump(in_array('1bc', $array)); //true
可以看到上面的情況返回的都是true,因為’abc’會轉換為0,’1bc’轉換為1。
ereg()
int ereg(string pattern, string string, array [regs]);
字符串對比解析,ereg函數存在NULL截斷漏洞,當ereg讀取字符串string時,如果遇到了%00,后面的字符串就不會被解析。
bool類型的true跟任意字符串可以弱類型相等
目前就整理這么多,以后有新的會繼續添加上去
松散比較的表格