一、精度繞過缺陷
php通常采用的是 IEEE 754 雙精度格式,則由于取整而導(dǎo)致的最大相對誤差為 1.11e-16。所以,可能會(huì)造成一些不必要的麻煩:

以十進(jìn)制能夠精確表示的有理數(shù)如 0.1 或 0.7,無論有多少尾數(shù)都不能被內(nèi)部所使用的二進(jìn)制精確表示,因此不能在不丟失一點(diǎn)點(diǎn)精度的情況下轉(zhuǎn)換為二進(jìn)制的格式。這就會(huì)造成混亂的結(jié)果:例如,floor((0.1+0.7)*10) 通常會(huì)返回 7 而不是預(yù)期中的8,因?yàn)樵摻Y(jié)果內(nèi)部的表示其實(shí)是類似 7.9999999999999991118…。
二、類型轉(zhuǎn)換的缺陷
理論
PHP提供了is_numeric函數(shù),用來變量判斷是否為數(shù)字。PHP弱類型語言的一個(gè)特性,當(dāng)一個(gè)整形和一個(gè)其他類型行比較的時(shí)候,會(huì)先把其他類型intval數(shù)字化再比。
案例代碼
<?php
error_reporting(0);
$flag = 'flag{1S_numer1c_Not_S4fe}';
id =_GET['id']; is_numeric($id)?die("Sorry...."):NULL;
if(id>665){ echoflag; }
?>
write-up
首先對GET傳入的id進(jìn)行檢測。通過is_numeric函數(shù)進(jìn)行檢測,如果是數(shù)字就GG,如果不是數(shù)字就與655比較,大于655輸出正確答案。看起來似乎很矛盾,這里又要用到php作為弱類型語言的一個(gè)特征:當(dāng)一個(gè)整形和一個(gè)其他類型行比較的時(shí)候,會(huì)先把其他類型intval數(shù)字化再比。也就是說將id構(gòu)造成數(shù)字+字符形式就能成功得到flag,如:id=666gg。
三、松散比較符的缺陷
理論
php比較相等性的運(yùn)算符有兩種,一種是嚴(yán)格比較,另一種是松散比較。
如果比較一個(gè)數(shù)字和字符串或者比較涉及到數(shù)字內(nèi)容的字符串,則字符串會(huì)被轉(zhuǎn)換成數(shù)值并且比較按照數(shù)值來進(jìn)行

嚴(yán)格比較符,會(huì)先判斷兩種字符串的類型是否相等,再比較。
松散比較符,會(huì)先將字符串類型轉(zhuǎn)換成相同,再比較。
一個(gè)簡單的例子

另一個(gè)例子

