盒子
盒子
文章目录
  1. 无丝毫验证的文件上传
  2. Mime类型验证
  3. 限制危险的拓展
  4. 服务端扩展名验证
  5. 检查图片头部
  6. js验证
  7. 通过.htaccess保护上传文件夹
  8. 客户端验证

文件上传小总结

本文主要总结下文件上传,主要是通过PHP的逻辑解释文件上传中的一些安全问题。在前人总结的基础上自己再琢磨琢磨。。。

上传漏洞通常是一个文件上传点,其缺乏相应的安全验证,从而可以上传恶意代码,网站最终被getshell
下面我们分情况分析

无丝毫验证的文件上传

一个简单的文件上传表单通常包含一个HTML表单和PHP脚本。HTML表单呈现给用户,而文件上传功能的通常通过PHP脚本实现。下面是一个例子:

HTML表单如下:

1
2
3
4
5
<form enctype="multipart/form-data" action="uploader.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Choose a file to upload: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>

PHP代码如下:

1
2
3
4
5
6
7
8
9
<?php
$target_path = "uploads/";
$target_path = $target_path . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file " . basename($_FILES['uploadedfile']['name']) . " has been uploaded";
}else{
echo "There was an error uploading the file, please try again!";
}
?>

当PHP接收POST请求且编码类型是multipart/form-data,它会在一个临时的文件名随机的临时目录创建一个文件(例如/ var/tmp/php6yXOVs)。 PHP也将填充全局数组$_FILES上传的文件的信息:
$_FILES [‘UploadedFile的’] [‘name’]:在客户机上的文件的原始名称
$_FILES [‘UploadedFile的’] [‘type’]:文件的MIME类型
$_FILES [‘UploadedFile的’] [‘size’]:文件的大小(以字节为单位)
$_FILES [‘UploadedFile的’][‘tmp_name’]:上传的文件存储在服务器上的临时文件名。
PHP函数move_uploaded_file将用户提供的临时文件移动到一个位置。在这种情况下,目的地是服务器根目录以下。因此,文件可以使 用的URL来访问。
在这个简单的例子中,由于上传的文件类型没有限制,因此攻击者可以上传一个PHP或.NET等带有恶意代码的文件,可导致服务器被getshell。

但是这种毫无验证的方式通常不会存在,下面从不同的验证方法上看上传绕过。

Mime类型验证

MIME的作用:使客户端软件,区分不同种类的数据,例如web浏览器就是通过MIME类型来判断文件是GIF图片,还是可打印的PostScript文件。web服务器使用MIME来说明发送数据的种类, web客户端使用MIME来说明希望接收到的数据种类。
常见的错误是Web开发人员验证文件上传表单时,只检查从PHP返回mime类型。当一个文件被上传到服务器,PHP将设置变量$_FILES[‘UploadedFile’][‘type’]所提供的Web浏览器客户端使用的MIME类型。

服务器端(tomcat5.5)接收不同浏览器上传的文件时,取得的MIME类型

type 用IE7上传 用Firefox3.0上传
GIF image/gif image/gif
JPG image/jpeg image/jpeg
ZIP application/x-compressed application/octet-stream
JSP text/html text/html
EXE application/octet-stream application/octet-stream

常见MIME类型例表:

序号 内容类型 文件扩展名 描述
1 application/msword doc Microsoft Word
2 application/octet-stream bin dms lha lzh exe class 可执行程序
3 application/pdf pdf Adobe Acrobat
4 application/postscript ai eps ps PostScript
5 appication/powerpoint ppt Microsoft Powerpoint
6 appication/rtf rtf rtf格式
7 appication/x-compress z unix 压缩文件
8 application/x-gzip gz gzip
9 application/x-gtar gtar tar 文档 (gnu 格式 )
10 application/x-shockwave-flash swf MacroMedia Flash
11 application/x-tar tar tar(4.3BSD)
12 application/zip zip winzip
13 audio/basic au snd sun/next 声音文件
14 audio/mpeg mpeg mp2 Mpeg 声音文件
15 audio/x-aiff mid midi rmf Midi 格式
16 audio/x-pn-realaudio ram ra Real Audio 声音
17 audio/x-pn-realaudio-plugin rpm Real Audio 插件
18 audio/x-wav wav Microsoft Windows 声音
19 image/cgm cgm 计算机图形元文件
20 image/gif gif COMPUSERVE GIF 图像
21 image/jpeg jpeg jpg jpe JPEG 图像
22 image/png png PNG 图像

