网上有两个版本20关的,和21关的,如果去找其他教程参考,注意看一下是否一致,本文1-4关使用的是20关版本,linux部署,21关的前四关方法和20关一样,两个版本可能在顺序上略有不同,本文有些关卡告知windows可行linux上不可行(linux部署的是20关版本的,之前没发现版本不同),是对比两个版本的大致条件得出的(比如在21关版本的第七关在windows上部署的使用空格绕过,我是在linux上20关版本找到对应没有首尾去空的关卡验证的),还是建议练习的时候在windows使用phpstudy下载21关的练习

靶场地址:https://github.com/c0ny1/upload-labs

第一关(前端绕过)

首先我们查看源码,发现他只能上传jpg,png,和gif,但是他只是在前端校验,所以就非常好办,一切在前端的校验都是没用的

然后我们可以直接禁用掉js绕过

upload-lab-1-1.png

上传成功之后右键图片在新标签页中打开就可以看到已经成功上传了

upload-lab-1-2.png

第二关(mime型绕过)

第二关是mime型绕过,只需要上传的时候吧Content-Type改成image/png,就可以成功绕过

upload-lab-2-1.png

不过我个人喜欢把php文件的拓展名改成png,然后上传的时候把文件名改成php,这样也是可以的。然后成功上传了

upload-lab-2-2.png

第三关(简单黑名单绕过)

第三关我们可以看到他定义了一个黑名单,不允许我们上传php文件,在某些特定环境有一些后缀仍然可以被当作php文件解析,比如php2、php3、php5、phtm、phtml等,然后上传后文件名不是原来的文件名了,改成了时间戳

upload-lab-3.png

我这里是修改了配置文件,所以才可以解析phtml,php3,当然真实环境绝对不可能修改人家配置文件的

直接上传了3.php3,然后打开发现成功解析了php3

upload-lab-3-1.png

tips:如果真实环境不能右键图片查看我们的webshell的具体路径,我们可以抓取返回包查看或者f12-->点击网络查看

upload-lab-3-2.png

第四关(.htaccess绕过)

我们可以看到限制了一堆的后缀,不过我们这个靶场使用的是apache,它没有限制.httaccess后缀,我们可以上传这个文件来让apache把我们上传的任意文件解析成php。

upload-lab-4.png

新建一个.htaccess文件,下面的两种文件内容任选一个即可

#这个是只把4.png解析成php
<FilesMatch "4.png">
SetHandler application/x-httpd-php
</FilesMatch>
#下面这个是把所有的png文件解析成php
AddType application/x-httpd-php .png

这里我们先上传.htaccess,在上传4.png,发现成功解析

upload-lab-4-1.png

第五关(.user.ini绕过)

第五关我们阅读源码可以发现它这次不让我们上传.htaccess了,这一关需要我们上传.user.ini(和.htaccess类似,可以改变php的配置),但是它要求Server API是FastCGI模式才可以,我们这个靶场运行在linux上,Server API模式是Apache 2.0 Handler,无法使用这个方式,所以我就在windows上又使用phpstudy搭建了这个环境来过这一关,推荐使用nginx+php,nginx默认就是FastCGI

tips:linux也有FastCGI模式,这里我只是图方便,就使用phpstudy了,现在FastCGI更适用生产环境

uploads-lab-5-2.png

新建一个.user.ini的文件,内容是auto_prepend_file=5.png

5.png里内容如下

uploads-lab-5-1.png

(5.png是上传的文件名称,我们也可以改成shell.gif,5.jpg之类的,只要扩展名是图片类型不在黑名单里面就可以,这句话是让目录下的所有php文件都包含5.png里面的内容,也就是说执行别的php文件也把这个5.png里面的内容执行了)

上传的目录必须有一个php后缀的文件才可以,靶场默认上传目录里面有个readme.php,然后先上传.user.ini,再上传5.png,最后访问的是那个php文件,也就是readme.php,

uploads-lab-5-3.png

第六关(大小写绕过)

这一关相比前面几关它少了转换小写,这一关还是只能在windows上完成,windows上6.PHp和6.php会被系统都当作php文件,被当作同一个文件,而linux上就没有这个说法,20关那个版本第六关就有转换成小写,所以用不了这个方法。

uploads-lab-6.png

然后我们直接把6.png在抓包的时候改成6.PHp就可以

uploads-lab-6-1-JXmY.png

然后成功上传

uploads-lab-6-2.png

第七关 (空格绕过)

