1. 程式人生 > >DVWA-1.9全級別教程之File Upload

DVWA-1.9全級別教程之File Upload

*本文原創作者:lonehand,轉載須註明來自FreeBuf.COM
目前,最新的DVWA已經更新到1.9版本(http://www.dvwa.co.uk/),而網上的教程大多停留在舊版本,且沒有針對DVWA high級別的教程,因此萌發了一個撰寫新手教程的想法,錯誤的地方還請大家指正。
DVWA簡介
DVWA(Damn Vulnerable Web Application)是一個用來進行安全脆弱性鑑定的PHP/MySQL Web應用,旨在為安全專業人員測試自己的專業技能和工具提供合法的環境,幫助web開發者更好的理解web應用安全防範的過程。
DVWA共有十個模組,分別是
Brute Force(暴力破解)
Command Injection(命令列注入)
CSRF(跨站請求偽造)
File Inclusion(檔案包含)
File Upload(檔案上傳)
Insecure CAPTCHA (不安全的驗證碼)
SQL Injection(SQL注入)
SQL Injection(Blind)(SQL盲注)
XSS(Reflected)(反射型跨站指令碼)
XSS(Stored)(儲存型跨站指令碼)
需要注意的是,DVWA 1.9的程式碼分為四種安全級別:Low,Medium,High,Impossible。初學者可以通過比較四種級別的程式碼,接觸到一些PHP程式碼審計的內容。


DVWA的搭建
Freebuf上的這篇文章《新手指南:手把手教你如何搭建自己的滲透測試環境(http://www.freebuf.com/sectool/102661.html)已經寫得非常好了,在這裡就不贅述了。
File Upload
File Upload,即檔案上傳漏洞,通常是由於對上傳檔案的型別、內容沒有進行嚴格的過濾、檢查,使得攻擊者可以通過上傳木馬獲取伺服器的webshell許可權,因此檔案上傳漏洞帶來的危害常常是毀滅性的,Apache、Tomcat、Nginx等都曝出過檔案上傳漏洞。


下面將對四種級別的程式碼進行分析。
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
		$html .= '<pre>Your image was not uploaded.</pre>';
	}
	else {
		// Yes!
		$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
	}
}

?>
basename(path,suffix)        
函式返回路徑中的檔名部分,如果可選引數suffix為空,則返回的檔名包含字尾名,反之不包含字尾名。
可以看到,伺服器對上傳檔案的型別、內容沒有做任何的檢查、過濾,存在明顯的檔案上傳漏洞,生成上傳路徑後,伺服器會檢查是否上傳成功並返回相應提示資訊。檔案上傳漏洞的利用是有限制條件的,首先當然是要能夠成功上傳木馬檔案,其次上傳檔案必須能夠被執行,最後就是上傳檔案的路徑必須可知。不幸的是,這裡三個條件全都滿足。
上傳檔案hack.php(一句話木馬)。

上傳成功,並且返回了上傳路徑。


開啟中國菜刀,右鍵新增,位址列填入上傳檔案所在路徑http://192.168.153.130/dvwa/hackable/uploads/hack.php,引數名(一句話木馬口令)為apple。


然後菜刀就會通過向伺服器傳送包含apple引數的post請求,在伺服器上執行任意命令,獲取webshell許可權。可以下載、修改伺服器的所有檔案。


可以開啟伺服器的虛擬終端。


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
			$html .= '<pre>Your image was not uploaded.</pre>';
		}
		else {
			// Yes!
			$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
		}
	}
	else {
		// Invalid file
		$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
	}
}

?>
可以看到,Medium級別的程式碼對上傳檔案的型別、大小做了限制,要求檔案型別必須是jpeg或者png,大小不能超過100000B(約為97.6KB)。因為採用的是一句話木馬,所以檔案大小不會有問題,至於檔案型別的檢查,嘗試修改檔名為hack.png。但是不幸的是,雖然成功上傳了檔案,但是並不能成功獲取webshell許可權,在菜刀上無論進行什麼操作都會返回如下資訊。