绕过
判断方法:基于不同mime类型多上传几次来测试
MIME类型通常在http头中,我们很容易通过抓包,改包的方式去绕过此种验证方式。最常用burp去修改http包。

限制危险的拓展

在另一个例子里,我们遇到了文件上传使用黑名单的做法,作为一项安全措施。从开发者收集制定的危险列表,如果正在上传的文件扩展名包含在列表中,访问会被拒绝。
使用危险的文件扩展名,其主要缺点之一是,它几乎不可能编制一份完整的清单,包括攻击者可以使用的所有可能的扩展名。
恶意用户可以很容易地绕过该检查上传一个文件名为“.htaccess”,其中包含类似于下面的一行代码:AddType application/x-httpd-php .jpg
上面的代码行指示ApacheWeb服务器执行jpg图片,好像他们是PHP脚本。攻击者现在可以上传一个jpg扩展名的文件,其中包含PHP代码。通过web浏览器请求一个jpg文件,其中包含PHP的命令phpinfo()函数,Web服务器依旧会执行php代码。

服务端扩展名验证

一般都是黑名单验证的,这部分建议看一下关于解析漏洞的文章:解析漏洞小总结
如何绕过:

  1. 找黑名单扩展名的漏网之鱼 – 通常对于asp就是 asa、cer之类,对于php就是php3、php4等
  2. 可能存在大小写绕过漏洞 – 比如 aSp 和 pHp 之类
  3. 特别文件名构造 – 比如发送的 http 包里把文件名改成 help.asp. 或 help.asp_(下划线为空格),这种命名方式在 windows 系统里是不被允许的,所以需要在 burp 之类里进行修改, 然后绕过验证后,会被windows系统自动去掉后面的点和空格。
  4. IIS或nginx文件名解析漏洞 – 比如 help.asp;.jpg或http://www.xx.com/help.jpg/2.php。这里注意网上所谓的 nginx 文件名解析漏洞实际上是 php-fpm 文件名解析漏洞,详见 http://www.cnbeta.com/articles/111752.htm
  5. 0×00 截断绕过 – 这个是基于一个组合逻辑漏洞造成的
  6. 双扩展名解析绕过攻击(1) – 基于 web 服务的解析逻辑。比如上传x.php.rar等文件
  7. 双扩展名解析绕过攻击(2) – 基于 web 服务的解析方式(解析漏洞)
  8. 如果在 Apache 的 conf 里有这样一行配置AddHandler php5-script .php 这时只要文件名里包含.php,即使文件名是 test2.php.jpg 也会以 php 来执行

检查图片头部

当仅允许上传图片的时候, 开发者通常使用PHP的getimagesize函数来检测图片的头部信息。该函数在被调用时将会返回图片的尺寸, 如果图片经验证为无效的, 也就是说图片头部信息不正确, 则会返回 false 值。因此一个开发者一般会检查该函数是否返回true或false,并且通过该信息来验证上传的文件。所以, 如果一个恶意用户试着上传一个内嵌有简单PHP shell的jpg文件的话,该函数会返回false,然后他将不允许上传此文件。然而, 即使这种方式也能被很容易的绕过。如果一个图片在一个图片编辑器内打开, 就如Gimp,用户就可以编辑图片的注释区, 那儿就能插入PHP代码,该图片仍然有一个有效的头部; 因此就绕过了getimagesize函数的检查。
这里就是php图片马的原理了。一般我们做图片马尽量不要去损坏图片头信息。

