网站安全之——文件上传攻击漏洞

转自:http://www.h3c.com.cn/About_H3C/Company_Publication/IP_Lh/2014/05/Home/Catalog/201408/839582_30008_0.htm

不少系统管理员都有过系统被上传后门,木马或者是网页被人篡改的经历,这类攻击相当一部分是通过文件上传进行的。入侵者是如何做到这些的,又该如何防御,本文以PHP脚本语言为例,简要介绍文件上传漏洞,并结合实际漏洞演示如何利用漏洞进行上传攻击。

一、 文件上传漏洞与WebShell的关系

文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器并执行。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。这种攻击方式是最为直接和有效的,部分文件上传漏洞的利用技术门槛非常的低,对于攻击者来说很容易实施。

文件上传漏洞本身就是一个危害巨大的漏洞,WebShell更是将这种漏洞的利用无限扩大。大多数的上传漏洞被利用后攻击者都会留下WebShell以方便后续进入系统。攻击者在受影响系统放置或者插入WebShell后,可通过该WebShell更轻松,更隐蔽的在服务中为所欲为。

这里需要特别说明的是上传漏洞的利用经常会使用WebShell,而WebShell的植入远不止文件上传这一种方式。

1 Webshell简介

WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称之为一种网页后门。攻击者在入侵了一个网站后,通常会将这些asp或php后门文件与网站服务器web目录下正常的网页文件混在一起,然后使用浏览器来访问这些后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载或者修改文件,操作数据库,执行任意命令等)。

WebShell后门隐蔽较性高,可以轻松穿越防火墙,访问WebShell时不会留下系统日志,只会在网站的web日志中留下一些数据提交记录,没有经验的管理员不容易发现入侵痕迹。攻击者可以将WebShell隐藏在正常文件中并修改文件时间增强隐蔽性,也可以采用一些函数对WebShell进行编码或者拼接以规避检测。除此之外,通过一句话木马的小马来提交功能更强大的大马可以更容易通过应用本身的检测。<?php eval($_POST[a]); ?>就是一个最常见最原始的小马,以此为基础也涌现了很多变种,如<script language="php">eval($_POST[a]);</script>等。

2 文件上传漏洞原理

大部分的网站和应用系统都有上传功能,如用户头像上传,图片上传,文档上传等。一些文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,导致允许攻击者向某个可通过Web访问的目录上传任意PHP文件,并能够将这些文件传递给PHP解释器,就可以在远程服务器上执行任意PHP脚本。

当系统存在文件上传漏洞时攻击者可以将病毒,木马,WebShell,其他恶意脚本或者是包含了脚本的图片上传到服务器,这些文件将对攻击者后续攻击提供便利。根据具体漏洞的差异,此处上传的脚本可以是正常后缀的PHP,ASP以及JSP脚本,也可以是篡改后缀后的这几类脚本。

m 上传文件是病毒或者木马时,主要用于诱骗用户或者管理员下载执行或者直接自动运行;

m 上传文件是WebShell时,攻击者可通过这些网页后门执行命令并控制服务器;

m 上传文件是其他恶意脚本时,攻击者可直接执行脚本进行攻击;

m 上传文件是恶意图片时,图片中可能包含了脚本,加载或者点击这些图片时脚本会悄无声息的执行;

m 上传文件是伪装成正常后缀的恶意脚本时,攻击者可借助本地文件包含漏洞(Local File Include)执行该文件。如将bad.php文件改名为bad.doc上传到服务器,再通过PHP的include,include_once,require,require_once等函数包含执行。

此处造成恶意文件上传的原因主要有三种:

m 文件上传时检查不严。一些应用在文件上传时根本没有进行文件格式检查,导致攻击者可以直接上传恶意文件。一些应用仅仅在客户端进行了检查,而在专业的攻击者眼里几乎所有的客户端检查都等于没有检查,攻击者可以通过NC,Fiddler等断点上传工具轻松绕过客户端的检查。一些应用虽然在服务器端进行了黑名单检查,但是却可能忽略了大小写,如将.php改为.Php即可绕过检查;一些应用虽然在服务器端进行了白名单检查却忽略了%00截断符,如应用本来只允许上传jpg图片,那么可以构造文件名为xxx.php%00.jpg,其中%00为十六进制的0x00字符,.jpg骗过了应用的上传文件类型检测,但对于服务器来说,因为%00字符截断的关系,最终上传的文件变成了xxx.php。