var_dump(0=="gg"); //true
var_dump(0==="gg"); //false
var_dump(1=="gg"); //false
0與gg進(jìn)行松散性質(zhì)的不嚴(yán)格比較,會(huì)將gg轉(zhuǎn)換為數(shù)值,強(qiáng)制轉(zhuǎn)換,由于gg是字符串,轉(zhuǎn)化的結(jié)果是0,所以 輸出 true
0與gg進(jìn)行嚴(yán)格 性質(zhì)的嚴(yán)格比較,這里的gg是字符串類型,和int類型的0不相等,所以輸出 false
0與gg進(jìn)行松散性質(zhì)的不嚴(yán)格比較,會(huì)將gg轉(zhuǎn)換為數(shù)值,強(qiáng)制轉(zhuǎn)換,由于gg是字符串,轉(zhuǎn)化的結(jié)果是0,不等于1,所以輸出 false
var_dump(1=="1gg"); //true
var_dump(1=="gg1"); //false
1與1gg進(jìn)行松散性質(zhì)的不嚴(yán)格比較,這里1gg被強(qiáng)制轉(zhuǎn)換為int類型的時(shí)候會(huì)從字符串的第一位開始做判斷進(jìn)行轉(zhuǎn)換,這里的1gg第一位是1,所以這里1gg被轉(zhuǎn)換為1,所以輸出 true
1與gg1進(jìn)行嚴(yán)格 性質(zhì)的嚴(yán)格比較,字符串gg1的第一位不是數(shù)字,所以它被強(qiáng)制轉(zhuǎn)換為0,所以輸出 false
var_dump("0e123" == "0e456"); //true
var_dump("0e123" == "0eabc"); //flase
這里比較特殊,字符串中出現(xiàn)了0e,PHP手冊介紹如下:
當(dāng)一個(gè)字符串欸當(dāng)作一個(gè)數(shù)值來取值,其結(jié)果和類型如下:如果該字符串沒有包含'.','e','E'并且其數(shù)值值在整形的范圍之內(nèi)
該字符串被當(dāng)作int來取值,其他所有情況下都被作為float來取值,
該字符串的開始部分決定了它的值,如果該字符串以合法的數(shù)值開始,則使用該數(shù)值,否則其值為0。
0e123與0e456相互不嚴(yán)格性質(zhì)比較的時(shí)候,會(huì)將0e這類字符串識(shí)為科學(xué)技術(shù)法的數(shù)字,0的無論多少次方都是零,所以相等,輸出true
0e123與0eabc相互進(jìn)行不嚴(yán)格性質(zhì)比較的時(shí)候,本應(yīng)該將0e這類字符串識(shí)為科學(xué)技術(shù)法的數(shù)字,但是這里的0e后面跟著的是abc,數(shù)學(xué)中科學(xué)計(jì)數(shù)的指數(shù)不可以包含字母。所以這里字符串中雖然是0e開頭,但是后面的abc卻不符合科學(xué)技法的規(guī)范,所以輸出是false四、sha1() md5()加密函數(shù)漏洞缺陷
理論
md5()和sha1()對一個(gè)數(shù)組進(jìn)行加密將返回 NULL一個(gè)小例子
if ($_GET['name'] == $_GET['password']) print 'Your password can not be your name.'; else if (sha1($_GET['name']) === sha1($_GET['password'])) die('Flag: '.$flag);
GET類型提交了兩個(gè)字段name和password,獲得flag要求的條件是:name != password
sha1(name) == sha1(password)
這個(gè)乍看起來這是不可能的,但是這里利用
sha1()函數(shù)在處理數(shù)組的時(shí)候由于無法處理將返回NULL可以繞過if語句的驗(yàn)證,if條件成立將獲得flag。 構(gòu)造語句如下:?name[]=a&password[]=b
這里符合了2個(gè)拿到flag的條件:
a不等于b
name和password由于是數(shù)組,經(jīng)過sha1()函數(shù)嫁給后都返回
NULL五、字符串處理函數(shù)漏洞缺陷
理論
strcmp()函數(shù):比較兩個(gè)字符串(區(qū)分大小寫).具體的用法解釋如下:
參數(shù) `str1`第一個(gè)字符串。 參數(shù) `str2`第二個(gè)字符串。 如果 `str1` 小于 `str2` 返回 `< 0`; 如果 `str1` 大于 `str2` 返回 `> 0`; 如果兩者相等,返回 0。這個(gè)函數(shù)接受到了不符合的類型,例如
數(shù)組類型,函數(shù)將發(fā)生錯(cuò)誤。但是在5.3之前的php中,顯示了報(bào)錯(cuò)的警告信息后,將return 0!!!! 也就是雖然報(bào)了錯(cuò),但卻判定其相等了。
ereg()函數(shù):字符串正則匹配。strpos()函數(shù):查找字符串在另一字符串中第一次出現(xiàn)的位置,對大小寫敏感。這2個(gè)函數(shù)都是用來處理字符串的,但是在傳入數(shù)組參數(shù)后都將返回
NULL。六、parse_str函數(shù)變量覆蓋缺陷
理論
parse_str函數(shù)的作用就是解析字符串并注冊成變量,在注冊變量之前不會(huì)驗(yàn)證當(dāng)前變量是否存在,所以直接覆蓋掉已有變量。void parse_str ( string $str [, array &$arr ] )str 輸入的字符串。
arr 如果設(shè)置了第二個(gè)變量 arr,變量將會(huì)以數(shù)組元素的形式存入到這個(gè)數(shù)組,作為替代。實(shí)踐
測試代碼:
<?php error_reporting(0); $flag = 'flag{V4ri4ble_M4y_Be_C0verEd}'; if (empty($_GET['b'])) { show_source(__FILE__); die(); }else{ $a = "www.windbsy.cn"; $b = $_GET['b']; @parse_str($b); if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) { echo $flag; }else{ exit('your answer is wrong~'); } } ?>考察點(diǎn)
- parse_str變量覆蓋缺陷
write-up
找到核心代碼:
@parse_str($b); 這里使用了parse_str函數(shù)來傳遞b的變量值 if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) 這里用到的是文章上面的知識(shí)點(diǎn)md5()函數(shù)缺陷因?yàn)檫@里用到了
parse_str函數(shù)來傳遞b,if的語句的條件是拿$a[0]來比較的,有因?yàn)檫@里的變量a的值已經(jīng)三是固定的了:$a = "www.windbsy.cn";這里其實(shí)是我博客的地址~~ 整體代碼乍看起來又不可能,但是利用變量覆蓋函數(shù)的缺陷這里可以對
a的變量進(jìn)行重新賦值,后面的的if語句再利用本文前面提到的md5()比較缺陷進(jìn)行繞過:http://localhost/?b=a[0]=240610708image.png
參考文獻(xiàn)
PHP代碼安全雜談
