upload-labs文件上传靶场闯关

uploads-lab靶场闯关

0x01 Pass-01 前端检测

源代码

function checkFile() {
    //这里getElementsByName取name的值,,判断表单里是否选择了文件
    var file = document.getElementsByName('upload_file')[0].value;
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    //定义允许上传的文件类型
    var allow_ext = ".jpg|.png|.gif";
    //提取上传文件的类型
    // substring()表示返回一个从里面的参数位置开始到末尾的字符串
    // lastIndexOf()表示 返回'.'首次出现的位置,如果未发现此字符串返回 -1
    var ext_name = file.substring(file.lastIndexOf("."));
    //判断上传文件类型是否允许上传
    if (allow_ext.indexOf(ext_name + "|") == -1) {
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
}

Get到javascript的知识点:

onsubmit="return false;" 将无论何时都阻止表单的提交
onsubmit="return check();" 是否提交表单取决于check()的返回值
onsubmit="check();" check()的返回值无影响
null:代表声明了一个空对象,不是一个字符串,可以赋给任何对象,是没有地址。
"":  代表声明了一个对象实例,这个对象实例的值是一个长度为0的空字符串,是有地址但是里面的内容是空的。

绕过方法

  • ​ 在控制台修改前端js代码,在allow_ext中 添加 '.php' 即可绕过
  • ​ 在前端界面审查元素处,删除检测上传的js函数,或者浏览器禁止js运行
  • ​ 上传shell.jpg,用burpsuit抓包,修改后缀为shell.php 即可绕过

0x02 Pass-02 MIME类型 检测

源代码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) { //upload_file 已经在别的文件里定义好了,表示'../upload/'
        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)) {//把临时文件移到UPLOAD_PATH目录
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '文件类型不正确,请重新上传!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
    }
}

将content-type修改为 image/jpeg image/png image/gif 其中一个即可


0x03 Pass-03

源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array('.asp','.aspx','.php','.jsp');
        $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 = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

  • 阅读源码发现,通过自定义的deldot函数进行末尾去点处理,如果没有对末尾的点进行处理,则可以利用windows特性上传后自动去除文件后缀名的点

​ 通过源码可以发现是 检测黑名单后缀,所以这里可以通过将后缀改为.php3 .phtml

GET Apache 相关知识点

  • .htaccess

一般来说,配置文件的作用范围都是全局的,但 Apache 提供了一种很方便的、可作用于当前目录及其子目录的配置文件—— .htaccess(分布式配置文件)

要想使 .htaccess 文件生效,需要两个条件:

1. Apache的配置文件中,要写上: AllowOverride All
2. Apache要加载mod_Rewrite模块。加载该模块,需要在Apache的配置文件中写上:LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

.htaccess文件内容:

AddType application/x-httpd-php xxx

<FilesMatch "shell.jpg">
    SetHandler application/x-httpd-php
  </FilesMatch>
  • httpd.conf

如果想要apache可以将xxx的后缀当作php来执行 .php3 .phtml .xxx等后缀,需要在apache下的httpd.conf配置:

 AddType application/x-httpd-php .php3 .phtml

这时,服务器会将php3, phtml后缀的文件当成php解析。

  • mime.types

有一个名为mime.types的文件,其中记录着Apache认识的后缀

可以修改mime.types的配置 让apache 将不常用的后缀解析为php

application/x-httpd-php .php .php3 .phtml

GET windows特性知识点

测试结果


可以成功将phtml文件解析

通过重写.htacess文件 来绕过黑名单(出现点小问题)

由于黑名单没有过滤掉 .htaccess ,所以我们能够上传.htaccess 文件导致其后上传的其他文件能够解析成 php,从而拿到shell ,

问题就在这里出现了,发现上传后的 .htaccess被改写为 2019***.htaccess,所以这里并不能直接上传htaccess

发现源码使用随机数对文件名进行重写


0x04 Pass-04 (.htaccess)绕过

源码

$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.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}
?>

黑名单过滤了一堆后缀名,但唯独没有过滤 .htaccess 于是上传一个 .htaccess文件,内容:

表示将所有文件名包含jpg的,都作为php解析

<FilesMatch "jpg">
    SetHandler application/x-httpd-php
  </FilesMatch>

上传成功

再上传一个shell.jpg看能否执行

可以正常执行,说明 .htaccess 成功上传

