Lemon's blog

文件上传漏洞——upload-labs(1-10)

Record my learning process of Upload-labs.

字数统计: 3.2k阅读时长: 14 min
2019/08/01 Share

前言:文件上传漏洞有很多种绕过技巧,这次就通过upload-labs进行学习

第一关

在这里插入图片描述
上传有限制,只让上传JPEG或PNG格式的图片,就先尝试一下抓包修改上传格式的方法看看是否可行

先将一句话木马PHP文件后缀名改为PNG格式,上传拦截抓包

在这里插入图片描述
拦截请求包后,将1.png改为1.php,再发包,用菜刀进行连接,在此之前需要知道上传的文件上传到哪个目录下,查看源码查出图片上传路径
在这里插入图片描述
路径也知道了,就用菜刀进行连接
在这里插入图片描述
连接成功
在这里插入图片描述
观察一下第一关的源码

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

通过观察只是限制了在上传时的类型,所以中途拦截抓包改格式的方法是完成可行的。

方法改包绕过上传

第二关

做第二关时发现沿用第一关的方法也是可行的,不过应该不会再考同一个点了

就查看一下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}

发现这一段代码判断content-type,那就可以通过修改content-type进行绕过:

1
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif'))

抓包将application/octet-stream修改为image/pngimage/gif都可以在这里插入图片描述
连接成功
在这里插入图片描述
方法修改content-type进行绕过

第三关

上传PHP文件时,发现有这样的提示
在这里插入图片描述
应该是源码中限制了这些文件的后缀名,查看大师傅们的博客发现还可以用

1
phtml,php3,php4, php5, pht

这些后缀名进行绕过,就来尝试一下,发现连接不上去,查了一下才知道原来前提是apachehttpd.conf中有如下配置代码

1
AddType application/x-httpd-php .php .phtml .phps .php5 .pht

在这里插入图片描述
我这里使用的是phpstudy+windows,即使添加了也不管用,查了大师傅的博客才知道是由于配置原因是解析不了php5等等这些后缀的,所以复现不了,可以在虚拟机中复现这关。
不过也知道了这一关是采用拓展名绕过
查看一下源码

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
$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']);//trim() 函数移除字符串两侧的空白字符或其他预定义字符
$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); //收尾去空

if(!in_array($file_ext, $deny_ext)) {//in_array() 函数搜索数组中是否存在指定的值
$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 . '文件夹不存在,请手工创建!';
}
}

观察源码就会发现这里确实采用黑名单来限制

方法拓展名绕过

第四关

没什么思路,查看一下提示
在这里插入图片描述
发现基本上将所有非法的脚本后缀都禁用了,还是黑名单限制,查看一下源码

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
$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");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

果然是将所有非法的脚本后缀都禁用了,但是没有禁用.htaccess,先来了解一下htaccess文件的作用,.htaccess的基本作用及相关语法介绍

在这里使用htaccess文件目的是为了将所有文件都当成php文件来解析
创建一个.htaccess文件

1
SetHandler application/x-httpd-php

在文件中写入该段代码,上传时将文件名去掉,只要后缀名
在这里插入图片描述
上传后,上传图片马,由于连接菜刀麻烦,这里就修改一句话语句为

1
<?php phpinfo(); ?>

上传图片马成功后,进行查看
在这里插入图片描述
解析成功

方法.htaccess文件进行绕过

第五关

上传.htaccess文件,发现该后缀名也被加入黑名单了
在这里插入图片描述
查看源码

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",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

还是采用了黑名单来进行限制,不过与前几关先比较会发现少了这一行代码

1
$file_ext = trim($file_ext); //首尾去空

那么就可以采用后缀名中加空绕过
在这里插入图片描述
在后缀名中加入空格.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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_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
$file_name = deldot($file_name);//删除文件名末尾的点

所以可以采用后缀名加.的方式绕过,在这之前,先来了解一下对Windows系统文件命名规则的特殊利用

1
2
3
4
5
6
7
8
9
10
11
	shell.php.                    ———-文件名后加点‘.’

shell.php(空格) ———-文件名后加括号空格

shell.php:1.jpg ———-文件名后加冒号’:’

shell.php::$DATA ———-文件名后加NTFS ADS特性::$DATA

shell.php::$DATA…… ———-文件名后::$DATA……

会被windows系统自动去掉不符合规则符号后面的内容。

windows系统文件命名规则的特殊利用

因此我们可以先抓包然后在后缀名后加上.,文件上传时由于不符合windows文件命名规则而将.去掉,从而将.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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = 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
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

没有去除字符串::$DATA,根据Windows系统文件命名规则进行抓包修改即可

1
shell.php::$DATA             ———-文件名后加NTFS ADS特性::$DATA

原理和第七关类似
在这里插入图片描述
访问成功
在这里插入图片描述
绕过方法后缀名加::$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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

观察看与前几关的代码有什么不同的地方,发现

1
$img_path = UPLOAD_PATH.'/'.$file_name;

路径拼接的是处理后的文件名$file_name,而不是$file_ext,也就是说最后保存文件的时候没有重命名而使用的原始的文件名,那相当于$file_name只经过那两段代码的过滤

1
2
$file_name = trim($_FILES['upload_file']['name']);//移除字符串两侧的空白字符
$file_name = deldot($file_name);//删除文件名末尾的点

那就可以采用后缀名(点+空格+点)的方法来绕过
在这里插入图片描述
上传时,代码会先将末尾的.去除,剩余.+空格,利用Windows系统文件命名规则,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
$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 = 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 . '文件夹不存在,请手工创建!';
}
}
#str_ireplace() 函数替换字符串中的一些字符(不区分大小写)
/*str_ireplace(find,replace,string,count)
find 必需。规定要查找的值。
replace 必需。规定替换 find 中的值的值。
string 必需。规定被搜索的字符串。*/

这一段代码将后缀名全部替换成了空

1
$file_name = str_ireplace($deny_ext,"", $file_name);

之前学习XSS时也碰到这种情况,可以采用双写进行绕过
在这里插入图片描述
过滤了php,前后再拼接成PHP
访问成功:
在这里插入图片描述

绕过方法双写绕过

总结:通过这十关又学到了很多文件上传的技巧,下次继续学习后十关,这次先学习到这里。

CATALOG
  1. 1. 第一关
  2. 2. 第二关
  3. 3. 第三关
  4. 4. 第四关
  5. 5. 第五关
  6. 6. 第六关
  7. 7. 第七关
  8. 8. 第八关
  9. 9. 第九关
  10. 10. 第十关