环境
windows 11
phpstudy
CmsEasy 7.7.4
代码分析
- 漏洞点:文件lib/admin/database_admin.php中的函数dorestore_action()方法。 //还原数据function dorestore_action() { /* db_dir = explode('_',front::get('db_dir')); if(db_dir[2]!=_VERCODE){ front::flash(lang_admin('database_recovery_failed').'!'); front::redirect(url::create('database/baker')); }*/ dir=ROOT.'/data/backup-data/'.front::get('db_dir'); if(is_dir(dir)) { db_files=front::scan(dir); foreach(db_files as db_file) { if(!preg_match('/^./',db_file)) tdatabase::getInstance()->restoreTables(db_file); } //更新表 nerrCode=service::checktable(); if (nerrCode) { this->json_info(5, nerrCode); exit; } user::deletesession(); category::deletesession(); type::deletesession(); special::deletesession(); front::flash(lang_admin('database_recovery').lang_admin('success').'!'); } front::redirect(url::create('database/baker'));}看到该方法运行中会直接接受GET参数db_dir拼接成目录dir=ROOT.'/data/backup-data/'.front::get('db_dir');没有经过过滤,该目录可以进行伪造。
- 之后执行db_files=front::scan(dir);获取该目录下得文件名,并带入方法tdatabase::getInstance()->restoreTables()执行下一步操作。
- 跟进restoreTables()方法,在文件lib/table/tdatabase.php中。function restoreTables(file) { database=config::getdatabase('database'); set_time_limit(0); //database = new tdatabase(); sqlquery = file_get_contents(file); if (!sqlquery) return; sqlquery = str_replace("r", "", sqlquery = str_replace("cmseasy_", sqlquery); sqls = preg_split ("/;(--) [ t]{0,}n/", sqlquery); //var_dump(sqls);exit; nerrCode = ""; sqls as q) { q = trim(q); if (q == "") { continue; } if (q)) i ; else nerrCode .= "执行:<font color='blue'>nerrCode; }可以看到,sqlquery = file_get_contents(file);将文件读取并赋值给sqlquery,在经过一系列的替换和切割后,形成sql语句数组sqls,并遍历带入
- 由此可以得到,如果能够控制某文件内容,将目录传递给
dorestore_action()
方法,即可实现sql语句的执行。 - 测试头像上传功能,上传内容为sql语句的图片。定位代码在lib/default/tool_act.php的uploadimage3_action()方法。可以看到将上传的图片内容传递到front::checkstr()方法进行检查,跟进lib/tool/front_class.php文件的checkstr()方法static function checkstr(str){ if (preg_match("/<(/?)(script|i?frame|style|html|?php|body|title|link|meta)([^>]*?)>/is", match)) { //front::flash(print_r(match,true)); return false; } if (preg_match("/(<[^>]*)on[a-zA-Z] s*=([^>]*>)/is", str, match)) { return false; } return true;}对文件内容进行两次关键词匹配,可以看到没有对sql语句的关键词进行匹配,可以上传内容为sql语句的图片。
演示验证
- 通过头像上传包含payload的图片
- 访问连接
http://easy.test/index.php?case=database&act=dorestore&admin_dir=admin&db_dir=../../cn/upload/images/202111
提取出相关图片
- 提取出构造的sql语句
- 执行成功,页面出现报错信息。
修复建议
1.更新到最新版本
2.对路径进行关键词过滤,例如"../"等