方法二:

因为apache 由右向左👈解析的特性, 因此可以上传一个它不认识的后缀来绕过黑名单检测,比如 shell.php.aaaaaa ,这样可以绕过黑名单后缀让文件成功上传,又因为apache的特性,可以成功以php来解析


0x05 Pass-05 后缀名大小写绕过

查看源码可以知道是 根据黑名单后缀名检测,且没有对大小写进行统一转换,所有这里可以利用 pHP 等来绕过


0x06 Pass-06 空格绕过黑名单

源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess");
        $file_name = $_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
        
        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 . '文件夹不存在,请手工创建!';
    }
}

通过源码发现,和前面几关相比,这关并没有对上传的文件名进行首位去除空白字符(或者其他字符)

所以可以利用windows的特性,末尾加空格后会自动去掉空格

于是上传 'shell.php ',这个后缀名刚好可以绕过黑名单,并且上传上去空格自动去掉,可以被解析

另外的思路

看到一篇博客上的作者说,可以用 'shell.php. ' 绕过,但是我分析之后发现是不行的,因为这里的源码会将上传文件重命名,首先它会将最后一个'.' 到 末尾的字符串 认为是后缀名, '.'的前面的字符会生成随机数,所以最后上传上去的文件应该是 'xxxx. ' ,又因为windows特性,所以 文件变成 'xxxx'


0x07 Pass-07 (没有对后缀名末尾去除.)

阅读源码发现,没有删除文件名末尾的点,所以这里又是利用windows的特性

上传 shell.php.

别的思路

  • 发现没有对文件进行冲命名,所以这里可以上传 '.htaccess.' 'shell.php.xxxxxxx'

0x08 Pass-08(利用windows NTFS $DATA特性绕过)

源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $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 . '文件夹不存在,请手工创建!';
    }
}

阅读源码可以看到,基本过滤了所以可以解析的后缀名, 前几关的几个windows小特性也不能利用,对'.' 空格 d等 做了过滤

GET知识点 Windows NTFS $DATA特性

当我们访问 a.php:: $DATA时,就是请求 a.php本身的数据

::DATA之后的数据当成文件流处理,不会检测后缀名.且保持DATA之前的文件名

上传成功, 访问 123.php


0x09 Pass-09 .[空格]. 绕过黑名单

源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".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",".htaccess");
        $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.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

通过阅读源码发现,

这里上传后的文件名用的还是未完全过滤之前的,前面几关采用了拼接经过strrchr()处理过的函数名,而这一关并没有。因为源码中只进行了一次点号的清除,且我们至需要让经过过滤后的后缀名不在黑名单即可,所以我们直接采用xxx.php.[空格]. 就可以进行绕过。

测试结果

方法二

依旧采用 apache特性👈由右向左解析, 上传 'xxx.php.rrrr' ,绕过黑名单


0x10 Pass-10 双写绕过黑名单

源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = str_ireplace($deny_ext,"", $file_name);//不区分大小写替换
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH.'/'.$file_name;        
        if (move_uploaded_file($temp_file, $img_path)) {
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

阅读源码可以发现,这里将把上传的文件名中所有包含黑名单字符的,全部替换为空,所以这里可以利用双写来绕过, xx.p[php]hp,

测试结果


0x11 Pass-11 00截断

源码

$is_upload = false;
$msg = null;
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类型文件!";
    }
}

这里先参考下这个链接

浅显的意思就是在url中%00表示ascii码中的0,而ascii码中0作为保留字符,表示字符串结束,所以当url中出现%00时就会认为读取已结束

img

所以在burpsuit中作出以下修改

最后成功上传2.php文件

上传思路分析

1.这里是因为上传路径可控,所以才可以对路径进行截断

2.当前版本是5.2 存在%00截断问题 (php 5.3 不可以)

GET 到的知识点--> %00和%00(urldecode)

在网上常见用Burp将数据包中的%00进行urldecode的操作,那为什么要进行这一个操作?网上也常见直接放入%00就可以截断成功的案例,为什么呢?

  1. 首先解释为什么要进行urldecode操作呢?其原因在于上传的表单中有一个enctype的属性,并且需要enctype="multipart/form-data" (不对表单中数据进行编码),path大多数都是存放在表单中的,因此需要在数据包中进行urldecode操作使%00变成字符串结束符号。
  1. 那么为什么网上也有直接添加%00而不进行urldecode操作呢?因为path也可以存放在URL或者Cookie中,而在提交数据的时候,浏览器会对数据做一次urldecode的操作,而到服务端,会对数据进行一次urldecode的操作,因此如果path在非enctype=multipart/form-data的表单中或URL or Cookie中的时候,就可以直接写%00不需要进行URLdecode操作,让服务端对%00进行URL解码即可。
  1. %00截断的使用是在路径上