js验证

判断方法:点击提交,用burp看是否有数据提交到服务器端,或者直接看是否直接弹出不允许的上传类型。
绕过方法:

  1. 我们直接删除代码中onsubmit事件中关于文件上传时验证上传文件的相关代码即可。
  2. 直接更改文件上传JS代码中允许上传的文件扩展名你想要上传的文件扩展名。
  3. 使用本地构造提交表单即可,作相应的更改。
  4. 使用burpsuite或者是fiddle等代理工具提交,本地文件先更改为jpg,上传时拦截,再把文件扩展名更改为asp即可。这也是最简单,最常用的了。

通过.htaccess保护上传文件夹

另一种流行的创建安全的文件上传表单的方法是使用.htaccess保护好上传文件存放的文件夹。办法是限制这个文件夹里的脚本文件的执行。这种情形一下一个.htaccess文件一般包含下面的代码:

1
2
AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi
Options –ExecCGI

上面的是另一种形式的黑名单,本身并不是很安全。在PHP手册中,move_uploaded_file一章中,有一个warning:若目标文件已经存在,则会覆盖原文件。因为上传的文件能够而且会覆盖已经存在的同名文件,一个恶意用户很轻易就能用他自己修改过的.htaccess替换掉原来的。进而可以 上传执行特定的恶意脚本。

客户端验证

另一种在文件上传表单中常用的安全技术是在客户端验证上传的文件。一般而言,该技术在ASP.NET应用中更通用一些,因为ASP.NET提供了易用的验证控件。
这些验证控件允许开发者对要上传的文件做正则检查,以查出待上传的文件扩展名是否在允许列表中。下面是一段来自微软网站的示例代码:

1
<asp:FileUpload ID="FileUpload1" runat="server" />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Upload File" />
<asp:Label ID="Label1" runat="server"></asp:Label>
<asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" ErrorMessage="Only mp3, m3u or mpeg files are allowed!" ValidationExpression="^(([a-zA-Z]:)|({2}w+)$?)((w[w].*))+(.mp3|.MP3|.mpeg|.MPEG|.m3u|.M3U)$" ControlToValidate="FileUpload1"></asp:RegularExpressionValidator>
<asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ErrorMessage="This is a required field!" ControlToValidate="FileUpload1"></asp:RequiredFieldValidator>

这段ASP.NET代码使用了验证控件,所以最终用户只被允许上传.mp3,.mpeg,或者.m3u文件到服务器。若文件类型和这三个指定的文件类型不一致,验证控件将抛出异常,文件也就不会被上传。
由于这种文件验证是在客户端完成的,恶意用户很容易就能绕过这一检查。写一段客户端脚本来替换web应用的验证脚本做验证。不用web浏览器,入侵者可以使用可以发送HTTP POST请求的程序来实现上传文件。

推荐的解决方案

  • 定义一个.htaccess文件,只允许访问指定扩展名的文件。不要把.htaccess文件和上传文件放在同一个目录里,应该放在父目录里。一个典型的只允许 gif, jpg, jpeg 和 png文件的.htaccess文件应当包含下面的代码(根据你的需求做调整)。这样也能阻止双扩展名攻击.

    1
    2
    3
    4
    5
    deny from all
    <Files ~ "^w+.(gif|jpe?g|png)$">
    order deny,allow
    allow from all
    </Files>
  • 如果可能,把文件上传到root目录以外的目录里。

  • 禁止覆盖已存在的文件(以阻止.htaccess覆盖攻击)
  • 创建一个mime-type白名单列表。(只允许这个列表里的Mime-type)
  • 生成一个随机的文件名,并且加上此前生成的文件扩展名、
  • 不要只依赖客户端验证,这不够。理想的是既有客户端验证也有服务器端验证。

参考文章:

上传漏洞科普[1]-文件上传表单是Web安全主要威胁
上传漏洞科普[1]-js验证