第七关和第六关相比,它这次没有首位去空,我们可以在把7.png改成7.png(空格)绕过

uploads-lab-7.png
uploads-lab-7-1.png

然后成功显示phpinfo,linux上用这个方法,直接就not found了。

uploads-lab-7-2.png

第八关(扩展名加点绕过)

这一关和上一关相比,它没有去除文件名后面的点 .

uploads-lab-8.png

我们可以上传8.png然后抓包在后面加一个 . ,然后成功绕过,不过这个方法在linux上试了下还是没成功解析。

uploads-lab-8-1.png

第九关(::$DATA绕过)

这一关少了删除::$DATA的代码,::$DATA 是 NTFS 文件系统中的一种数据流标识

uploads-lab-9.png

在windows环境中,读写9.php和9.php::$DATA的结果是一样的,我们把9.png抓包改成9.php::$DATA即可

uploads-lab-9-1as.png

不过要注意我们访问的时候后面不能带::$DATA,直接访问xxx.php就可以

uploads-lab-9-2.png

第十关(. .绕过)

我们发现这一关没法使用上一关办法了,前面的关卡的限制它基本都加上了

uploads-lab-10.png

但是我们可以把10.php改成10.php. .来绕过,这样它去掉末尾的点,我们访问的还是xxx.php.

uploads-lab-10-1.png

然后和第八关效果一样,成功绕过

uploads-lab-10-2.png

第十一关(双写绕过)

这一关它对应的是20关版本的第十关,linux也可以同样使用这个方式绕过。

这一关的核心就是这一段代码$file_name = str_ireplace($deny_ext,"", $file_name);他的作用就是检测到上传的文件的拓展名是属于上面黑名单中的任意一个就会把违规的部分删除,而且不区分大小写,比如上传11.PHP就会变成11. 如果上传的是11.php,也会变成11.但是它只会删除一次

uploads-lab-11.png

那我们如果上传的文件是11.php,抓包改成11.pphphp,那实际保存的就是11.php

uploads-lab-11-2.png

可以看到成功解析

uploads-lab-11-1.png

第十二关(%00截断)

这一关真实环境应该很少见到基本可以pass,它需要php的版本低于5.3.29,我在5.3.29-nts上试过没成功,切换成5.2.17最后才成功,而且无论哪个版本都需要关闭php的magic_quotes_gpc模块,它在linux上应该也是可以使用(我没有测试,比较麻烦)

uploads-lab-12.png

当path路径中出现ASCII为0(0x00)的空字符时,将截断其后面的字符不执行,他的url编码就是%00

我们抓包改一下,在savepath中把在/upload后面加上12.php%00即可

uploads-lab-12-1.png

最后我们直接访问12.php就可以

uploads-lab-12-2.png

第十三关(POST型%00截断)

这一关其实和上一关方法一样,只不过上一关是GET型,这一关是POST型

但是post不会像get一样自动编码,我们需要改hex,这次我使用burp改包

uploads-lab-13-1.png

然后把2b改成00,然后发包

uploads-lab-13-2.png

最后访问13.php成功

uploads-lab-13-3.png

第十四关(文件头绕过)

接下来几关就需要配合文件包含漏洞才可以实现,靶场也提示我们了,有个include.php我们需要利用这个php文件解析图片,linux也可以用

uploads-lab-14.png

这一关我们需要准备一个图片命名为1.png,然后把webshell命名成14.php

这里我系统是windows,打开cmd(注意不是powershell)进入两个文件的路径输入copy /b 1.png + 15.php image_shell.png可以看到生成了一个image_shell.png的图马

upload-lab-14.png

查看image_shell.png的HEX可以看到phpinfo在图片的末尾

upload-lab-14-1.png

直接把这张图片上传,然后使用include.php拼接路径解析即可

tips:include.php在靶场根目录,而上传的图片在靶场根目录下的upload文件夹里面拼接时需要注意路径

upload-lab-14-1-2.png

第十五关(图马绕过)

其实我第14关的方法用的是第15关的,但是我觉得第14关有点麻烦就直接使用第十五关的方法了

第15关用了一个getimagesize函数获取图片的宽和高等信息,我们第十四关把图片和恶意语句融合在了一起,载体是一个真正的图片,而不是伪造的文件头,所以绕过效果更好一些,不过我看别的博客也有修改文件头绕过的

upload-lab-15.png

也是成功绕过

upload-lab-15-1.png

第十六关