GET到的知识点--> 00截断利用条件

PHP<5.3.4,且GPC关闭

GET到的知识点--> 误区

有很多朋友喜欢在文件名中加%00进行截断,笔者认为这种方式是不对的,为什么呢?比如攻击者构造文件名:admintony.php%00a.jpg,在提取后缀名的时候遇到%00则认为字符串结束了,那么他提取到的后缀名会是.php.php后缀又不允许上传所以上传失败了(这里有必要提一句,有人可能会说在一些情况下,%00截断文件名可以成功,这种案例你试一下是不是任意文件上传,西普的00截断实验就是一个任意文件上传的上传点,既然是任意文件上传又何必用00截断绕过呢?)


0x12 Pass-12 POST类型 截断

这关和上一关基本一致,这里就不详细说了,附上测试结果


0x13 Pass-13 -15文件头类型检查

13关源码

function getReailFileType($filename){
    $file = fopen($filename, "rb");//fopen() 函数打开一个文件或 URL。
    $bin = fread($file, 2); //只读2字节 fread() 函数读取打开的文件。
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);    
    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
    $fileType = '';    
    switch($typeCode){      
        case 255216:            
            $fileType = 'jpg';
            break;
        case 13780:            
            $fileType = 'png';
            break;        
        case 7173:            
            $fileType = 'gif';
            break;
        default:            
            $fileType = 'unknown';
        }    
        return $fileType;
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_type = getReailFileType($temp_file);

    if($file_type == 'unknown'){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

通过阅读源码发现,getReailFileType() 只会读取文件的前两个字节

14关

这题一样使用getimagesize函数判断文件类型,但是这个函数也是检查文件头,是不可信的。

15关

exif_imagetype的作用是判断一个图像的类型 ,其实是读取一个图像的第一个字节并检查其签名。

方法一:

使用winhex等编辑器 打开图片文件,将webshell直接插入末尾

方法二:

直接上传webshell,使用burpsuit 拦截并添加图片的文件头

方法三:

使用cmd命令行 copy 生成图片马

方法四: php 读文件

若是服务器采用文件头验证, 则可以先上传一个 php文件读文件 readfile() file_get_contents() highlight()


0x16 Pass-16 二次渲染

源码

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    $target_path=UPLOAD_PATH.'/'.basename($filename);

    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagejpeg($im,$img_path);
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "该文件不是png格式的图片!";
                @unlink($target_path);
            }else{
                 //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagepng($im,$img_path);

                @unlink($target_path);
                $is_upload = true;               
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "该文件不是gif格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagegif($im,$img_path);

                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }
    }else{
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}

这里有个函数imagecreatefromjpeg,作用是由文件或 URL 创建一个新图象。

原理:

将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片 部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。

先知上有人针对16关专门开了一帖分析,分析得十分详细:upload-labs之Pass16

也可以参考国外大神,二次渲染与绕过方法参考连接 https://secgeek.net/bookfresh-vulnerability/

0x17 Apache 换行解析漏洞(CVE-2017-15715)

影响版本:

2.4.0~2.4.29版本

源码

<?php
if(isset($_FILES['file'])) {
    $name = basename($_POST['name']);
    $ext = pathinfo($name,PATHINFO_EXTENSION);
    if(in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
        exit('bad file');
    }
    move_uploaded_file($_FILES['file']['tmp_name'], './' . $name);
}

可见,这里用到了黑名单,如果发现后缀在黑名单中,则进行拦截。

利用CVE-2017-15715,上传一个包含换行符的文件。注意,只能是\x0A,不能是\x0D\x0A,所以我们用hex功能在1.php后面添加一个\x0A

image

然后访问/1.php%0A,即可发现已经成功getshell:

总结

研究这个漏洞的过程中遇到几个问题:

  1. 获取文件名时不能用$_FILES['file']['name'],因为他会自动把换行去掉,这一点有点鸡肋

  2. 默认的Apache配置即可利用,因为默认Apache配置就使用了``:

  3. <FilesMatch \.php$>
        SetHandler application/x-httpd-php
    </FilesMatch>
    

