PHP代碼安全雜談1

一、精度繞過缺陷

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

image.png

以十進(jìn)制能夠精確表示的有理數(shù)如 0.10.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)行


image.png

嚴(yán)格比較符,會(huì)先判斷兩種字符串的類型是否相等,再比較。

松散比較符,會(huì)先將字符串類型轉(zhuǎn)換成相同,再比較。

一個(gè)簡單的例子

image.png

另一個(gè)例子

image.png
var_dump(0=="gg"); //true
var_dump(0==="gg"); //false
var_dump(1=="gg"); //false

0gg進(jìn)行松散性質(zhì)的不嚴(yán)格比較,會(huì)將gg轉(zhuǎn)換為數(shù)值,強(qiáng)制轉(zhuǎn)換,由于gg是字符串,轉(zhuǎn)化的結(jié)果是0,所以 輸出 true

0gg進(jìn)行嚴(yán)格 性質(zhì)的嚴(yán)格比較,這里的gg是字符串類型,和int類型的0不相等,所以輸出 false

0gg進(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

11gg進(jìn)行松散性質(zhì)的不嚴(yán)格比較,這里1gg被強(qiáng)制轉(zhuǎn)換為int類型的時(shí)候會(huì)從字符串的第一位開始做判斷進(jìn)行轉(zhuǎn)換,這里的1gg第一位是1,所以這里1gg被轉(zhuǎn)換為1,所以輸出 true

1gg1進(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。

0e1230e456相互不嚴(yán)格性質(zhì)比較的時(shí)候,會(huì)將0e這類字符串識(shí)為科學(xué)技術(shù)法的數(shù)字,0的無論多少次方都是零,所以相等,輸出 true

0e1230eabc相互進(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è)字段namepassword,獲得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]=240610708

image.png

參考文獻(xiàn)

PHP代碼安全雜談

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,560評論 0 13
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,246評論 0 38
  • 大概,時(shí)間久了,就長肉里面了,最后拔出來也是傷筋動(dòng)骨,哪怕是最快的刀也剔不凈啊,那怎么辦,老人的話是可以偷來的,不...
    向楊春泥閱讀 219評論 0 2
  • 周六 起床吃飯,然后開始對暖小姐的拉練計(jì)劃開始。對于她的晚睡,實(shí)在是太崩潰。 從家里出發(fā),事先商量好我們?nèi)汖?,?..
    小豬天堂閱讀 575評論 4 1
  • 待續(xù)
    上帝死了眾神在墮落閱讀 442評論 0 0

友情鏈接更多精彩內(nèi)容