DVWA-5.4 File Upload(文件上傳)-Impossible


Impossible Level

查看源碼

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Check Anti-CSRF token----校驗token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );


    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);//返回上傳的文件名.后面的字符,即文件類型 $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
    $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];

    // Where are we going to be writing to?
    $target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
    //$target_file   = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
    $target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;//重寫文件名(在文件名前面加id再整體md5) $temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
    $temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;

    // Is it an image?
   // 校驗文件類型及大小
   // 文件名后綴必須為jpg、jpeg、png之一,
   // 並且文件大小必須小於100000B約為97.7KB,
   // 並且文件類型必須為image/jpeg或image/png
   // 並且可以使用getimagesize()返回圖像的大小和類型(注意,這個函數本身是不安全的)
if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) && ( $uploaded_size < 100000 ) && ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) && getimagesize( $uploaded_tmp ) ) { // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD) if( $uploaded_type == 'image/jpeg' ) { $img = imagecreatefromjpeg( $uploaded_tmp ); imagejpeg( $img, $temp_file, 100); } else { $img = imagecreatefrompng( $uploaded_tmp ); imagepng( $img, $temp_file, 9); } imagedestroy( $img ); // Can we move the file to the web root from the temp folder? if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { // Yes! $html .= "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; } else { // No $html .= '<pre>Your image was not uploaded.</pre>'; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file $html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } // Generate Anti-CSRF token generateSessionToken(); ?>

相關函數介紹

substr(string,start,length)

函數返回字符串的一部分

strrpos(string,find,start)

函數查找字符串在另一字符串中最后一次出現的位置(區分大小寫)

uniqid()

函數基於以微秒計的當前時間,生成一個唯一的 ID

in_get(varname)

函數返回相應選項的值

imagecreatefromjpeg ( filename )

函數返回圖片文件的圖像標識,失敗返回false

imagejpeg ( image , filename , quality)

從image圖像以filename為文件名創建一個JPEG圖像,可選參數quality,范圍從 0(最差質量,文件更小)到 100(最佳質量,文件最大)。

imagedestroy( img )

函數銷毀圖像資源

getimagesize()

函數用來獲取圖像的大小和類型

可以看到,Impossible級別的代碼對上傳文件進行了重命名(為md5值,導致%00截斷無法繞過過濾規則),加入Anti-CSRF token防護CSRF攻擊,同時對文件的內容作了嚴格的檢查,導致攻擊者無法上傳含有惡意腳本的文件。

擴展

源碼中使用了一個非常不安全的的函數:getimagesize()

getimagesize()函數會對目標文件的16進制去進行一個讀取,去讀取頭幾個字符串是不是符合圖片的要求的。

getimagesize()返回結果中有文件大小和文件類型,如果用這個函數來獲取類型,從而判斷是否是圖片的話,會存在問題。因為圖片頭可以被偽造,我們完全可以通過偽造正確的圖片頭來繞過它對圖片類型的檢查。

這就是圖片的十六進制,前幾位都是一樣的

按照這樣的邏輯,我們就可以去偽造一個假圖片,讓函數以為我們這就是圖片,達到繞過的目的。

  • 方法1 直接偽造頭部GIF89A
  • 方法2 CMD:copy /b test.png+munma.php hack.png
  • 方法3 使用GIMP(開源的圖片修改軟件),通過增加備注,寫入執行命令。

但是,即使我們可以根據上述方法繞過函數getimagesize(),我們也繞不過imagecreatefromjpeg ( filename )、imagejpeg ( image , filename , quality)、imagedestroy( img )等幾個函數對我們上傳圖片的重塑。所以,Impossible等級的代碼是比較安全的。

 

參考:

https://www.freebuf.com/articles/web/119467.html

https://blog.csdn.net/weixin_43915842/article/details/90183305


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM