2015-08-25: 积极联系厂商并且等待厂商认领中,细节不对外公开 2015-10-09: 厂商已经主动忽略漏洞,细节向公众公开
这cms很无语,而且国内很大公司好像还都在用。注入的问题特多。
/model/base.class.php 文件为初始化时加载的一个文件。里面全是初始化函数。146 行function init_user() 函数直接是初始化用户信息的。代码如下: function init_user() { @$sid = tcookie('sid'); //接受sid参数 @$auth = tcookie('auth'); $user = array(); @list($uid, $password) = empty($auth) ? array(0, 0) : taddslashes(explode("\t", authcode($auth, 'DECODE')), 1); if (!$sid) { $sid = substr(md5(time() . $this->ip . random(6)), 16, 16); tcookie('sid', $sid, 31536000); } $this->load('user'); //var_dump($_ENV['user']);die; if ($uid && $password) { $user = $_ENV['user']->get_by_uid($uid, 0); ($password != $user['password']) && $user = array(); } if (!$user) { $user['uid'] = 0; $user['groupid'] = 6; } $_ENV['user']->refresh_session_time($sid, $user['uid']); //后面我们跟进这个函数看一下 $user['sid'] = $sid; $user['ip'] = $this->ip; $user['uid'] && $user['loginuser'] = $user['username']; $user['uid'] && $user['avatar'] = get_avatar_dir($user['uid']); $this->user = array_merge($user, $this->usergroup[$user['groupid']]); //var_dump($this->user);die; }上面代码的问题出在sid参数上。这个参数通过tookie函数接受进来的。我们跟进看一下。此函数在/lib/global.func.php 文件中。函数代码如下:function tcookie($var, $value = 0, $life = 0) { global $setting; $cookiepre = $setting['cookie_pre'] ? $setting['cookie_pre'] : 't_';//注意前缀,方便我们在前端找对应的参数 if (0 === $value) { $ret = isset($_COOKIE[$cookiepre . $var]) ? $_COOKIE[$cookiepre . $var] : ''; checkattack($var, 'cookie'); return $ret; } else { $domain = $setting['cookie_domain'] ? $setting['cookie_domain'] : ''; checkattack($var, 'cookie'); //用来过滤 setcookie($cookiepre . $var, $value, $life ? time() + $life : 0, '/', $domain, $_SERVER['SERVER_PORT'] == 443 ? 1 : 0); }}以上代码的核心部分是checkattack函数。是用来做过滤的。我们看下这个函数,此函数在globa.func.php文件中,代码如下:function checkattack($reqarr, $reqtype = 'post') { $filtertable = array( 'get' => '\'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)', 'post' => '\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)', 'cookie' => '\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)' ); foreach ($reqarr as $reqkey => $reqvalue) { if (preg_match("/" . $filtertable[$reqtype] . "/is", $reqvalue) == 1 && !in_array($reqkey, array('content'))) { print('Illegal operation!'); exit(-1); } }}看到哪个foreach 我就郁闷了。他只过滤数组的情况,一般的情况不做正则匹配的过滤。上面的分析已经说的很明白了。我们来看看前端的sid参数在哪里。当用户访问这个系统的首页的时候其实已经初始化了这个参数,参数为cookie字段的:tp_sid。上面的sid参数最终进入了 refresh_session_time函数。这个函数在/model/user.class.php文件中,代码如下: function refresh_session_time($sid, $uid) { $lastrefresh = intval(tcookie("lastrefresh")); //var_dump($lastrefresh);die; if (!$lastrefresh) { //echo 11111;die; if ($uid) { $this->db->query("UPDATE " . DB_TABLEPRE . "session SET `time` = {$this->base->time} WHERE sid='$sid'"); } else { $session = $this->db->fetch_first("SELECT * FROM " . DB_TABLEPRE . "session WHERE sid='$sid'"); if ($session) { $this->db->query("UPDATE " . DB_TABLEPRE . "session SET `time` = {$this->base->time} WHERE sid='$sid'"); } else { $this->db->query("INSERT INTO " . DB_TABLEPRE . "session (sid,`ip`,`time`) VALUES ('$sid','{$this->base->ip}',{$this->base->time})"); } } tcookie("lastrefresh", '1', 60); } }从上面的代码可以看出,只要$lastrefresh这个参数的值为0就可以进入数据库操作。而$lastrefresh这个参数也是cookie字段中的一个参数:tp_lastrefresh=0;所以最终的攻击流程为。访问首页抓包在tp_sid参数处注入同时设置tp_lastrefresh=0;通过测试发现不会受到任何拦截。可以看下面的漏洞证明
下面的图为我在本地搭建的从tipask官网下载的最新版做的测试。utf8和gbk都测了,漏洞复现以utf8为例
下面的图为数据的监控日志
下面的图为我在他们官网做的测试。直接sleep(10)。官网挂了
下面的图为我自己写的一个脚本盲注数据库名,当然也可以读admin的密码
ps:你猜
未能联系到厂商或者厂商积极拒绝
开发者的脑子抽了。