文件上传漏洞是一种安全问题,它发生于程序开发者未对用户上传的文件进行充分的验证和过滤,从而让用户有可能上传能做到超越其原本权限的执行脚本文件到服务器。常见的情形包括头像上传、图片上传,以及在 Office 自动化软件中的文件上传,或者媒体文件的上传等场景。如果在处理用户上传的文件时,程序的过滤措施不当或者不严格,可能被恶意用户利用,他们可以上传含有恶意脚本的文件到服务器。一旦这些文件被服务器执行,这些恶意用户就可能获得服务器的访问权限,甚至可能对服务器造成更严重的损害。

# 危害

  1. 未授权访问:通过这个漏洞,攻击者可以上传恶意脚本或程序到服务器,并使其执行,从而控制或访问不该访问的信息或资源,实现越权。
  2. 代码执行:如果服务器执行了恶意用户上传的脚本,那么可以执行任何攻击者编写的命令,包括删除文件、窃取数据、安装恶意软件等。
  3. 数据泄露:攻击者可以通过上传并执行恶意脚本来窃取存储在服务器或网络中的敏感数据。
  4. 拒绝服务攻击:攻击者可以上传一些占用大量计算能力的文件,从而使服务器过载,影响正常的服务提供。
  5. 服务器篡改:攻击者上传的文件可能会修改服务器的设置,可能会改变访问控制、篡改网站内容,甚至插入恶意广告等。
  6. 创建攻击的跳板:被利用的服务器可以作为对其他系统和网络发起进一步攻击的跳板,让攻击者能够进一步深入到网络内部。

因此,对于任何允许用户上传文件的系统或网站来说,文件上传应严格过滤和控制,以防止这类攻击和漏洞产生。

# 类型

# 1. 直接文件上传

攻击者可以无约束地上传文件,包括可执行的脚本文件。这将可能导致远程代码执行(RCE)漏洞,让攻击者控制服务器。

# 2. 有条件的上传漏洞

  • 前端认证或文件检测可以被绕过: 例如,如果开发者仅通过前端 JS 代码检查文件类型,攻击者可以修改 POST 请求绕过此类验证。对于服务器端的文件类型验证,如果仅根据文件扩展名或内容头信息(MIME 类型)来检验,攻击者可以上传一个假设的图像文件,其实是一个可执行的 PHP 脚本。
  • 权限验证不足: 如果任何人,包括匿名者,都可以上传文件,那么攻击者可以轻易地上传一个恶意文件。
  • 上传逻辑有问题: 某些情况下,开发者可能没有全面地考虑到攻击者可以利用的点,例如,允许用户控制上传文件的路径和文件名;考虑不足的黑名单检验等。
  • 利用中间件或系统特性上传可解析的脚本文件: 例如 Apache 服务器可以解析.htaccess 文件,如果此文件被恶意改写,就可能导致严重的安全漏洞。

# 防御

防御文件上传漏洞,主要是对上传的文件进行严格监管。这包括:

  1. 始终在服务器端进行文件类型、大小、内容等验证。
  2. 对文件名做合适的处理,如自动生成随机的文件名,并限制有效文件拓展名。
  3. 应用最小权限原则,仅授权必要的权限给特定的用户角色。
  4. 把上传的文件存储在无法直接访问或无法执行脚本的位置,或者存储在数据库中。
  5. 使用如 Web Application Firewall (WAF) 等工具,自动防御已知的上传攻击。
  6. 您还可以进行日常的安全审计和定时的代码审查,定期更新并修复已知的安全漏洞。

可执行脚本常见的后缀名

asp asa cdx

cer php aspx ashx jsp php3 php.a shtml phtml 有些网站会对 asp 或者 php 进行过滤转成空可用这些后缀名。 aspasp asaspp phpphp

任意文件上传代码分析 直接获取文件名 把上传的临时文件移动到 hackable/uploads 目录

image.png

码并没有对用户上传的文件做过滤导致任意文件都可以上传

# 靶场 upload-labs

# 第一关 js 前端验证绕过

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;
    }
}
上述代码只允许上传 .jpg .png .gif后缀的文件,检测用户上传的文件不合法则不允许上传

绕过方法

burp 抓包后缀名

先上传 png 后缀的恶意文件

img

然后绕过了前端检测后

抓包在 burp 中把后缀改为 php

img

img

img

因为抓到包之前前端已经检测通过,而后端接收时又没有进行检测所以上传成功,蚁剑连接

# 第二关 MIME 检查绕过

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

