2019--redhat

1. redhat初赛

2. redhat复赛

2.1 粤湾投资(cms)

爱客猴框架:https://www.aikehou.com/

漏洞1 任意文件上传

漏洞路径:\www\Apps\Home\Controller\ UploadfileController.class.php

<?php
/*
* Author: [ Copy Lian ]
* Date: [ 2015.05.08 ]
* Description [ 上传文件控制器 ]
*/
namespace Home\Controller;

class UploadfileController extends CommonController
{

    /**
     * [index 文件上传代码]
     * @return [type] [description]
     */
    public function index(){
        if(IS_POST){

            /**
             * 1、如果是图片直接调用uploadPhoto()
             * 2、如果是替他附件则按照当前配置设置
            */
            $type = I('post.type','files');
            //在thinkphp中为了安全的原因建议统一使用 I 函数来获取变量值,也就是说这些post的值我们都是可以控制的。
            $saveDir = I('post.savedir');
            $thumbw = I('post.thumbw');
            $thumbh = I('post.thumbh');
            $filesize = I('post.filesize');
            if($type == 'images' || preg_match('/^image\//', $_FILES['Filedata']['type'][0])){
                //这里如果匹配到image/结构,进入判断,^表开头。
                $type = "images";
                //是否添加水印
                $water = I('post.water');

                if($water){
                    $water_site = C('SITE_SYSTEM_IMG_WATER');
                   // 配置操作函数C(大写字母C)。ThinkPHP按照默认的顺序加载完配置之后,配置全局有效,在框架作用范围内(一般指应用目录下),所有配置都可以直接使用C函数读取(包括ThinkPHP默认配置)。
                    $water_new = isset($water_site) && !empty($water_site) ? $water_site : C('SYSTEM_IMG_WATER');
                    //$info = uploadPhoto('photo/'.$type.'',$thumbw,$thumbh,array('open'=>1));
                    $info = uploadPhoto($saveDir.'/'.$type,$thumbw,$thumbh,$water_new,$filesize);
                } else {
                    $info = uploadPhoto($saveDir.'/'.$type,$thumbw,$thumbh,'',$filesize);
                }

                if(!is_array($info)){
                    $error_data['info'] = $info;
                    $error_data['status'] = 0;
                    $this->ajaxReturn($error_data);
                }

                $file['oringinal_type'] = 'images';
                $file['status'] = 1;
                $file['name'] = $_FILES['Filedata']['name'][0];
                $file['savename'] = $info[0]['savename'];
                $file['photo'] = substr($info[0]['savepath'],1).$info[0]['savename'];
                $file['thumb'] = $info[0]['thumbpath'];
                $file['location'] = 'upload';
                $this->ajaxReturn($file);
            } else {
                $type = 'files';
                $upload = new \Think\Upload();
                $upload->maxSize = $filesize*1024; //文件上传的最大文件大小,附件最大100M
                $upload->rootPath = "./"; //文件上传保存的根路径
                $upload->savePath = $upload->rootPath."Public/Uploads/".$saveDir."/" . $type ."/";//文件上传的保存路径
                $upload->saveName = date("YmdHis")."_".uniqid(); //上传文件的保存规则
                $upload->replace = true; //存在同名文件是否是覆盖
                $upload->autoSub = true;//自动使用子目录保存上传文件
                $upload->subName = date("Ymd");//子目录创建方式,采用数组或者字符串方式定义
                $upload->hash = true;//是否生成文件的hash编码 默认为true

                if(!$info = $upload->upload()){
                    $error_data['status'] = 0;
                    $error_data['info'] = $upload->getError();
                    $this->ajaxReturn($error_data);
                } else {
                    //处理文件图标
                    //excel
                    if(in_array($info[0]['ext'], array('xls','xlsx'))){
                        $info[0]['ext'] = 'xls';
                    }
                    //ppt
                    if(in_array($info[0]['ext'], array('ppt','pptx'))){
                        $info[0]['ext'] = 'ppt';
                    }
                    //word
                    if(in_array($info[0]['ext'], array('doc','docx'))){
                        $info[0]['ext'] = 'doc';
                    }
                    //音频
                    if(in_array($info[0]['ext'], array('wma','mp3','mid','wav'))){
                        $info[0]['ext'] = 'mp3';
                    }
                    //视频
                    if(in_array($info[0]['ext'], array('avi','mov','mpeg','mpg','swf','mp4'))){
                        $info[0]['ext'] = 'vedio';
                    }
                    //zip
                    if(in_array($info[0]['ext'], array('zip','rar','7z'))){
                        $info[0]['ext'] = 'zip';
                    }
                    //判断是否存在文件不存在则取默认图片
                    if(!file_exists("./Public/images/uploadfile/".$info[0]['ext'].".png")){
                        $info[0]['ext'] = "readme";
                    }
                    $info[0]['status'] = 1;
                    $info[0]['oringinal_type'] = 'files';
                    $info[0]['savepathall'] = substr($info[0]['savepath'],1).$info[0]['savename'];
                    $info[0]['location'] = 'upload';
                    $this->ajaxReturn($info[0]);
                }
            }
        } else {
             //缩略图配置
            $site_thumbw = C('SITE_SYSTEM_THUMB_WIDTH');
            $site_thumbh = C('SITE_SYSTEM_THUMB_HEIGHT');
            $thumbw = isset($site_thumbw) && !empty($site_thumbw) ? C('SITE_SYSTEM_THUMB_WIDTH') : C('SYSTEM_THUMB_WIDTH');
            $thumbh = isset($site_thumbh) && !empty($site_thumbh) ? C('SITE_SYSTEM_THUMB_HEIGHT') : C('SYSTEM_THUMB_HEIGHT');
            $this->thumbw = I('get.thumbw',$thumbw);
            $this->thumbh = I('get.thumbh',$thumbh);
            $this->thumbw = $this->thumbw === 0 ? $thumbw : $this->thumbw;
            $this->thumbh = $this->thumbh === 0 ? $thumbh : $this->thumbh;

            $this->type = I('get.type',''); //类型
            $this->myid = I('get.myid',''); //返回容器内容的值
            $this->iframe = I('get.iframe',''); //返回的iframe容器
            $this->field = I('get.field','');
            $this->returntype = I('get.returntype','multiple'); //返回文件的个数类型,单个-single,多个-multiple,默认是多个

            //控制上传文件大小
            $this->filesize = I('get.filesize');
            if(empty($this->filesize)){
                if($this->type == 'images'){
                    $this->filesize = 3072; //图片默认3M
                } else {
                    $this->filesize = 102400; //附件默认100M
                }
            }

            //上传目录
            $this->savedir = I('get.savedir','uploadfile');
            $this->display();
        }
    }

