Web 安全防护指南基础篇-文件上传

ʕ •ᴥ•ʔ (๑˃̵ᴗ˂̵) ପ( ˘ᵕ˘ ) ੭

1 文件上传

1.1 上传攻击原理

上传功能是用户与服务器进行文件交互的重要手段,攻击者的目标是取得当前 Web 服务器的权限。若通过 Web 层面开展攻击,必须将攻击者的木马插入 Web 系统中,并在服务器端执行。这就是文件注入攻击。

  1. 攻击原理文件上传攻击攻击者利用 Web 应用对文件上传过滤不严的漏洞,将应用程序定义范围之外的文件上传到 Web 服务器,并且此类文件通常是木马,在上传成功后攻击者即可获得当前的 webshell。
  2. 上传原理文件上传原理攻击者想要获取webshell,最直接的方法就是将 Web 木马插入到服务器进行成功解析。那么如何理解成功解析?假设目标服务器为用PHP语言构建的 Web 系统,那么针对上传点就需要利用PHP木马,并且要求木马在服务器以后缀名为.php进行保存。因此,上传木马的过程就是在 Web 系统中新增一个页面。 当木马上传成功后,攻击者就可远程访问这个木马文件,也就相当于浏览一个页面,只不过这个页面就是木马,具备读取、修改文件内容、连接数据库等功能。
  3. 漏洞原因上传漏洞存在前提存在上传点且在上传点用户可独立控制上传内容,同时上传文件可被顺利解析。

攻击端上传木马(.php)--> 服务器,攻击端访问木马所在目录 --> 服务器(成功解析并执行)

上传攻击防护思路

1.2 上传攻击条件

  1. 目标网站具有上传功能
  2. 上传的目标文件(木马)能够被 Web 服务器解析执行
  3. 知道文件上传到服务器后的存放路径和文件名称
  4. 目标文件可被用户访问

以上四个条件为文件上传攻击的必要条件,对于攻击者来说缺一不可,对于防护这来说必须至少解决其中一条。

1.3 上传检测绕过技术

基于客户端参数的检测手段:JS 防护、MIME 防护。

1.3.1 客户端 Javascript 检测及绕过

防护方法:客户端 Javascript 检测是在表单上传到服务器前进行的检测, 检测文件名后缀,允许的后缀名放行通过,使用白名单方式。

绕过方法

  • 浏览器禁用 JS 运行
  • 抓包软件抓包更改后缀名(JS 防护手段的 JS 脚本是在 HTTP 数据包发出之前执行完毕的)

1.3.2 服务器端 MIME 检测及绕过

在 HTTP 协议中,会利用Content-Type标识本次上传的内容类型。这个类型由客户端的浏览器根据本次上传文件的后缀名自动生成,这里检测到的Content-Type是在body里。

客户端上传文件时会自动检测上传的文件类型,检测的依据是文件后缀,检测的方法是通过检查 HTTP 包的Content-Type字段中的值来实现的。将php文件的后缀改成png,MIME 检测类型判断后缀为imae/png

常见的类型如下:

Content-Type 含义 Content-Type 含义
Image/jpg JPG 图像 Text/html HTML 文档
Image/gif GIF 图像 application/XML XML 文档
Image/png PNG 图像 application/PDF PDF 文档

防护方法:通过在客户端浏览器检测Content-Type,使用白名单的方式放行通过。类似于客户端 Javascript 检测防护方法。

绕过方式:由于Content-Type类型是由客户端浏览器自动生成的,那么在这个过程中Content-Type的值是可控的(因为在客户端生成),只需要 HTTP 抓包后修改Content-Type即可绕过。

1.3.3 服务器端文件扩展名检测及绕过

防护思路

  1. 基于客户端参数的检测手段可以在文件上传到服务器前对 HTTP 报文拦截进行修改,当服务器就收到上传的信息后,校验文件名是否合法,采用黑/白名单方式。
  2. 完全不信赖用户所上传文件的后缀名,在用户上传文件到服务器之后,重新给上传文件添加后缀名

防护方法

  • 文件后缀重命名
  • 白名单过滤
  • 黑名单过滤

