翻译: 西风微雨
预估稿费:100RMB(不服你也来投稿啊!)
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
0x00 漏洞背景
e107 CMS是一个基于PHP、Bootstrap、Mysql的网站内容管理系统,可广泛用于个人博客、企业建站,在全球范围内使用较为广泛。
0x01 漏洞影响版本
version <=2.1.2
0x02 漏洞分析环境
运行环境:macOS10.12.2 + apache2.4.23 + PHP5.6.27 + Mysql5.7.16
e107 CMS版本:v2.1.2
0x03 漏洞详情
首先我们从rips的扫描报告中可以大致知道整个漏洞的触发是利用反 序列化 漏洞来进行数据库数据修改,进一步进行权限提升。 接下来,我们就来对整个触发流程进行分析:
1.首先我们注册普通用户test2,原始邮箱地址为test22@1.com ;我们可以看到 user_admin 字段为0(e107 CMS以 user_admin 字段标示用户权限,1为管理员,0为普通用户),因此 test2 是普通用户;接下来我们进入 /e107/usersettings.php 修改邮箱
2.反序列化漏洞及数据库注入漏洞代码跟踪
变量关系注释:$_POST[‘updated_data’]为base64编码的值,$new_data是base64解码后的值是一个序列化的值,$changedUserData为反序列化后的值,是一个数组。
首先跟进 usersettings.php 353-387行的代码
123 | 353 $new_data = base64_decode($_POST['updated_data']); ... 387 $changedUserData = unserialize($new_data); |
353行中用户可控变量 $_POST[‘updated_data’] 未经进一步处理就直接在387行中进行了反序列化,并将数据赋值给 $changedUserData 变量,以便进一步操作.
继续跟进 $changedUserData 变量
123 | 455 $changedData['data'] = $changedUserData; ... 460 if (FALSE === $sql->update('user', $changedData)) |
$changedUserData 变量在460行进入mysql类方法,跟进 /e107_handlers/mysql_class.php 中的 update 函数
1234 | 1160 function update($tableName, $arg, $debug = FALSE, $log_type = '', $log_remark = '') { 1162 $arg = $this->_prepareUpdateArg($tableName, $arg); ... 1183 $result = $this->mySQLresult = $this->db_Query($query, NULL, 'db_Update'); |
跟进 _prepareUpdateArg 函数
123456789 | 1083 private function _prepareUpdateArg($tableName, $arg) { 1084 ... 1085 foreach ($arg[‘data’] as $fn => $ fv ) { 1086 $new_data .= ($new_data ? ', ' : ''); 1087 $ftype = isset($fieldTypes[$fn]) ? $fieldTypes[$fn] : 'str'; 1088 $new_data .= "{$fn}=".$this->_getFieldValue($fn, $fv, $fieldTypes); 1089 ... 1090 } 1091 return $new_data .(isset($arg[‘WHERE’]) ? ' WHERE '. $arg['WHERE'] : ''); |
跟进 _getFieldValue 函数
123456 | 1247 function _getFieldValue($fieldKey, $fieldValue, &$fieldTypes) { 1248 $type = isset($fieldTypes[$fieldKey]) ? $fieldTypes[$fieldKey] : $fieldTypes['_DEFAULT']; 1249 switch ($type) { 1250 case 'str': 1251 case 'string': 1252 return "'".$this-> escape ($fieldValue, false)."'"; |
可以看出 $changedUserData 变量仅仅被拆分开来,而没有做进一步校验是否有恶意参数,因此只要 $changedUserData 中包含恶意的user表字段,便能够任意修改数据表中的值。
3.漏洞利用
首先我们来看看测试正常修改邮箱的数据格式,测试更改邮箱为 22test2@1.com
这里就可以清楚地看到, $new_data 变量为被修改数据序列化的值, $changedUserData 为 $new_data 反序列化后的值,数据校验成功后, $changedUserData 就会被拆分,然后进入 $sql->update 函数执行,进而任意修改数据库数据。
那么,我们如何利用这个漏洞链呢?
要做到提权操作,我们就需要更新 test2 用户的 user_admin 字段,并且在修改 $new_data 变量的值后,必须顺利通过 usersetings.php 的两个if语句检查:
123 | 358 if (md5($new_data) != $_POST['updated_key'] || ($userMethods->hasReadonlyField($new_data) !==false)) ... 366 if (md5($new_extended) != $_POST['extended_key']) |
从358行来看,我们在 抓包 修改 $_POST[‘updated_data’] 的同时需要修改掉 $_POST[‘updated_key’] ,使之满足md5值校验。 我使用如下的php代码生成 update_key 和 updated_data
123456 | /* php code */ $a = array('user_email'=>'2test2@1.com','user_admin'=>1); $b = serialize($a); echo 'updated_data is: '.$b; echo 'update_key is : '.md5($b); /* php code */ |
接下来使用burpsuite抓包修改 $_POST[‘updated_data’] 为以及 $_POST[‘update_key’] (注意:e107 在修改邮箱时会验证密码,我们只修改校验了密码之后的数据包,如下图:)
成功反序列化:
查看数据库字段,发现 test2 用户的 user_admin 字段已经被成功修改为1,权限提升成功
test2 用户成功进入后台管理面板:
0x04 漏洞修复
升级e107 CMS至2.1.3版本
0x05 漏洞总结
此漏洞的修复过程也有些许奇妙,Kacper Szurek安全研究员早在2016年6月就在2.1.1版本发现了此漏洞,官方多次修复均被饶过,并且在2.1.2版本中仍未修复,或许官方暂未找到更好的修复方法,此漏洞便一度被搁置;直到2016年11月RIPS再次报告漏洞,官方终于在2.1.3版本的修复中完全重写了usersettings.php文件,以修复包括此漏洞在内的多个漏洞。 另外,此篇文章在我的个人博客中也有备份:。
0x06 参考链接
#diff-dbac6e5a7c66d48e23884c0968e6dad7
#diff-dbac6e5a7c66d48e23884c0968e6dad7