    /**
     * [del 上传文件]
     * @return [type] [description]
     */
    public function del()
    {
        if(IS_POST){
            $type = I('post.oringinal_type');
            $location = I('post.location');
            if($location == 'upload'){
                if($type == 'images'){
                    $photo = I('post.photo');
                    $thumb = I('post.thumb');
                    if(file_exists('.'.$photo)){
                        unlink('.'.$photo);
                    }
                    if(file_exists('.'.$thumb)){
                        unlink('.'.$thumb);
                    }
                } else {
                    $savepathall = I('post.savepathall');
                    if(file_exists('.'.$savepathall)){
                        unlink('.'.$savepathall);
                    }
                }
            }
            $this->success(L('_DEL_SUCCESS_'));
        } else {
            $this->error(L('_ACCESS_ERROR_'));
        }
    }

    /**
     * [files 获取本地文件]
     * @return [type]        [description]
     */
    public function files()
    {
        //上传类型
        $this->returntype = I('get.returntype');
        //获取类型
        $this->savedir = I('get.savedir');
        //获取类型
        $this->type = I('get.type','files');
        //返回容器
        $this->origin_domid = I('get.myid');
        //返回容器的iframe
        $this->iframe = I('get.iframe');
        //最原始目录
        $this->oringi_path = "./Public/Uploads/".$this->savedir."/".$this->type;

        //获取目录
        $uploadfilePath = I('get.dir',"");
        if(empty($uploadfilePath)){
            $uploadfilePath = $this->oringi_path."/*";
        } else {
            $uploadfilePath = decode($uploadfilePath)."/*";
        }

        //取出来的中文会乱码,暂时不考虑
        $dirs = glob($uploadfilePath,GLOB_NOESCAPE);
        $dirall = array();
        foreach ($dirs as $key => $value) {
            if(is_file($value)){
                //判断文件类型
                $pathinfo = pathinfo($value);
                $imagesarr = array('jpg','png','gif','bmp');
                if(in_array($pathinfo['extension'],$imagesarr)){
                    $pathinfo['type'] = 'images';
                } else {
                    $pathinfo['type'] = 'files';
                    if(!file_exists("./Public/images/uploadfile/".$pathinfo['extension'].".png")){
                        $pathinfo['extension'] = 'readme';
                    }
                }

                $pathinfo['filepath'] = substr($value,1);
                $dirall['files'][] = $pathinfo;
            } else if(is_dir($value)) {
                $arr = explode("/", $value);
                $dirarr = array('path'=>$value,'pathname'=>$arr[count($arr)-1]);
                $dirall['dirs'][] = $dirarr;
            }
        }
        //当前目录
        $this->path = substr($uploadfilePath,0,strlen($uploadfilePath)-2);
        //上一目录
        $prevarr = explode("/", $this->path);
        unset($prevarr[count($prevarr)-1]);
        $prevpath = implode("/",$prevarr);
        $this->prevpath = $prevpath;
        $this->data = $dirall;
        $this->display();
    }
}
?>

两种上传操作,第一种是图片的上传,满足判断条件后调用uploadPhoto;

而第二种是其他文件的上传,没有任何过滤。(我们就利用这点来getshell)

upload

漏洞2 日志泄露+ssti模板注入