对于文件上传在服务器端文件重命名(重命名文件名和文件后缀)时,能够针对后缀名的各种绕过。通常会使用一个随机数生成函数来生成文件名,可以防止%00截断和解析漏洞。由于该方法适用场景单一,目前基本上无有效手段进行绕过。

文件重命名防护代码示例:

1
2
3
4
$filerename ='jpg';
$newfile = md5 (uniqid (microtime(7)).'_'.$flerename;
if(move_uploaded_fle($_ FILES['upfile']l'tmp_name'], $distination.'/'.$newfile))
......

绕过思路:黑白名单机制归根结底为限制用户的上传文件后缀名,避免可解析成 Web 的后缀名出现。可尝试使用未被过滤的文件后缀、解析漏洞等构造非限制条件进行绕过。

绕过方法

针对黑名单:

  1. 多重测试过过滤文件名

    如针对.php的文件禁止上传,但没有对其他格式做出限制,可构造php4php5cer等后缀,同时中间件仍旧按照.php等进行解析。

  2. 后缀名大小写绕过

  3. 后缀名包含绕过

    假如禁用.php后缀名,可构造.pphphp进行绕过。

  4. 特殊文件名构造(Windows 下)

    由于黑名单方式是先对文件后缀进行校验,然后再将临时文件转存到真实路径下。该绕过方式是在文件后缀名验证通过后,在临时文件转存之前进行绕过。

    代理抓上传的包将文件后缀名构造为.php..php_,文件上传到服务器后首先进行黑名单后缀名验证,验证通过后进行文件转存时,由于 Windows不识别这些后缀名,回自动去掉._等特殊符号,从而绕过黑名单防护。

  5. %00截断

    此绕过方式利用的是 C 语言的终止符特性,当 C 语言在执行过程中遇到%00%00会被当成终止符,因此程序会自动截断后续信息,仅保留%00之前的内容。此漏洞仅存在于PHP 5.3.7之前的版本,如shell.php .jpg (注意,jpg 前有一个空格)。在上传页面进行转存时,之前文件名中的空格会被当成终止符,导致空格之后的内容被忽略。因此,最终文件名会变为shell.php,从而绕过了文件后缀名检查。

针对白名单:

相对于黑名单防护手段,白名单限制更为严格,非允许的后缀名一律拒绝上传,所以在黑名单中常用的修改大小写绕过手段、多类型后缀名绕过手段等,由于都无法满足白名单的过滤规则,因此都会被过滤。只有如下两种方式可以绕过防护机制:

  • 特殊文件名构造(参考黑名单防护)
  • 0x00截断(参考黑名单防护)

1.3.4 服务器端文件内容检测及绕过

防护思路

针对图像文件,对文件内容的检测主要有以下三种方法。

  1. 通过检测上传文件的文件头来判断当前文件格式。
  2. 调用 API 或函数对文件进行加载测试,常见的是图像二次渲染。
  3. 检测上传文件是否为图像文件内容。

在上传漏洞的防护过程中,在文件上传时检测内容是一项非常有效的防护措施。防护手段可包含检测文件的文件头及内容图像格式是否合法(参考第一、三种防护方法),或者在显示过程中调用函数进行二次渲染,导致木马等非图像代码由于无法渲染成像素而被丢弃,从而达到防护的效果(参考第二种防护方法)。但需注意的是,针对图像二次渲染会给服务器带来额外的性能开销。因此需要根据业务防护需求来寻找性能开销与安全要求之间的平衡点。

防护代码

(1)文件头判断

