【漏洞分析】 织梦前台任意用户密码修改

百家 作者:锦行科技 2018-01-10 11:36:54


常见的弱类型问题

类型转换问题

类型转换是无法避免的问题。例如需要将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')=0e54599327
4517709034328855841020就是0
的n次方,所以还是等于0

但是要注意:

"0e123456abc"=="0e1dddada"//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);

}

注意一下$send为N

我们跟进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."rnReply-To: $cfg_adminemail";

    $mailbody = "亲爱的".$userid.":rn您好!感谢您使用".$cfg_webname."网。rn".$cfg_webname."应您的要求,重新设置密码:(注:如果您没有提出申请,请检查您的信息是否泄漏。)rn本次临时登陆密码为:".$randval." 请于三天内登陆下面网址确认修改。rn".$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&id=".$mid."&key=".$randval);

            }

        }

        else

        {

            return ShowMsg('对不起修改失败,请联系管理员', 'login.php');

        }

}

这里直接是对dede_pwd_tmp表插入临时密码,临时密码为$randval = random(8);是8位,但是别急。紧接着插入完成之后ShowMsg('稍后跳转到修改页', $cfg_basehost.$cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".$mid."&key=".$randval);也就是说在insert完成之后跳转把$randval输出到了页面。





也就是这个key,这个key就是管理员的临时密码。

利用方法:

先注册一个帐号并登录,然后访问:

http://localhost/test.php?i=0.0

dopost=safequestion&safequestion=0.0&safeanswer=&id=1

就可以发现上面那个包了




结语

不要相信用户输入。应多使用===来避免弱类型安全问题.



锦行,让安全一路随行

听说这个公众号有最前沿的欺骗技术分享、最专业的内网安全干货、最新的漏洞信息、最快的安全资讯速递。


如果您对我们的公众号运营有建议,请微信后台留言或联系邮箱:market@jeeseen.com 。建议被采纳,将有机会获得锦行科技定制的幻云陀螺一个哦

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接