DVWA(Damn Vulnerable Web Application)
DVWA(Damn Vulnerable Web Application)是一個用來進行弱點安全測試的網站系統,旨在為安全專業人員測試自己的專業技能和工具提供合法的環境,幫助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 的程式碼分為四種安全級別:Low,Medium,High,Impossible。初學者可以通過比較四種級別的程式碼,接觸到一些PHP代碼審計的內容。
File Upload (low)
File Upload,即檔案上傳漏洞,通常是由於對上傳檔案的類型、內容沒有進行嚴格的過濾、檢查,使得攻擊者可以通過上傳webshell取得伺服器權限,因此檔案上傳漏洞帶來的危害常常是嚴重性的。
在這題目的就是要上傳webshell,所以直接來看原始碼
透過POST方式進行上傳,第5行顯示上傳後的路徑為hackable/uploads,簡單來說就是一個很單純的上傳檔案功能,沒有經過任何的過濾、檢查。
<?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>";
}
}
?>
所以我們可以直接上傳一個我們構造的php檔案,例如webshell或phpinfo。
接著將該檔案上傳後,提示檔案的目錄為
../../hackable/uploads/phpinfo.php
接著直接訪問該檔案目錄即可。
http://192.168.43.102/DVWA/hackable/uploads/phpinfo.php
File Upload (medium)
直接查看Source Code,路徑一樣為hackable/uploads/,但在第14行檢查了檔案的type是否為image/jpeg、png,並且檔案的size < 100000 (約97.6KB)。
<?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>';
}
}
?>
在這題,要成功上傳webshell有兩種方式,一種是直接上傳圖片馬,然後可以用一些webshell管理工具直接連接。一種是可透過Burp更改附檔名。
這邊介紹第二種方式,首先我們先將原本的phpinfo.php改成phpinfo.png,然後開啟Burp後,上傳該檔案。
在Burp這邊,可以看到我們上傳的filename=phpinfo.png,我們將他更改為phpinfo.php,即可成功上傳檔案。
可以注意的是,我們一樣是Content-Type:image/png,只是檔案附檔名改為php了。
將phpinfo.png更改為phpinfo.php後,在Burp點選Forward,即可看到檔案成功上傳。
File Upload (High)
直接看原始碼,這題一樣檢查了jpg、jpeg、png類型,這邊需要注意的是用到了getimagesize(string filename),主要就是讀取圖片的header、圖片長寬等資訊,如果格式不對就會報錯。
<?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>';
}
}
?>
這題因為會檢查圖片的格式,所以需要將webshell與圖片合併起來。
我們可以準備一張jpg圖片跟一個php檔案,然後透過copy的方式合併
生成一個webshell.jpg
接著將這webshell.jpg上傳,並透過Burp攔截檢查,可以發現最下面藏著我們的phpinfo程式碼。
成功上傳了偽造的圖片。
這樣的方式需要透過webshell管理工具(例:中國菜刀、蟻劍)來搭配利用,既然成功上傳了,後面就不多解釋了。接下來就是透過管理工具連上偽造的圖片即可拿到shell了。
File Upload (Impossible)
直接看原始碼,第五行這邊增加了Anti-CSRF,目的是防止CSRF,接著第23行檢查檔案附檔名及大小跟檔案屬性,最重要的還是18行把上傳的檔案名稱重新命名為md5亂數,30行也用到了imagecreatefromjpeg等,成功阻擋了上傳惡意文件的可能性。
<?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();
?>
結束。