读取文件开头部分的数个字节,判断文件头与文件类型是否匹配。通常情况下,通过判断前10个字节基本上就能判断出一个文件的真实类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function getTypeList( )
{
return array(
array("FFD8FFE0", "jpg"),
array("89504E47", "png"),
array("47494638""gif"),
array("49492A00""tif"),
array("424D", "bmp"),
array("41433130""dwg"),
array("38425053", "psd"),
array("7B5C727466", "rtf"),
array("3C3F786D6C", "xml"),
array("68746D6C3E", "html"),
array("44656C69766572792D646174", "eml"),
array ("CFAD12FEC5FD746F", "dbx"),
array("2142444E", "pst"),
array("DOCF11E0", "xls/doc"),
array("5374616E64617264204A", "mdb"),
array("FF575043", "wpd"),
array("252150532041646F6265", "eps/ps"),
array("255044462D312E", "pdf"),
array("B3828596", "pwl")
array("504B0304", "zip"),
array("52617221", "rar"),
array("57415645", "wav"),
array("41564920", "avi"),
array("2E7261FD", "ram"),
array("2B524D46", "rm"),
array("000001BA", "mpg"),
array("000001B3", "mpg"),
array("6D6F6F76", "mov"),
array("3026B2758E66CF11", "asf"),
array("4D546864", "mid"));
}
function checkFileType($fleName){
$file = @fopen( $fleName, "rb");
$bin = fread($fle, 5); //只读5个字节
fclose($file);
$typelist=getTypeList();
foreach ($typelist as $v)
{
$blen=strlen(pack("H*",$v[0])); // 得到文件头标记字节数
$tbin=substr($bin,0,intval($blen));//需要比较文件头长度
if(strtolower($v[0])==strtolower(array_shift(unpack("H*",$tbin))))
{
return $v[1];
}
}
return 'error';
}
$upfile=$_FILES["upfile"];
$name=$upfile["name"];
$type=$upfile["type"];
$size=$upfile["size"];
$tmp_name=$upfile["tmp_ name"];
$distination = '/var/www/html/up1oad_04/fle/'.$name;
echo checkFileType($upfile['tmp_name']);
move_uploaded_file($upfile['tmp_name'],$distination);

(2)文件加载检测中的图像二次渲染

(3)图像内容检测

绕过方法

  1. 文件头检测

    修改文件头,对前20字节进行替换,后面再插人一句话木马,即可实现对文件内容检测的绕过。使用时先在要上传的文件的所有内容前添加GIF89a,Web 系统可判断当前文件为gif类型。需要注意的是,在实际中仅使用这种方法很多时候不能成功,因为上传功能还检测了后缀名MIME等。因此若仅仅是针对文件内容检测,可采用这种方法进行尝试。

  2. 文件二次渲染(极难)

1)基于数据二义性,即让数据既是图像数据也包含一句话木马。

2)对文件加载器进行溢出攻击。

这里需要注意的是,如果仅仅对.php文件添加上述文件头,并不一定会控制MIME的生成值。因为在不同浏览器下,针对文件生成MIME时会有不同的情况。IE浏览器默认会根据文件头确定MIME值。但是ChromeFirefox 则仍以后缀名方式进行判断。

1.3.5 上传流程安全防护总结

攻击者利用上传功能实现的主要目标是:

  • 上传木马
  • 让木马按照Web格式进行解析

因此,在防护手段上,系统设计者在设计之初,考虑到系统性能问题,无法对每个上传的内容进行检查。这是因为多数上传的文件内容过于庞大,如果贸然对文件内容进行完全检查,则要消耗大量的系统资源,同时对系统速度造成极大影响,导致用户体验下降。因此有效的防护手段就是避免木马按照既定格式进行上传。可用的思路有:

(1)限制高危扩展名上传

  • 利用黑白名单确定后缀名是否合法。
  • 根据应用特点重新对上传文件进行后缀重命名。

(2)限制高危文件内容出现

  • 利用内容检索来检测是否存在非正常内容。
  • 确认图片格式与上传文件内容是否对应。
  • 在图像加载时重新渲染,避免非图像内容出现。

不同于XSS攻击与SQL注入攻击,在上传过程中,在每个步骤上均可开展防护,根本目标是避免木马在服务器端执行,这也就是上传攻击的防护初衷。

1.4 文件解析漏洞

解析漏洞可以说是 Web 安全中比较“古老”的漏洞类型,绝大多数解析漏洞都是由于中间件的版本过低造成的。由于中间件在判断文件类型时,存在判断机制的问题,导致在实际解析过程中并不会按照既定的后缀名进行解析。攻击者针对这种情况,在文件名构造上进行特殊设计,导致中间件在判断此类特殊文件名时触发解析漏洞,实际解析后的文件 与文件名并不相同(.jpg文件会被当作.php文件执行)。解析漏洞的危害在于 Web 应用防护方面防护看似非常到位,但仍存在安全隐患。目前主流中间件在老版本中均在解析漏洞。以下为常见的存在解析漏洞的中间件及对应特性。