抓包后修改红框里的内容,Content-Type 就是文件类型

更改为下面其中一种图片格式就行

image/jpeg、image/png、image/gif

img

img

蚂蚁剑连接成功

# 第三关

黑名单绕过

在 iis 里 asp 禁止上传了,可以上传 asa cer cdx 这些后缀,如在网站里允许.net 执行 可以上传 ashx 代 替 aspx。如果网站可以执行这些脚本,通过上传后门即可获取 webshell。 在不同的中间件中有特殊的情况,如果在 apache 可以开启 application/x-httpd-php 在 AddType application/x-httpd-php .php .phtml .php3 后缀名为 phtml 、php3 均被解析成 php 有的 apache 版本默认就会开启。 上传目标中间件可支持的环境的语言脚本即可,如.phtml、php3。

注意 php nts 版本不生效 AddType application/x-httpd-php .php .phtml .php3

上传 php5 连接成功

img

# 第四关 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",".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 . '文件夹不存在,请手工创建!';
    }
}
拉黑了更多后缀

上面把能被解析的都给拉黑了,这时如果没有禁止上传.htaccess 后缀文件

上传模块,黑名单过滤了所有的能执行的后缀名,如果允许上传.htaccess。htaccess 文件的作用是 可以帮我们实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定 IP 地址的用户、只允许特定 IP 地址的用户、禁止目录列表,以及使用其他文件作为 index 文件等一些功能。

在 htaccess 里写入 SetHandler application/x-httpd-php 则可以文件重写成 php 文件。要 htaccess 的规则生效 则需要在 apache 开启 rewrite 重写模块,因为 apache 是多数都开启这个模块,所以规则一般都生效。

创建一个文件里面的内容是

<FilesMatch “jpg”>

SetHandler application/x-httpd-php

就可以把 jpg 当 php 文件解析

img

# 第五关 大小写绕过

这关代码没有对上传的文件后缀进行小写转换

<?php
include '../config.php';
include '../common.php';
include '../head.php';
include '../menu.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 = 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 . '文件夹不存在,请手工创建!';
    }
}
?>

.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”);

观察拦截的后缀 可以使用 .Php 后缀绕过

img

# 第六关 空格绕过

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

这关没有对末尾去空再根据 win 特性上传到服务器后自动清除后缀的空格

即可连接

img

img

# 第七关。绕过

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

这关没有对末尾去除点的处理 win 特性后缀末尾不能加点上传到服务器后自动删除就可以绕过了

img

img

# 第八关 NTFS 交换数据流::$DATA 绕过上传

如果后缀名没有对::$DATA 进行判断,利用 windows 系统 NTFS 特征可以绕过上传。

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

img

img

# 第九关

利用 windows 环境的叠加特征绕过上传

在 windwos 中如果上传文件名 webshell.php:.jpg 的时候,会在目录下生产空白的文件名 webshell.php 再利用 php 和 windows 环境的叠加属性,

以下符号在正则匹配时相等

双引号 " 等于 点号.

大于符号 > 等于 问号?

小于符号 < 等于 星号 *

文件名.<或文件名.<<< 或文件名.>>> 或文件名.>>< 空文件名

首先抓包上传 a.php:.jpg 上传会在目录里生成 a.php 空白文件,接着再次提交把 a.php 写成 a.>>>

img

img

现在里面是空的

img

现在就写到了 a.php 里

img

# 第十关 双写绕过

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

匹配到违法后缀会把它删除可以写两次后缀名绕过

img

上传成功

img

# 白名单

# 第十一关

save_path 是一个可控的变量,后面还有一个后缀名需要绕过,这个时候需要使用 %00 截断,不过有使用条件

php版本小于5.3.4
php的magic_quotes_gpc为OFF状态
$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类型文件!";
    }
}

img

img

# 第十二关

和第十一关对比,发现接受值变成了 post,那么思路就和第十一关一样,不过 post 方式不会自行解码,所以要对 %00 进行 urldecode 编码

$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 = $_POST['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类型文件!";
    }
}

img

img

上传成功

# 第十三关 配合文件包含漏洞

图片码

copy 13.jpg /b + 13.php /a a.jpg

把 13.jpg 跟 13.php 组合成 a.jpg

上传后文件包含漏洞会把所有文件当 php 解析

地址加上 file

img

img

# [第十四关]

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

getimagesize 是获取图片的大小,如果头文件不是图片会报错直接可以用图片马绕过检测。所以跟上面步骤一样

# 第十五关

查看源码 exif_imagetype () 读取一个图像的第一个字节并检查其签名需要打开 php_exif