这一关不知道为啥,我windows使用的21关的版本上传之后出了点问题,上传直接就一片空了

upload-lab-16-1.png

这一关对应的是20关版本的第15关,使用的exif_imagetype来检测图片,所以我就在linux上的20关上传了14关制作的那个图马。也是顺利通过

upload-lab-16.png

第十七关(二次渲染绕过)

这是第十七关的源码我们可以看到

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

Copyright @ 2018 ~ 2026 by c0ny1

这一关的主要难度就是二次渲染,我们如果还是使用第14关那样的图马,服务端会把图片二次渲染在保存,这样我们的恶意代码大概率会失效,所以我们需要制作进阶版本的图马

首先我们现在网上找一个比较简单的gif,然后保存成1.gif,然后上传到服务端在把二次渲染后的图片保存对比,在原gif没有变化的部分插入<?php phpinfo(); ?>也可以是其他恶意代码,然后保存上传

upload-lab-17.png

可以发现成功解析,windows和linux均成功

upload-lab-17-1.png

第十八关(条件竞争)

这一关对应的是20关版本的第17关,需要使用条件竞争,这段代码就干了一个事情,先把上传的文件xxx.png或者xxx.php移动upload文件夹里面,上传的文件是xxx.png在白名单里面,然后重命名成一个随机文件名,上传的是xxx.php或者其他不是png,gif,jpg的文件就直接删除

upload-lab-18-2.png

所以我们要新建一个18.php,然后在它把18.php移动到uplaod文件夹里还没校验时我们的18.php已经新建了一个shell.php

<?php
phpinfo();
@file_put_contents(__DIR__ . '/shell.php', '<?php @eval($_POST["cmd"]);?>');
?>

bp发送到intruder,然后一直发包,我们可以写一个脚本,如果18.php成功被执行,我们就可以链接shell.php执行命令

upload-lab-18-1.png

但是我不想费功夫,就浏览器一直刷新访问18.php,访问成功了,就会出现phpinfo界面

upload-lab-18.png

然后我们连接shell.php,发现成功执行我们的命令了

upload-lab-18-3as.png

第十九关

这一关对应的是20关版本的第18关,但是我发现21版本这个路径有点问题,(20关版本的也有问题)最后上传文件本来是upload目录下19.php.7z结果变成upload19.php.7z,需要修改Pass-19/myupload.php

function setDir( $dir ){
    
    if( !is_writable( $dir ) ){
      return "DIRECTORY_FAILURE";
    } else { 
      $this->cls_upload_dir = $dir.'/';
      return 1;
    }
  }

主要使用的是apache服务器的解析漏洞,apache解析文件的时候从后向前解析,比如当上传xxx.php.7z的时候先解析png,解析不了png再解析前面的php,但是我实际操作遇到一个问题,访问xxx.php.7z的时候它要么是直接下载,要么就是直接变成纯文本,根本不执行,查了不少资料说Server API: Apache 2.0 Handler,需要修改apache,只要含有php就解析,当然也有使用文件包含绕过的,但是这一关没有写文件包含那个提示,本意应该不是要我们使用文件包含的。

upload-lab-19.png

我们需要修改apache配置文件,然后重启服务,还是使用条件竞争

upload-lab-19-1.png

然后不断访问,出现phpinfo

upload-lab-19-2.png

然后连接shell.php即可

第二十关

这一关没什么限制,好像使用.user.ini和%00截断也可以

upload-lab-20.png

我在windows上使用了20.php.(扩展名加点绕过)也可以

upload-lab-20-1.png
upload-lab-20-2.png

然后对应的应该是20关版本的第19关,我看了别的文章把保存名称改成19.php/.也成功绕过

upload-lab-20-3.png

第二十一关

这一关对应的是20关版本的最后一关,核心是 $file_name = reset($file) . '.' . $file[count($file) - 1];

upload-lab-21.png

首先有save_name这个数组,原本是0,1,2,一共三个元素,然后咱们只穿传0,2,不传1,然后它直接数0和2,2个元素,然后2-1=1,拼接上第一个元素,我们传入数组第0个元素是21.php,跳过第一个,传入第二个元素是png,但是1我们没传,所以就是空的,变成了21.php+.+空

upload-lab-21-2.png

然后访问21.php.,发现成功,通过windows的文件资源管理器查看发现实际保存的是21.php,不过我们访问得是21.php

但是linux上失败了,linux好像解析不了21.php.只能解析21.php

这里出现phpinfo就可以了

upload-lab-21-1.png