低版本的中间件存在解析漏洞。 下图是上传安全防护流程图。 

上传安全防护流程图

1.4.1 .htaccess 攻击

Apache 中允许多站点同时解析,关于Apache 的虚拟主机、虚拟目录、多站点:

多站点:访问主机服务的本质访问 Web 服务,本质上看(从协议上)是访问某个 IP 的主机上的某个端口(默认是80),通常需要通过访问不同的域名或者端口,实现对不同网站的访问(具体到服务器里就是不同目录),这个时候就需要设置网站空间。通常分为3种:基于域名、基于端口、基于IP以及它们的混合

Apache 的虚拟主机就是在一台服务器上运行多个网站,每个虚拟主机都可以绑定独立的域名,为这些域名可以指定单独的目录,访问这些域名的时候,Apache 会打开对应目录里面的东西。实际是依靠域名访问不同的目录文件

目录别名(虚拟目录): 某个站点www.abc.com所对应文件夹下有一个目录为d1,则可以这样访问该目录: www.abc.com/d1 这个d1就称为“真实目录”; 虚拟目录就是: 该站点下不存在某目录名,但却可以使用同样的语法形式,去访问,比如: www.abc.com/d2 ,假设该站点目录下根本就没有d2这个目录,此时却可以访问实现这种技术,就是所谓“目录别名”(虚拟目录

Apache 虚拟主机文档Apache2.4虚拟主机和多站点配置单站点和多站点和虚拟目录Apache的虚拟主机功能

虚拟主机:Apache的虚拟主机功能 (Virtual Host) 是可以让一台服务器基于IP、主机名或端口号实现提供多个网站服务的技术。简单来说,Apache 虚拟主机就是在一个 Apache 服务器上配置多个虚拟空间,实现一个服务器提供多站点服务,其实就是访问同一个服务器上的不同目录。每个目录对应一个站点。

.htaccess文件是 Apache 服务器中最常用的一个配置文件,它负责相关目录下的网页配置。通过 .htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置索引入口等功能。

在早期以黑白名单为主流防护技术的时期,攻击者经常在获得当前系统的 Webshell 之后,会修改.htaccess文件内容,添加未知后缀以.php的方式执行(任意后缀的文件都会被当作 php 文件执行)。这样就算当前的 Webshell 丢失,也可在下次攻击时直接利用未知文件名重新快速获得 Webshell,这也算另一种“后门”的效果。

虽然用户端做了大量的防护功能,但是可利用虚拟目录功能,实现中间件层的解析欺骗,导致木马可以非正常后缀名进行执行。不过,实际过程中可利用的场景非常少,主要是由于.htaccess 文件需放在当前 Web 目录下面。攻击者如果能在当前Web目录下插人任意的文件,其实攻击者已经获得当前系统的webshell,也就没有必要利用这种方式进行攻击。

htaccess功能

1.4.2 Apache 解析漏洞攻击

Apache中间件早期版本在后缀解析test.php.x1.x2.x3时,Apache 将从右至左开始判断后缀,若x3为非可识别后缀,再判断x2,直到找到可识别后缀为止,然后将该可识别后缀进解析。例如,test.php.x1.x2.x3会被解析为php

解析漏洞的危害在于对以上的文件上传防护机制都可以进行绕过。当然,存在解析漏洞的中间件的版本都比较老,新版本不存在上述问题。因此要解决解析漏洞的问题,简单有效的方法就是升级中间件版本。

另外有一点需要注意, 很多人会使用集成环境来部署 Web 服务器。集成环境部署简易、使用方便,因此应用广泛。但需要注意的是,集成环境的软件版本并不是中间件的版本。因此从安全角度考虑,需要严格检查集成环境中的 Apache及 PHP 版本,避免出现解析漏洞或截断漏洞等。下面是些存在解析漏洞的集成环境版本及中间件对应版本信息,用以核对当前服务器安全状态或进行漏洞测试:

  • WampServer2.0 All Version (WampServer2.0i / Apache 2.2.11)
  • WampServer2. 1 All Version (WampServer2. le-x32 / Apache 2.2.17)
  • Wamp5 All Version (Wamp5_ 1.7.4 / Apache 2.2.6)
  • AppServ 2.4 All Version (AppServ - 2.4.9 / Apache 2.0.59)
  • AppServ 2.5 All Version (AppServ - 2.5.10 / Apache 2.2.8)
  • AppServ 2.6 AlI Version (AppServ - 2.6.0 / Apache 2.2.8)

1.4.3 IIS 解析漏洞攻击

(1) IIS 6.0

随着 Windows Server 2003 的停止维护,IIS 6.0 的利用范围也在大幅度缩小。目前,新版的 IIS 中已不存在解析漏洞。这里仅以6.0环境作为漏洞利用场景来分析解析漏洞的综合利用方式。在解析asp格式的时候有两个解析漏洞:

1)如果目录名包含.asp字符串,那么这个目录下所有的文件都会按照asp格式解析。例如,对于/xx.asp/xx.jpgxx.jpg可替换为任意文本文件),IIS 6.0 会将xx.jpg解析为asp文件。

