File Upload(文件上傳)
File Upload(文件上傳),由於對上傳文件的內、類型沒有做嚴格的過濾、檢查,使得攻擊者可以通過上傳木馬文件獲取服務器的webshell文件。
File Upload主題:

Low
源碼解析
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? //文件的目標路徑hackable/uploads/,也就是文件上傳的位置 $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; // basename(path,suffix) //函數返回路徑中的文件名部分,如果可選參數suffix為空,則返回的文件名包含后綴名,反之不包含后綴名。 $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // Can we move the file to the upload folder? //移動用戶上傳文件至目標路徑 if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } ?>
漏洞復現
從源碼中可以看到對上傳文件的類型、內容沒有做任何的過濾與檢查,同時告訴了我們文件上傳的路徑,存在明顯的文件上傳漏洞。
(1)上傳一個phpinfo.php進行測試

當我們上傳成功后,會提示我們成功上傳,並且返回一個上傳文件的路徑
(2)打開我們所上傳的文件

Medium
源碼解析
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // Is it an image? //文件類型必須是image/jpeg 或者 image/png,大小不能超過100000B(約為97.6KB) if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?>
對文件上傳的類型做了限制,要求必須是image/jpeg 或者 image/png 類型的。
漏洞復現
(1)還是使用剛才的phpinfo.php(把剛才上傳的那個記得刪掉)


(2)打開上傳的文件

High
源碼解析
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Is it an image? // strtoLower把所有字符轉換為小寫 getimagesize(string filename) 函數會通過讀取文件頭,返回圖片的長、寬等信息,如果沒有相關的圖片文件頭,函數會報錯。 可以看到,High級別的代碼讀取文件名中最后一個”.”后的字符串,期望通過文件名來限制文件類型,因此要求上傳文件名形式必須是”*.jpg”、”*.jpeg” 、”*.png”之一。同時,getimagesize函數更是限制了上傳文件的文件頭必須為圖像類型。 if( ( strtoLower( $uploaded_ext ) == "jpg" || strtoLower( $uploaded_ext ) == "jpeg" || strtoLower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?>
這個就必須上傳一個圖片馬了,他對文件的文件頭,文件名形式等等都做了檢查。
漏洞復現
(1)先制作一個圖片馬(注意:圖片馬的文件大小不能超過100000B)

copy phpinfo.png/b+phpinfo.php/a phpinfo.png

(2)將准備好的圖片馬上傳

(3)打開上傳的圖片馬,可以看到上傳成功

(4)結合文件包含漏洞,去包含我們上傳的文件,成功執行phpinfo

Impossible
源碼解析
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Check Anti-CSRF 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 ) . '-'; //上傳文件的文件前綴md5加密 $target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; //in_get(varname) 函數返回相應選項的值 $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? 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' ) { //imagecreatefromjpeg ( filename ) 函數返回圖片文件的圖像標識,失敗返回false $img = imagecreatefromjpeg( $uploaded_tmp ); //imagejpeg ( image , filename , quality) 從image圖像以filename為文件名創建一個JPEG圖像,可選參數quality,范圍從0(最差質量,文件更小)到100(最佳質量,文件最大)。 imagejpeg( $img, $temp_file, 100); } else { $img = imagecreatefrompng( $uploaded_tmp ); imagepng( $img, $temp_file, 9); } // imagedestroy( img ) 函數銷毀圖像資源 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! echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; } else { // No echo '<pre>Your image was not uploaded.</pre>'; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } // Generate Anti-CSRF token generateSessionToken(); ?>
漏洞復現
這個級別的文件上傳對上傳的文件進行了重命名(搞了一個MD5的加密),還增加了token值的校驗,對文件的內容也做了嚴格的檢查。
