uploads-lab文件上传靶场闯关记录

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的空字符串,是有地址但是里面的内容是空的。

绕过方法


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 . '文件夹不存在,请手工创建!';
    }
}

​ 通过源码可以发现是 检测黑名单后缀,所以这里可以通过将后缀改为.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

15223122857686.jpg

然后访问/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 
posted @ 2020-03-06 23:41  dxmbrsl  阅读(1024)  评论(0编辑  收藏  举报