2)只要文件名中含有 .asp;,就会优先按asp来解析。例如,对于/xx.asp;jpg (此处需抓包修改文件名),IIS 6.0 都会把此类后缀文件成功解析为asp文件。(/xx.asp:.jpg这 类文件在Windows下不允许存在,:.jpg 被自动除去,剩下/xx.asp。)

HIS 6.0 默认的可执行文件除了asp还包含如下三种: /xx.asa /xx.cer/xx.cdx。(在 IIS默认配置中,这几个后缀默认由asp.dll来解析,所以执行权限和.asp一模一样,可在配置中自行删除该后缀,以免出现安全隐患。)

(2)IIS 7.0/IIS 7.5

IIS 7.0/7.5 存在的解析漏洞利用场景比 IIS 6.0要苛刻,主要是在对php解析时存在类似于 Nginx 的解析漏洞,并且需要处于Fast-CGI开启状态。在漏洞利用方式上,只要在任意文件名的 URL 后追加字符串/任意文件名.php,那么当前文件就会按照php的方式解析。

利用方法如下:将一张图和一个写人后门代码的文本文件合并, 将恶意文本写人图片的二进制代码之后,避免破坏图片文件头和尾。Windows 下可利用 CMD 命令实现文件拼接::copy xx.jpg/b + yy.txt/a xy.jpg

其中,参数的意义为:

  • /b 二进制(binary)模式。
  • /a ASCII 模式xx.jpg正常图片文件。
  • yy.txt的内容如下:
1
<?PHP fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>

这段代码的作用是写人一个内容为<?php eval($_POST[cmd])?> 、名称为shell.php的文件。利用图片合成工具,也可达到上述效果。

执行成功后的效果就是在上传文件的真实目录下生成一句话木马文件shel.php,再利用各类连接工具连接即可完成整体漏洞利用。需要注意的是,IIS7.0/7.5 下的解析漏洞是由于php-cgi本身的问题导致,与 IIS 自身并没有直接关系

1.4.4 Nginx 解析漏洞攻击

目前 Nginx 的解析漏洞利用方式与 Apache、IIS基本一致。一个是对于任意文件名,在后面添加/任意文件名.php的解析漏洞,比如原本文件名是test.jpg,可以添加为test.jpg/x.php进行解析攻击。还有一种是针对低版本的 Nginx,可以在任意文件名后面添加%00.php进行解析攻击。

下面几个版本都存在此问题:

  • Nginx 0.5.*
  • Nginx 0.6.*
  • Nginx 0.7 <= 0.7.65
  • Nginx 0.8 <= 0.8.37

任意文件名/任意文件名.php这个漏洞其实是出现自php-cgi的漏洞,与 Nginx 自身无关,这点与 IIS 7.0/7.5 与 PHP 配合使用时解析漏洞产生原理一样。