m 文件上传后修改文件名时处理不当。一些应用在服务器端进行了完整的黑名单和白名单过滤,在修改已上传文件文件名时却百密一疏,允许用户修改文件后缀。如应用只能上传.doc文件时攻击者可以先将.php文件后缀修改为.doc,成功上传后在修改文件名时将后缀改回.php。

m 使用第三方插件时引入。好多应用都引用了带有文件上传功能的第三方插件,这些插件的文件上传功能实现上可能有漏洞,攻击者可通过这些漏洞进行文件上传攻击。如著名的博客平台WordPress就有丰富的插件,而这些插件中每年都会被挖掘出大量的文件上传漏洞。

3 文件上传攻击实例

前文已经提到造成文件上传漏洞的原因有多种,下面以其中的第二种为例,选取 LibrettoCMS文件上传漏洞(漏洞exploit-db编号为60560)详解整个漏洞的利用过程。

Libretto是一款使用PHP语言和MySQL语言开发的内容管理系统。LibrettoCMS 2.2.2版本允许未验证的用户上传文件,并且可以对已上传的文件进行后缀名修改。虽然系统限制用户只能上传doc和pdf格式的文件,但修改文件名时处理错误,导致用户可修改文件后缀名。攻击者可以将恶意文件后缀改为doc或者pdf,上传成功后再将后缀修改为php即可执行。

l 上传doc后缀的WebShell

访问该系统的文件管理页面/plugins/pgrfilemanager/PGRFileManager.php,上传一个正常的doc文件,发现可以上传成功。编写一个PHP语言的WebShell后门,也可以从网上下载已有的WebShell,并将WebShell文件的后缀修改为doc,此处将myshell.php后门修改为myshell.doc。

准备好WebShell以后访问PGRFileManager.php文件管理页面将myshell.doc上传到服务器,如图1所示,doc后缀的myshell已经成功上传。此时通过浏览器访问该doc格式的myshell是无法正常执行的。

图1 mybshell.doc成功上传

l 将WebShell后缀改为php

在文件管理页面右键点击mybshell.doc并选择rename进入修改文件名称页面,将mybshell.doc改为mybshell.php并点击Ok按钮提交修改结果(如图2所示)。此时myshell文件的后缀已被成功修改了php,受该应用编码实现影响文件管理页面已经无法读取myshell.php文件,但我们在系统服务器的文件上传目录里可以看见修改后的文件(如图3所示)。

图2 将mybshell.doc修改为mybshell.php

图3 服务器里myshell后缀已改为php

l 执行Webshell

此时服务器上传目录里的WebShell已经是php后缀,服务器环境已可以正常解析,通过浏览器直接访问该文件:http://192.168.20.174/vlun/Mylibretto/userfiles/myshell.php,输入WebShell中我们设置的密码即可登录到该WebShell页面(如图4所示)。从图中我们可以看到,仅通过该WebShell文件攻击者就可以在服务器上进行文件管理,数据库管理,执行系统命令,执行任意PHP代码。借助该WebShell,攻击者可以将其他WebShell文件放置到更深层的目录中,或者将PHP后门代码直接添加到系统中已有的很少用的php文件中以防止被系统管理员发现。

图4 成功访问WebShell后门

4 文件上传漏洞防御

l 系统开发阶段的防御

系统开发人员应有较强的安全意识,尤其是采用PHP语言开发系统。在系统开发阶段应充分考虑系统的安全性。对文件上传漏洞来说,最好能在客户端和服务器端对用户上传的文件名和文件路径等项目分别进行严格的检查。客户端的检查虽然对技术较好的攻击者来说可以借助工具绕过,但是这也可以阻挡一些基本的试探。服务器端的检查最好使用白名单过滤的方法,这样能防止大小写等方式的绕过,同时还需对%00截断符进行检测,对HTTP包头的content-type也和上传文件的大小也需要进行检查。

l 系统运行阶段的防御