中國菜刀的原理是向上傳檔案傳送包含apple引數的post請求,通過控制apple引數來執行不同的命令,而這裡伺服器將木馬檔案解析成了圖片檔案,因此向其傳送post請求時,伺服器只會返回這個“圖片”檔案,並不會執行相應命令。那麼如何讓伺服器將其解析為php檔案呢?這裡可以藉助Medium級別的檔案包含漏洞來獲取webshell許可權,開啟中國菜刀,右鍵新增,在位址列中輸入http://192.168.153.130/dvwa/vulnerabilities/fi/?page=hthttp://tp://192.168.153.130/dvwa/hackable/uploads/hack.png,引數名為apple,指令碼語言選擇php。

點選新增,成功獲取webshell許可權。


還可以抓包修改檔案型別。上傳hack.png檔案,抓包。


可以看到檔案型別為image/png,嘗試修改filename為hack.php。


上傳成功。


上菜刀,獲取webshell許可權。


另外,在php版本小於5.3.4的伺服器中,當Magic_quote_gpc選項為off時,可以在檔名中使用%00截斷,所以可以把上傳檔案命名為hack.php%00.png。可以看到,包中的檔案型別為image/png,可以通過檔案型別檢查。

上傳成功。


而伺服器會認為其檔名為hack.php,順勢解析為php檔案。遺憾的是,由於本次實驗環境的php版本為5.4.31,所以無法進行驗證。
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
			$html .= '<pre>Your image was not uploaded.</pre>';
		}
		else {
			// Yes!
			$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
		}
	}
	else {
		// Invalid file
		$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
	}
}

?>
strrpos(string,find,start)
函式返回字串find在另一字串string中最後一次出現的位置,如果沒有找到字串則返回false,可選引數start規定在何處開始搜尋。
getimagesize(string filename)
函式會通過讀取檔案頭,返回圖片的長、寬等資訊,如果沒有相關的圖片檔案頭,函式會報錯。
可以看到,High級別的程式碼讀取檔名中最後一個”.”後的字串,期望通過檔名來限制檔案型別,因此要求上傳檔名形式必須是”*.jpg”、”*.jpeg” 、”*.png”之一。同時,getimagesize函式更是限制了上傳檔案的檔案頭必須為影象型別。
採用%00截斷的方法可以輕鬆繞過檔名的檢查,但是需要將上傳檔案的檔案頭偽裝成圖片,由於實驗環境的php版本原因,這裡只演示如何藉助High級別的檔案包含漏洞來完成攻擊。首先利用copy將一句話木馬檔案php.php與圖片檔案1.jpg合併。

生成的檔案hack.jpg 。


開啟可以看到,一句話木馬藏到了最後。


順利通過檔案頭檢查,可以成功上傳。


上菜刀,右鍵新增shell,位址列填入http://192.168.153.130/dvwa/vulnerabilities/fi/?page=file:///C:/xampp/htdocs/dvwa/hackable/uploads/hack.jpg,引數名填apple,指令碼語言選擇php。


成功拿到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!
			$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();

?>
in_get(varname)
函式返回相應選項的值。
imagecreatefromjpeg(filename)
函式返回圖片檔案的影象標識,失敗返回false。
imagejpeg(image,filename,quality)
從image影象以filename為檔名建立一個JPEG影象,可選引數quality,範圍從 0(最差質量,檔案更小)到100(最佳質量,檔案最大)。
imagedestroy(img)
函式銷燬影象資源。
可以看到,Impossible級別的程式碼對上傳檔案進行了重新命名(為md5值,導致%00截斷無法繞過過濾規則),加入Anti-CSRF token防護CSRF攻擊,同時對檔案的內容作了嚴格的檢查,導致攻擊者無法上傳含有惡意指令碼的檔案。