DedeCMS最近又有一个缺陷被爆出来,可以绕过一些判断条件从而导致前台任意用户登录,配合上一个重置密码漏洞,可以达到从前台登录管理员账户并修改dede_admin表里的密码,也就是真正修改了管理员密码。下面来简单分析一下。
前几天爆出的DedeCMS最新版(20180109)任意用户密码修改漏洞存在有一定局限性,一是只能影响没有设置密保问题的用户,二是不能重置管理员admin的密码,原因当时也说了,管理员信息存在另一个表dede_admin
中,而且管理员默认不允许从前台登录,所以就算更改了dede_member
里admin
的密码也没法登录。但是最近又有一个缺陷被爆出来,可以绕过一些判断条件从而导致前台任意用户登录,配合上一个重置密码漏洞,可以达到从前台登录管理员账户并修改dede_admin表里的密码,也就是真正修改了管理员密码。
下面来简单分析一下
先来看一下DedeCMS判断登录用户的逻辑
include/memberlogin.class.php:292
1 2 3 4 5 6 |
function IsLogin() { if($this->M_ID > 0) return TRUE; else return FALSE; } |
跟进$this->M_ID
看一下,170行
1 2 |
$this->M_ID = $this->GetNum(GetCookie("DedeUserID")); |
GetNum()include/memberlogin.class.php:398
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * 获取整数值 * * @access public * @param string $fnum 处理的数值 * @return string */ function GetNum($fnum){ $fnum = preg_replace("/[^0-9\.]/", '', $fnum); return $fnum; } |
正则匹配,去除了数字以外的字符,这里就可以构造一个利用点,一会儿再看
看一下GetCookie()
include/helpers/cookie.helper.php:54
关键点在这个判断条件
1 2 |
if($_COOKIE[$key.'__ckMd5'] != substr(md5($cfg_cookie_encode.$_COOKIE[$key]),0,16)) |
就是说从cookie中取到DedeUserID__ckMd5
值,与md5($cfg_cookie_encode.$_COOKIE[$key])
取前16位比较,相等才能进行下一步
我们知道admin的DedeUserID
为1,现在需要知道DedeUserID__ckMd5
的值
其实再思考一下,就算我们不知道admin的DedeUserID__ckMd5
值,只要能过这个if条件就能绕过接着往下走了,那我们可不可以利用其他用户来绕过if条件呢?
在本程序中从数据库取用户的过程其实很简单,就是简单的查询语句Select * From #@__member where mid='$mid'
。当我们利用其他用户的cookie通过了上面的if判断,然后修改mid为admin的id(1),就可以从前台登录到admin账户。
那么如何在请求过程中修改DedeUserID
的值让它能和admin的id相等呢?
我们使进入GetNum
方法的参数为数字1+字母
的形式,经过正则替换就会变成1
,也就是$this->M_ID
的值,然后带入数据库查询
$fnum
为1qqqq
的情况,经过正则替换后值成为了1
在include/memberlogin.class.php:178
有这么一行代码
1 2 |
$this->M_ID = intval($this->M_ID); |
对$this->M_ID
进行了整数类型转换,假设注册一个用户名,经过intval
转换后为1
就能使查询条件变成Select * From #@__member where mid='1'
,也就取出了管理员在dede_member
表里的密码,此时配合上一个漏洞,我们已经修改了dede_member
中管理员的密码,只要在前台再进行一次修改密码操作,就能真正修改admin的密码。
这是调试的时候注册用户名为0000001
的情况,经过intval
转换后M_ID
的值变成了1
下面看一下如何从前台登录admin账户
在index.php
里有一个最近访客记录
的功能,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
else { require_once(DEDEMEMBER.'/inc/config_space.php'); if($action == '') { include_once(DEDEINC."/channelunit.func.php"); $dpl = new DedeTemplate(); $tplfile = DEDEMEMBER."/space/{$_vars['spacestyle']}/index.htm"; //更新最近访客记录及站点统计记录 $vtime = time(); $last_vtime = GetCookie('last_vtime'); $last_vid = GetCookie('last_vid'); if(empty($last_vtime)) { $last_vtime = 0; } if($vtime - $last_vtime > 3600 || !preg_match('#,'.$uid.',#i', ','.$last_vid.',') ) { if($last_vid!='') { $last_vids = explode(',',$last_vid); $i = 0; $last_vid = $uid; foreach($last_vids as $lsid) { if($i>10) { break; } else if($lsid != $uid) { $i++; $last_vid .= ','.$last_vid; } } } else { $last_vid = $uid; } PutCookie('last_vtime', $vtime, 3600*24, '/'); PutCookie('last_vid', $last_vid, 3600*24, '/'); |
else条件是当访问页面http://127.0.0.1/dedecms/uploads/member/index.php?uid=1111
传入的uid不为空时进入
当我们传入的last_vid
为空的时候,$last_vid = $uid;
而uid
是我们能控制的,所以我们就能控制传给PutCookie
的参数,进入PutCookie
方法
1 2 3 4 5 6 7 8 9 10 |
if ( ! function_exists('PutCookie')) { function PutCookie($key, $value, $kptime=0, $pa="/") { global $cfg_cookie_encode,$cfg_domain_cookie; setcookie($key, $value, time()+$kptime, $pa,$cfg_domain_cookie); setcookie($key.'__ckMd5', substr(md5($cfg_cookie_encode.$value),0,16), time()+$kptime, $pa,$cfg_domain_cookie); } } |
在这里设置了last_vid__ckMd5
的值
所以攻击流程已经明确了
数字1+字母
的形式,或者经过intval()
后值为1
last_vid__ckMd5
的值DedeUserID
和DedeUserID__ckMd5
的值,替换成我们注册的用户名和last_vid__ckMd5
,就能登录到前台admin1qqqq
/member/index.php?uid=1qqqq
,获取last_vid__ckMd5
的值/member/index.php
,替换DedeUserID
和DedeUserID__ckMd5
的值member/edit_baseinfo.php
,还是要修改cookie值dede_member
表中的admin密码,这样就达到了真正修改admin的密码这回有两处可导致判断条件的绕过,有时候一个漏洞影响力有限的时候也不能轻视,往往配合另一处缺陷就可以造成很大的危害
暂时关闭会员注册功能,管理员设置安全问题,关注官方更新补丁及时升级