系统上线后运维人员应有较强的安全意思,积极使用多个安全检测工具对系统进行安全扫描,及时发现潜在漏洞并修复。定时查看系统日志,web服务器日志以发现入侵痕迹。定时关注系统所使用到的第三方插件的更新情况,如有新版本发布建议及时更新,如果第三方插件被爆有安全漏洞更应立即进行修补。对于整个网站都是使用的开源代码或者使用网上的框架搭建的网站来说,尤其要注意漏洞的自查和软件版本及补丁的更新,上传功能非必选可以直接删除。除对系统自生的维护外,服务器应进行合理配置,非必选一般的目录都应去掉执行权限,上传目录可配置为只读。

l 安全设备的防御

文件上传攻击的本质就是将恶意文件或者脚本上传到服务器,专业的安全设备防御此类漏洞主要是通过对漏洞的上传利用行为和恶意文件的上传过程进行检测。恶意文件千变万化,隐藏手法也不断推陈出新,对普通的系统管理员来说可以通过部署安全设备来帮助防御。目前华三通信公司发布的SecPath IPS系列产品经过长期的积累,不但可以基于行为对网络中大量文件上传漏洞的利用进行检测,同时还能基于内容对恶意文件进行识别。

二、 结束语

对攻击者来说,文件上传漏洞一直都是获取服务器shell的重要途径。对系统维护人员来说,文件上传漏洞的巨大危害也无须赘述,积极学习,深入了解漏洞的相关知识可以更从容的面对这类攻击。

 

转自:http://blog.csdn.net/fenglibing/article/details/7728275

本文出自冯立彬的博客,原地址:http://www.fenglibin.com/use_java_to_check_images_type_and_security.html

一、通常情况下,验证一个文件是否图片,可以通过以下三种方式:

1)、判断文件的扩展名是否是要求的图片扩展名

这种判断是用得比较多的一种方式,不过这种方式非常的不妥,别人稍微的把一个不是图片的文件的扩展名修改为图片的扩展名,就绕开了你的这种校验,如果这上传的文件是shell、php或者jsp,那你的网站基本上可以说就在别人的手里面了。

不过这种判断方式也不是完全没有用,我们可以把它放在判断图片的最外层,如果一个文件连扩展名都不是我们所要求的图片扩展名,那就根本不用后面的内容格式检查了,从一定程度上说,对减少服务器的压力还是有一定的帮助,否则所有的文件都等上传完后成后再通过服务器去判断,那会在一定程度上浪费器资源的。

2)、根据文件的前面几个字节,即常说的魔术数字进行判断,不同文件类型的开头几个字节,可以查看我的另外一篇专站介绍:表示不同文件类型的魔术数字

但是这种判断方式也是非常不靠谱的,因为他只能够验证文件的前面几个字节,如此时有人把一个可执行的PHP文件的扩展名修改为PNG,然后再在前面补上”89 50″两个字节,就又绕开了这种验证方式。

以下是一段通过JAVA代码获取文件前面两个字节的示例程序: 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5.    
  6. public class ImageTypeCheck {  
  7.    
  8.     public static String bytesToHexString(byte[] src) {  
  9.         StringBuilder stringBuilder = new StringBuilder();  
  10.         if (src == null || src.length <= 0) {  
  11.             return null;  
  12.         }  
  13.         for (int i = 0; i < src.length; i++) {  
  14.             int v = src[i] & 0xFF;  
  15.             String hv = Integer.toHexString(v);  
  16.             if (hv.length() < 2) {  
  17.                 stringBuilder.append(0);  
  18.             }  
  19.             stringBuilder.append(hv);  
  20.         }  
  21.         return stringBuilder.toString();  
  22.     }  
  23.     public static void main(String[] args) throws IOException {  
  24.         String imagePath = "c:/favicon.png";  
  25.         File image = new File(imagePath);  
  26.         InputStream is = new FileInputStream(image);  
  27.         byte[] bt = new byte[2];  
  28.         is.read(bt);  
  29.         System.out.println(bytesToHexString(bt));  
  30.     }  
  31. }  

 

 

不过这种判断方式和判断扩展名一样,也不是完全没有用,至少可以在前期在简单的检查,为进入下一步检查做铺垫。

