1. 程式人生 > 其它 >upload-labs(17-20)

upload-labs(17-20)

17 條件競爭上傳

黑盒測試

招數都用了,上傳失敗,看看提示:

原始碼分析

$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 = '上傳出錯!';
    }
}
  1. 白名單上傳,獲取副檔名
  2. 先將臨時檔案上傳到路徑
  3. 判斷副檔名是否在白名單,如果不在就刪除上傳的臨時檔案
  4. 如果在白名單,就重新命名上傳的臨時檔名

看完原始碼,難道條件競爭上傳?

白盒測試

條件競爭怎麼實現呢?

就是不斷上傳,把後端給累死,後端就會疏忽,然後上傳成功

首先BP攔截資料包

然後發去爆破

整個指令碼,輸出1-99998作為字典

filename = '123.txt'
with open(filename, 'w') as file_object:
    for i in range(1,99999):
        a=str(i)
        file_object.write(a+'\n')

在此處攻擊

可以看到上傳目錄1.php在忽隱忽現

可以連線

但是一直刪,導致連線不穩定

字典跑完了,1.php最後又被刪光了,連線就又斷開了

把一句話改成phpinfo()顯示資訊也不錯

18 競爭上傳,apache解析漏洞

程式碼審計

原始碼分析

//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" );

......
......
......  

  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" );
  }
......
......
...... 
};

myupload.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.
  var $cls_rename_file = 1;         // Set to 1 to rename file after upload.
  var $cls_file_rename_to = '';     // New name for the file after upload.
  var $cls_verbal = 0;              // Set to 1 to return an a string instead of an error code.

  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;
  }

  function isUploadedFile(){
    
    if( is_uploaded_file( $this->cls_tmp_filename ) != true ){
      return "IS_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }
  }

  function setDir( $dir ){
    
    if( !is_writable( $dir ) ){
      return "DIRECTORY_FAILURE";
    } else { 
      $this->cls_upload_dir = $dir;
      return 1;
    }
  }


  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;
    }
  }


  function checkSize(){

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


  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;
    }

  }


  function checkFileExists(){
    
    if( file_exists( $this->cls_upload_dir . $this->cls_filename ) ){
      return "FILE_EXISTS_FAILURE";
    } else {
      return 1;
    }
  }


  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;
    }
  }
  
  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" );
  
  }

  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
?>

可以看到程式碼中是先檢查擴充套件,再上傳,利用條件競爭上傳

然後移動檔案,最後改名

白盒測試

改成1.php.rar,利用apache解析漏洞上傳

可以看到1.php.rar上傳成功,沒有改名

但是apache好像能解析rar

改成1.php.7z試試,成功解析

19 截斷,windows特性-黑名單

黑盒測試

上傳1.php

儲存名稱改為upload-19.php空格

就上傳成功了

訪問成功

沒明白這道題想要幹什麼,看看提示,難道它不是想讓用這種方法?

原始碼分析

$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 . '資料夾不存在,請手工建立!';
    }
}

1.POST過來的名字作為$file_name

2.用 pathinfo函式獲取$file_name的副檔名$file-ext

3.如果副檔名不在黑名單中,就上傳檔案

簡單的說,就是根據提交的savename進行判斷,只要savename的副檔名不再黑名單,就行了

因此利用windows系統特性進行繞過

upload-labs pass-5-利用系統特性繞過 - AlucardLink - 部落格園 (cnblogs.com)

搜了一下,看到還有人說利用%00截斷?

  • 原始碼中move_uploaded_file($temp_file, $img_path)來上傳檔案

  • $img_path = UPLOAD_PATH . '/' .$file_name;

  • 利用$file_name,命名成例如1.php%00.jpg,然後上傳時就截斷了

  • 注意post上傳時,需要手動把%00url解碼

    上傳後文件命名為了upload-119.php%EF%BF%BD.jpg,但是由於截斷,實際上檔案是upload-119.php

    進行訪問,截斷成功

20 程式碼審計繞過

程式碼審計

原始碼分析

$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 = "請選擇要上傳的檔案!";
}

1.Content-Type來判斷檔案是否在白名單,如果不在,禁止上傳

2.如果post過來的save_name為空,就上傳的檔名賦值給$file作為檔名,否則save_name賦值給$file

3.如果$file不是陣列,就用explode() 函式.作為分割線,分為陣列,並全部轉小寫

4.將$file陣列的最後一個值作為副檔名賦值給$ext

5.如果$ext不在白名單就禁止上傳

6.reset()獲取$file陣列的第一個值,和$file陣列的最後一個值用.連線,賦值給$file_name

7.檔案上傳到UPLOAD_PATH/$file_name

關鍵部分在於判斷$file是不是陣列,不是陣列就被拆分的七零八落,導致無法繞過

如果是陣列,我們自定義陣列內容,繞過就輕而易舉了

白盒測試

1.修改content-type繞過$_FILE[][type]檢測

2.將save_name改為陣列,只有0和4

3.save_name[0]為1.php,便於後面拼接

4.save_name[4]為jpg

  • 1.為了過end($file)的白名單檢測
  • 2.count計算為2,但是save_name[1]不存在,因此$file[count($file) - 1]為空

5.拼接後為1.php.,由於windows特性,變成了儲存後變成了1.php

連線成功