i春秋作家:F0rmat
0x01 前言
已经一个月没有写文章了,最近发生了很多事情,水文一篇。
今天的这个CMS是FineCMS,版本是5.0.10版本的几个漏洞分析,从修补漏洞前和修补后的两方面去分析。
文中的evai是特意写的,因为会触发论坛的防护机制,还有分页那一段的代码也去掉了,因为会触发论坛分页的bug。
0x02 环境搭建
https://www.ichunqiu.com/vm/59011/1 可以去i春秋的实验,不用自己搭建那么麻烦了。
0x03 任意文件上传漏洞
1.漏洞复现
用十六进制编辑器写一个有一句话的图片
去网站注册一个账号,然后到上传头像的地方。
抓包,把jepg的改成php发包。
可以看到文件已经上传到到/uploadfile/member/用户ID/0x0.php
2.漏洞分析
文件:finecms/dayrui/controllers/member/Account.php
177~244行
/**
* 上传头像处理
* 传入头像压缩包,解压到指定文件夹后删除非图片文件
*/
public function upload() {
// 创建图片存储文件夹
$dir = SYS_UPLOAD_PATH.'/member/'.$this->uid.'/';
@dr_dir_delete($dir);
!is_dir($dir) && dr_mkdirs($dir);
if ($_POST['tx']) {
$file = str_replace(' ', '+', $_POST['tx']);
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $file, $result)){
$new_file = $dir.'0x0.'.$result[2];
if (!@file_put_contents($new_file, base64_decode(str_replace($result[1], '', $file)))) {
exit(dr_json(0, '目录权限不足或磁盘已满'));
} else {
$this->load->library('image_lib');
$config['create_thumb'] = TRUE;
$config['thumb_marker'] = '';
$config['maintain_ratio'] = FALSE;
$config['source_image'] = $new_file;
foreach (array(30, 45, 90, 180) as $a) {
$config['width'] = $config['height'] = $a;
$config['new_image'] = $dir.$a.'x'.$a.'.'.$result[2];
$this->image_lib->initialize($config);
if (!$this->image_lib->resize()) {
exit(dr_json(0, '上传错误:'.$this->image_lib->display_errors()));
break;
}
}
list($width, $height, $type, $attr) = getimagesize($dir.'45x45.'.$result[2]);
!$type && exit(dr_json(0, '图片字符串不规范'));
}
} else {
exit(dr_json(0, '图片字符串不规范'));
}
} else {
exit(dr_json(0, '图片不存在'));
}
// 上传图片到服务器
if (defined('UCSSO_API')) {
$rt = ucsso_avatar($this->uid, file_get_contents($dir.'90x90.jpg'));
!$rt['code'] && $this->_json(0, fc_lang('通信失败:%s', $rt['msg']));
}
exit('1');
}
这个我记得在5.0.8的版本有讲过这个代码的漏洞执行https://getpass.cn/2018/01/30/The%20latest%20version%20of%20FineCMS%205.0.8%20getshell%20daily%20two%20holes/
后来官方修复的方案是加上了白名单了:
if (!in_array(strtolower($result[2]), array('jpg', 'jpeg', 'png', 'gif'))) {
exit(dr_json(0, '目录权限不足'));
}
...
$c = 0;
if ($fp = @opendir($dir)) {
while (FALSE !== ($file = readdir($fp))) {
$ext = substr(strrchr($