3)、获取图片的宽高属性
如果能够正常的获取到一张图片的宽高属性,那肯定这是一张图片,因为非图片文件我们是获取不到它的宽高属性的,以下是用于获取根据是否可以获取到图片宽高属性来判断这是否一张图片的JAVA代码:

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. /** 
  2.  * 通过读取文件并获取其width及height的方式,来判断判断当前文件是否图片,这是一种非常简单的方式。 
  3.  *  
  4.  * @param imageFile 
  5.  * @return 
  6.  */  
  7. public static boolean isImage(File imageFile) {  
  8.     if (!imageFile.exists()) {  
  9.         return false;  
  10.     }  
  11.     Image img = null;  
  12.     try {  
  13.         img = ImageIO.read(imageFile);  
  14.         if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {  
  15.             return false;  
  16.         }  
  17.         return true;  
  18.     } catch (Exception e) {  
  19.         return false;  
  20.     } finally {  
  21.         img = null;  
  22.     }  
  23. }  



 

二、图片文件的安全检查处理
好了,我们终于判断出一个文件是否图片了,可是如果是在一个可以正常浏览的图片文件中加入一些非法的代码呢:

这就是在一张正常的图片末尾增加的一些iframe代码,我曾经尝试过单独打开这张图片,也将这张图片放于网页上打开,虽然这样都不会被执行,但并不代表插入其它的代码也并不会执行,杀毒软件(如AVAST)对这种修改是会报为病毒的。
那我们要如何预防这种东西,即可以正常打开,又具有正确的图片文件扩展名,还可以获取到它的宽高属性?呵,我们这个时候可以对这个图片进地重写,给它增加水印或者对它进行resize操作,这样新生成的图片就不会再包含这样的恶意代码了,以下是一个增加水印的JAVA实现:

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. /** 
  2.      * 添加图片水印 
  3.      *  
  4.      * @param srcImg 目标图片路径,如:C:\\kutuku.jpg 
  5.      * @param waterImg 水印图片路径,如:C:\\kutuku.png 
  6.      * @param x 水印图片距离目标图片左侧的偏移量,如果x<0, 则在正中间 
  7.      * @param y 水印图片距离目标图片上侧的偏移量,如果y<0, 则在正中间 
  8.      * @param alpha 透明度(0.0 -- 1.0, 0.0为完全透明,1.0为完全不透明) 
  9.      * @throws IOException 
  10.      */  
  11.     public final static void addWaterMark(String srcImg, String waterImg, int x, int y, float alpha) throws IOException {  
  12.         // 加载目标图片  
  13.         File file = new File(srcImg);  
  14.         String ext = srcImg.substring(srcImg.lastIndexOf(".") + 1);  
  15.         Image image = ImageIO.read(file);  
  16.         int width = image.getWidth(null);  
  17.         int height = image.getHeight(null);  
  18.    
  19.         // 将目标图片加载到内存。  
  20.         BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);  
  21.         Graphics2D g = bufferedImage.createGraphics();  
  22.         g.drawImage(image, 00, width, height, null);  
  23.    
  24.         // 加载水印图片。  
  25.         Image waterImage = ImageIO.read(new File(waterImg));  
  26.         int width_1 = waterImage.getWidth(null);  
  27.         int height_1 = waterImage.getHeight(null);  
  28.         // 设置水印图片的透明度。  
  29.         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));  
  30.    
  31.         // 设置水印图片的位置。  
  32.         int widthDiff = width - width_1;  
  33.         int heightDiff = height - height_1;  
  34.         if (x < 0) {  
  35.             x = widthDiff / 2;  
  36.         } else if (x > widthDiff) {  
  37.             x = widthDiff;  
  38.         }  
  39.         if (y < 0) {  
  40.             y = heightDiff / 2;  
  41.         } else if (y > heightDiff) {  
  42.             y = heightDiff;  
  43.         }  
  44.    
  45.         // 将水印图片“画”在原有的图片的制定位置。  
  46.         g.drawImage(waterImage, x, y, width_1, height_1, null);  
  47.         // 关闭画笔。  
  48.         g.dispose();  
  49.    
  50.         // 保存目标图片。  
  51.         ImageIO.write(bufferedImage, ext, file);  
  52.     }  


通过以上几种方式,应该可以避免绝大部份图片中带恶意代码的安全问题,不过由于我个人的才疏学浅,可能有没有考虑周全的地方,还请各位不吝指教了。

 

 

 

猜你喜欢

转载自kavy.iteye.com/blog/2219245