function isImage($filename){
    // 需要开启 php_exif 模块
    $image_type = exif_imagetype($filename);
    switch ($image_type) {
        case IMAGETYPE_GIF:
            return "gif";
            break;
        case IMAGETYPE_JPEG:
            return "jpg";
            break;
        case IMAGETYPE_PNG:
            return "png";
            break;    
        default:
            return false;
            break;
    }
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

跟之前 13,14 步骤一样

img

# 第十六关 绕过图片二次渲染上传

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

只允许上传 JPG PNG gif 在源码中使用 imagecreatefromgif 函数对图片进行二次生成。生成的图片保存在,upload 目录下。

二次渲染将图片马里面的 php 代码删了。接下来把原图和修改后的图片进行比较,看哪里没有被渲染,在这里插入 php 代码。

制作图片马

将原图片上传,下载渲染后的图片进行对比,找相同处,覆盖字符串,填写一句话后门,或者恶意指令。

img

找到相同的地方在这个地方写一句话就可以了

img

img

# 第十七关 条件竞争

如果上传的符合它的白名单,那就进行重命名,如果不符合,直接删除

在它删除之前就访问这个文件,他就不会删除了。

$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 = '上传出错!';
    }
}

img

burp 一直上传

img

用浏览器一直访问这个地址访问到了因为已经打开了图片就不会删除了

# 第十八关 配合文件包含

检测了后缀其他跟 17 关步骤一样

img

# 第十九关 文件名控可控攻击方法

两种 %00 截断 和 /.

比前面的多了一个保存名称 没有对上传的文件做判断 move_uploaded_file () 这样一个函数,特性是会忽略到文件末尾的 /.
直接上传 shell.png,抓包,在末尾加上 /.

img

img

img

%00 也可以但是要注意 php 版本

# upload-labs 关卡技巧

upload-labs 是一个使用 php 语言编写的,专门收集渗透测试过程中遇到的各种上传漏洞的靶场。目前一共 19 关,每一关都包含着不同上传方式。具体的每一关的解题方法如下:

Pass-01: 通过禁用 js 插件或修改网页源代码绕过前端检查限制,可以成功上传文件。

Pass-02: 通过修改上传文件的 Content-Type 字段为允许上传的文件类型之一,绕过后端的 MIME 验证,可以成功上传文件。

Pass-03: 通过上传一个以被绕过的黑名单后缀名为文件的方式,绕过后端黑名单验证,可以成功上传文件。

Pass-04: 通过上传一个.htaccess 文件并设置对应的解析规则,可以将上传的文件解析为 php 文件,从而成功上传代码。

Pass-05: 通过上传一个大小写混合的后缀名为文件,绕过后端大小写敏感的验证,可以成功上传文件。

Pass-06: 通过在上传文件的末尾加上空格,绕过后端删除文件末尾空格的验证,可以成功上传文件。

Pass-07: 通过在上传文件的末尾加上一个点,绕过后端删除文件末尾点的验证,可以成功上传文件。

Pass-08: 通过在上传文件的末尾加上::$DATA,绕过后端删除文件字符串的验证,可以成功上传恶意文件。

Pass-09: 通过修改文件后缀名为.php 和.,绕过后端对文件名的验证,可以成功上传文件。

Pass-10: 通过修改文件后缀名为.pphphp,绕过后端的黑名单验证,可以成功上传文件。

Pass-11: 通过在上传的文件被删除之前,快速访问该文件,绕过后端的删除操作,可以成功上传文件。

Pass-12: 通过对上传文件进行 URL 编码,绕过后端对特殊字符的处理,可以成功上传文件。

Pass-13: 通过在图片文件中添加 php 代码,实现文件包含漏洞,成功上传 webshell。

Pass-14: 通过在图片文件头部添加图片文件的文件头,实现文件包含漏洞,成功上传 webshell。

Pass-15: 通过启用 php_exif 扩展,并在图片文件中添加 php 代码,成功上传 webshell。

Pass-16: 通过在图片动态渲染的过程中,找到不被渲染的地方,添加 php 代码,成功上传 webshell。

Pass-17: 通过在文件上传的过程中,使用条件竞争,绕过后端的删除操作,可以成功上传文件。

Pass-18: 通过修改 18 关的 myupload.php 文件并重启靶场,绕过后端的文件保存路径验证,可以成功上传文件。

Pass-19: 通过在上传的文件名末尾添加 /,绕过后端对文件后缀名的验证,可以成功上传文件。