从 php 代码层面分析文件上传漏洞
从 PHP 代码层面分析文件上传漏洞
- 根据 Upload-Labs 来进行总结
0x00.参考文章
如果 shell 写的是回显型(phpinfo())或者是个图片,需要通过另一个[[文件包含漏洞]]漏洞打组合拳,或者将以下包含代码传上去
1 |
|
0x01.从客户端 JS 去限制文件扩展名(本地校验)
代码
1 | function checkFile() { |
Bypass
删除关键白名单 JS 代码片段
在白名单里加入恶意扩展名
0x02.检查数据包的 MIME(文件类型)
代码
1 | $is_upload = false; |
Bypass
- 仅将数据包中的 Content-Type 修改为合法类型,文件扩展名不变
0x03.对扩展名进行黑名单限制
- 模板代码
1 | $is_upload = false; |
1.规则-扩展名进行黑名单限制
代码
1 | $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"); |
Bypass
- 使用一些别名,例如 php 则可以使用
php2
php3
php4
php5
phps
pht
phtm
phtml
- 使用 Apache 的.htaccess 规则
通过构造当前网页配置文件.htaccess
创建一个文件名为.htaccess 的文件,保存格式为 eXtensible Markup Language file
内容为:
1 | <FilesMatch "cimer"> //cimer可以替换为自己想要的扩展名,后期改这个扩展名上传就好了 |
- 使用 Apache 从右向左解析的规则
上传 1.php.bak 或者任意一个编造的扩展名
在某些版本可当做 php 来执行(因为 apache 从右到左对文件名进行解析,而 .bak 是 httpd.conf 识别不了的文件名扩展名,
故 apache 会自动提取左边的.php)
2.规则-扩展名大小写限制
代码
1 | $file_ext = strtolower($file_ext);//转换为小写 |
Bypass
- 如果没有大小写限制,那么我们就可以使用 pHp 来进行绕过
3.规则-扩展名首尾去空
代码
1 | $file_ext = trim($file_ext);//对扩展名进行首尾去空操作 |
Bypass
- 如果没有收尾去空操作,那么我们就可以在扩展名后加空格进行绕过
4.规则-删除末尾的点
代码
1 | $file_name = deldot($file_name);//删除末尾的点 |
Bypass
如果没有删除末尾的点,那么我们就可以使用 1.php.进行绕过
Windows 环境下会自动删除最后的点
5.规则-进行文件流限制
代码
1 | $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA |
Bypass
如果没有进行文件流限制,那么我们可以使用上传文件名是 1.php::$DATA 来进行绕过(访问时去除文件流后缀)
1.php:1.txt 是正常文件流,而:$DATA 是当前文件默认文件流
6.规则-对恶意扩展名进行替换
代码
1 | $file_name = str_ireplace($deny_ext,"", $file_name);//对恶意扩展名进行替换操作 |
Bypass
- 注意,即便有这样的替换操作,我们依旧可以使用双写进行绕过
0x04.上传路径可控
1.通过 GET 方式控制(URL)
代码
1 | index.php |
Bypass(%00 截断)
如果路径可控,我们可以先上传一个有一句话的合法类型文件(JPG/GIF 等)
然后我们在可控制路径的地方使用%00 截断,具体操作操作例如将路径更换为你要上传马的路径加上 php 文件名:../upload/1.php%00
然后上传上去就是 1.php,原先的 jpg 不会被上传
2.通过 POST 方式控制
代码
1 | index.php |
Bypass(0x00 截断)
和 GET 方式大致相似,但在可控路径上添加“一个空格与 a”:../upload/1.php a
空格与 a 的 HEX 分别是 20、61 将 20 改为 00,即可绕过
0x05.检查图片内容
1.只检查头两个字节
代码
1 | function getReailFileType($filename){ |
Bypass
- 只用在 php 文件前加上一行标识
GIF:GIF89a
JPG:ff, d8(HEX 里边修改)
PNG:89 50 4e 47 0d 0a 1a 0a(HEX 里边修改)
2.通过特别函数来检查图片内容
利用 getimagesize()
1 | function isImage($filename){ |
利用 exif_imagetype()
1 | function isImage($filename){ |
Bypass
- 只用上传个在末尾有加 shell 代码的图片马就好了
0x06.将上传的图片进行重新渲染
代码
程序通过 imagecreatefromjpeg()函数调用了 PHP GD 库(GD 库,是 php 处理图形的扩展库),对图片进行了转换
1 | 关键代码 |
Bypass
- GIF
先上传 GIF,然后将上传的图片下载下来
用可以看 16 进制的编辑器进行对比,在未变化的部分加入 shell 代码
- PNG
参考上面的文章
- JPG
使用 [[JPG_Payload.php]] 这个脚本,注意此脚本最好去传比较小一些的文件,太大了会将 shell 插入错地方导致失败,而且多试几张图片
从 php 代码层面分析文件上传漏洞