日志泄露

漏洞文件:\www\Apps\Runtime\Logs\Home\19_11_20.log

[ 2019-11-20T00:43:54+08:00 ] 127.0.0.1 /aikehou/echo/index.php/Jquery/?template_file=/etc/passwd

可以看出这是主办方直接测试用的payload,稍加改动即可去读flag。

ssti模板注入(不理解)

上述可以直接读flag源于ssti模板注入造成的前台任意文件读取,跟踪下过程。

漏洞文件:Apps/Home/Controller/JqueryController.class.php

在sublime里对template_file进行find in folder,得到的第一条正好是漏洞文件所在路径,双击框选部分直接跳转到目标代码。

粤湾投资

漏洞函数:index()

public function index(){
        if(!isset($_GET['template_file'])) {

            $this->seoData = array('title' => 'Jquery插件', 'keywords' => 'Jquery插件', 'description' => 'Jquery插件');

            $this->display();
        }
        else{
            $this->display($_GET['template_file']);
            #  display函数对template_file进行处理
        }
    }
}

Goto Definition1

粤湾投资

在Controller.class.php中,直接将我们传递的template_file参数代入进display方法中,跟进:

protected function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
        $this->view->display($templateFile,$charset,$contentType,$content,$prefix);
    }

Goto Definition2

粤湾投资

这里除了$templateFile,其余传递参数都为空,这里将templateFile传递进了fetch()方法。

public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
        G('viewStartTime');
        // 视图开始标签
        Hook::listen('view_begin',$templateFile);
        // 解析并获取模板内容
        $content = $this->fetch($templateFile,$content,$prefix);
        // 输出模板内容
        $this->render($content,$charset,$contentType);
        // 视图结束标签
        Hook::listen('view_end');
    }

Goto Definition3

$this->fetch,肯定就是在当前php文件里找了

粤湾投资

这里可以将传入的$templateFile直接当做模板渲染,param string $content 模板输出内容。

    /**
     * 解析和获取模板内容 用于输出
     * @access public
     * @param string $templateFile 模板文件名
     * @param string $content 模板输出内容
     * @param string $prefix 模板缓存前缀
     * @return string
     */
public function fetch($templateFile='',$content='',$prefix='') {
        if(empty($content)) {
            $templateFile   =   $this->parseTemplate($templateFile);
            // 模板文件不存在直接返回
            if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);
        }else{
            defined('THEME_PATH') or    define('THEME_PATH', $this->getThemePath());
        }
        // 页面缓存
        ob_start();
        ob_implicit_flush(0);
        if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) { // 使用PHP原生模板
            $_content   =   $content;
            // 模板阵列变量分解成为独立变量
            extract($this->tVar, EXTR_OVERWRITE);
            // 直接载入PHP模板
            empty($_content)?include $templateFile:eval('?>'.$_content);
        }else{
            // 视图解析标签
            $params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);
            Hook::listen('view_parse',$params);
        }
        // 获取并清空缓存
        $content = ob_get_clean();
        // 内容过滤标签
        Hook::listen('view_filter',$content);
        // 输出模板文件
        return $content;
    }

将/flag直接作为模板进行渲染,/index.php/Jquery/index?template_file=/flag

问题:1:渲染模板的原理是什么?
问题2:这里的路由也不明白怎么找的?

路由

问题3:修补方案不理解?

判断$_GET['template_file']的路径是不是在模板文件夹的目录下(模板文件夹在哪里???)。

<?php
#  判断指定文件夹下是否存在指定文件
function dir_exist_file($path,$filename)
{
    if (!is_dir($path)) {
        return false;
    }
    $files = scandir($path);
    foreach ($files as $key => $value) {
        // echo $value;
        if($filename === $value){
            return true;
        }else{
            continue;
    }
    }
}
if(dir_exist_file("c:\\","Users")){
  #  $path放模板文件夹所在位置,$filename即为$template_file
    echo "file exists!!";
}
?>

2.2 粤湾租赁

2.5 总结

First:

将代码拖到sublime,find in folder下列内容:

/etc/passwd
flag
upload

参考资料

1、官方writeup:

https://mp.weixin.qq.com/s/7DBMDr85_nBCjBkQoqlOWQ

2、详细writeup:

https://xz.aliyun.com/t/6826#toc-1

https://threezh1.com/2019/12/18/2019%20Redhat%E5%86%B3%E8%B5%9B%20Writeup/

https://lihuaiqiu.github.io/2019/11/26/2019%E7%BA%A2%E5%B8%BD%E6%9D%AF%E7%BA%BF%E4%B8%8B%E9%A2%98%E7%9B%AE%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/


   转载规则


《2019--redhat》 pperk 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
basic_hexo basic_hexo
我想给你一把打开这扇门的钥匙,而你要做的便是静静地聆听接下来的故事。
2020-05-04
下一篇 
2020--XCTF 2020--XCTF
我想给你一把打开这扇门的钥匙,而你要做的便是静静地聆听接下来的故事。
2020-05-02
  目录