所以理论上,只要用正则来匹配后缀进行php解析的Apache就有这个问题。而这个做法刚好是为了解决Apache老的解析漏洞而做的,可谓非此即彼,必然存在一种解析漏洞。

原理

正则中$可以匹配行尾或一个换行符,所以可以上传一个后缀末尾包含换行符的文件,来绕过FilesMatch。当然绕过FilesMatch不一定就能被PHP解析,所以这个漏洞也需要看情况,比较鸡肋


0x18 Pass-18 条件竞争

源码

*过滤代码:*unlink()函数作用是删除文件。若成功,则返回 true,失败则返回 false。

$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_name = $_FILES['upload_file']['name'];
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . '/' . $file_name;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上传出错!';
    }
}

上面源码的代码逻辑:

  1. 首先接收上传的文件
  2. 然后判断上传的文件的文件后缀是否在白名单中
  3. 在白名单中,则将文件重命名
  4. 不在白名单则删除文件

通过不断发送请求包,直到访问shell.php成功为止


0x19 常见中间件解析漏洞

一:IIS 服务器

01:IIS<=6.0 解析漏洞

​ 起因是解析标准不一致,即Web应用程序与IIS服务器对同一个文件的文件名称(类型)理解不一致造成。

​ *** 利用方法有两种:**

 1. 畸形目录解析/xxxx.asp/xxx.jpg
 2. 分号文件解析test.asp;.jpg 
  • 第1种是因为 xxx.jpg文件在某个以.asp结尾的目录下,而被IIS当成可执行文件来解析
  • 第2中虽然以.jpg结尾,但是IIS解析时忽略了';'分号后面的.jpg,因此就当成test.asp来处理
02: IIS 7.0 & 7.5 畸形解析漏洞

​ 默认fast-cgi开启状况下,在一个文件路径后面加上/xx.php会将原来的文件解析为php文件

​ *** 利用方法有两种:**

  • 上传一张图片马 test.jpg,然后访问test.jpg/.php或test.jpg/abc.php 可以被执行。

二:nginx

01:畸形解析漏洞

默认fast-cgi开启状况下,在一个文件路径后面加上/xx.php会将原来的文件解析为php文件

和 IIS7.0&7.5畸形解析漏洞一样,上传图片马test.jpg,访问/test.jpg/.php或test.jpg/abc.php 可以被执行

02:空字节代码执行漏洞

在fast-cgi关闭的情况下,nginx版本:0.5., 0.6., 0.7- 0.7.65, 0.8 -0.8.37,nginx在图片后附加 php代码然后通过访问

 xx.jpg%00.php 

意思是指在上传了一个图片马之后,通过访问 xx.jpg%00.php 来执行php代码

03:文件名逻辑漏洞(CVE-2013-4547)

受影响的nginx版本: 0.8.41至1.4.3和1.5.7之前的1.5.x正常上传一个附加代码的图 片"test.jpg",访问时后面+"空格"+"\0"+".php",即让图片作为php文件解析

 "/test.jpg \0.php" 

三:Apache 解析漏洞

apache 是从右到左开始判断解析文件名,如果为不可识别的后缀名,就再往左判断,直到可以解析为止,如

test.php.jpegxtxt.xtie    解析成    test.php 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 文件上传漏洞 常见的漏洞分类服务器配置不当导致文件上传开源编辑器存在上传漏洞本地文件上传限制可以上传被绕过服务器端...
    二潘阅读 17,010评论 2 3
  • title: upload-labsdate: 2019-04-17 09:20:52tags:- 文件上传cat...
    Miracle778阅读 11,468评论 0 7
  • 中间件漏洞 IIS IIS6.0文件解析 xx.asp;.jpg IIS6.0目录解析 xx.asp/1.jpg ...
    l0st阅读 591评论 0 1
  • 文件上传的目的是通过上传.php文件,从而植入木马,然后通过菜刀进行连接,最终get shell 0x01 Pas...
    Hf1dw阅读 1,192评论 0 0
  • Pass-01(前端JS绕过) 方法一:前端检测。js的检测只能位于client,可以禁用js,在浏览器设置中修改...
    简言之_阅读 2,339评论 0 2