Upload-labs 通關學習筆記


Upload-labs

By:Mirror王宇陽

2019年11月~

文件上傳解析學習

環境要求

若要自己親自搭建環境,請按照以下配置環境,方可正常運行每個Pass。

配置 項 配置 描述
操作系統 Window or Linux 推薦使用Windows,除了Pass-19必須在linux下,其余Pass都可以在Windows上運行
PHP版本 推薦5.2.17 其他版本可能會導致部分Pass無法突破
PHP組件 php_gd2,php_exif 部分Pass依賴這兩個組件
中間件 設置Apache以moudel方式連接

Upload-labs環境結構

技術摘錄

  • 判斷文件長傳點

判斷上傳點

Pass-01

[源碼]

function checkFile() {
    var file = document.getElementsByName('upload_file')[0].value;
    if (file == null || file == "") {
        alert("請選擇要上傳的文件!");
        return false;
    }
    //定義允許上傳的文件類型
    var allow_ext = ".jpg|.png|.gif";
    //提取上傳文件的類型
    var ext_name = file.substring(file.lastIndexOf("."));
    //判斷上傳文件類型是否允許上傳
    if (allow_ext.indexOf(ext_name + "|") == -1) {
        var errMsg = "該文件不允許上傳,請上傳" + allow_ext + "類型的文件,當前文件類型為:" + ext_name;
        alert(errMsg);
        return false;
    }
}

[分析]

Pass-01是在客戶端使用js對文件進行校驗,完全可以繞過。

[思路]

使用障眼法,將PHP文件修改圖像格式后直接上傳;使用burp攔截該數據包,修改文件格式(后綴名)

Pass-02

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']            
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '文件類型不正確,請重新上傳!';
        }
    } else {
        $msg = UPLOAD_PATH.'文件夾不存在,請手工創建!';
    }
}

[分析]

服務端的源碼對接受的文件MIME進行檢測,判斷是否符合圖像格式;但MIME校驗的缺陷是嚴重的,用戶完全可以隨意修改MIME的參數值,但完全不影響文件格式的正常解析。

[思路]

正常上傳一個PHP文件,Burp攔截數據包,修改數據包中的MIME格式類型為圖像格式的MIME。

Pass-03

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array('.asp','.aspx','.php','.jsp');
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//刪除文件名末尾的點
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //轉換為小寫
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空

        if(!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;            
            if (move_uploaded_file($temp_file,$img_path)) {
                 $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '不允許上傳.asp,.aspx,.php,.jsp后綴文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

源碼檢測進行文件名后綴檢測,由此確定文件類型;無法使用burp攔截改包的方式;由於采用的黑名單方式,只隔離了asp、aspx、php、jsp等文件的后綴;由於php的特性,可以上傳php5等文件后綴也是可以按照PHP文件被解析的

[思路]

上傳“11.php5”文件,直接繞過黑名單。

[ps:獲取上傳目錄和文件名]

由於文件上傳后的文件位置是位置的,且文件名是采用隨機數進行的二次重命名;故此我們可以根據返回的圖像打開圖像位置獲取文件的具體路徑。成功上傳一個php(php5)文件后就會返回一個圖像,打開圖像的URL地址就是PHP文件的地址,例:../upload-labs-master/upload/202003141844327950.php5這個取決於Apache的配置問題(需要修改Apache的配置,讓Apache支持解析例如php3之類的文件為PHP文件)

Pass-04

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//刪除文件名末尾的點
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //轉換為小寫
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件不允許上傳!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

一看數組deny_ext那么多的黑名單;之前的特殊解析方式(混淆解析)也可存在可能,關鍵還是取決於apache的配置安全

[.htaccess知識]

.htaccess文件(分布式配置文件)提供了針對目錄改變配置的方法;特定的文檔目錄中放置一個包含一個或多個指令的文件,以作用於此目錄及其所有子目錄;(是Apache環境下的一種配置行為)

設置.htaccess將當前目錄的所有文件以php文件解析$#$

Pass-05

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//刪除文件名末尾的點
        $file_ext = strrchr($file_name, '.');
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件類型不允許上傳!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

這里把.htaccess也拉入了黑名單,細細的看代碼發現少了之前源代碼中的一行"大小寫轉寫";代碼缺陷瞬間暴露,將文件名進行大小寫混淆即可。

[思路]

上傳muma.Php文件

Pass-06

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = $_FILES['upload_file']['name'];
        $file_name = deldot($file_name);//刪除文件名末尾的點
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //轉換為小寫
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file,$img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件不允許上傳';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

從源碼看來,對黑名單的建設是相當的完善了,同時也添加了小寫轉變檢測的代碼段,之前的大小寫混淆、特殊解析混淆、.htaccess等方法在這里都被封殺了;但是按照慣例,既然是黑名單,我們的繞過思路就依舊是圍繞在黑名單的缺陷上,同時圍繞代碼缺陷找到漏洞.

[思路]

通過對源碼的剖析,發現沒有對截取的文件后綴進行去空處理;這里提示:在操作系統中文件后綴是自動屏蔽刪除后綴名的,但是在代碼處理中空符號存在且可被處理.

在上傳WebShell中直接提交".php"文件即可,使用Burp攔截數據包,修改數據包中的文件后綴(添加一個空符號)

Pass-07

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //轉換為小寫
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件類型不允許上傳!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

文件后綴被全部拉入黑名單,統一轉小寫檢測,字符串去空;但是回歸突破的宗旨,查缺陷!圍繞黑名單發現沒有被拉入且可以繞過檢測的文件后綴方式進行枚舉

[思路]

這里的思路是利用Win文件存儲的特性,自動屏蔽后綴名的尾部.符號;正常上傳php文件,burp攔截數據包就該文件后綴為.php.,成功繞過php的檢測,在win、linux系統上自動消除尾部符號正常保存為。.php文件;(不選擇其他特殊符號結尾的原因:在文件后綴修改上,其他特殊符號都會被正常存儲,只有符號點會被消除)

Pass-08

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//刪除文件名末尾的點
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //轉換為小寫
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件類型不允許上傳!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

這里沒有對::$DATA繞過進行安全檢測

[思路] -> ::$DATA繞過

NTFS文件系統的存儲數據流的一個屬性DATA時,當我訪問a.php::DATA時,就是請求a.php本身的數據。

提交.php文件,Burp攔截數據包修改為.php::$DATA放包;正常以./././xxxx.php訪問該文件即可

Pass-09

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//刪除文件名末尾的點
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //轉換為小寫
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件類型不允許上傳!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

這里否決了之前的所有繞過方案,不過依舊按照黑名單的繞過原則,通過名單缺陷和代碼邏輯缺陷發現:刪除邏輯(刪除尾部的點、刪除收尾的空字符、刪除::$DATA) 這里的刪除邏輯可以利用“雙寫”的方式來做突破

[思路]

通過Burp將文件名字符串修改為.php. .,代碼邏輯會刪除尾部的點和空字符,最后會將.php.提交進行黑名單校驗

Pass-10

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = str_ireplace($deny_ext,"", $file_name);
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH.'/'.$file_name;        
        if (move_uploaded_file($temp_file, $img_path)) {
            $is_upload = true;
        } else {
            $msg = '上傳出錯!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工創建!';
    }
}

[分析]

依舊是黑名單機制,這里發現一個關鍵的代碼邏輯,符合黑名單的字符全部替換為空字符;故此無法在使用之前那些什么后綴名混淆的方法沒有用了,因為只要有符合黑名單中的字符全部替換為空

[思路]

burp攔截將文件名修改為雙寫.phphpp,交給代碼進行校驗,校驗代碼進行一次校驗並替換敏感詞為空字符,再上傳。

Pass-11

[源碼]

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上傳出錯!';
        }
    } else{
        $msg = "只允許上傳.jpg|.png|.gif類型文件!";
    }
}

[分析]

這里不再是黑名單機制,反之采用了“白名單”機制

[思路]

使用%00字節截斷方式繞過-->.php%00

Pass-12

[源碼]

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上傳失敗";
        }
    } else {
        $msg = "只允許上傳.jpg|.png|.gif類型文件!";
    }
}

[分析]

[思路]

00字節截斷,php5.3+版本不再支持

Pass-13

[源碼]

**************客戶端校驗****************
function getReailFileType($filename){
    $file = fopen($filename, "rb");
    $bin = fread($file, 2); //只讀2字節
    fclose($file);
    $strInfo = @unpack("C2chars", $bin); 
        // unpack() 函數從二進制字符串對數據進行解包。   
    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
    $fileType = '';    
    switch($typeCode){      
        case 255216:            
            $fileType = 'jpg';
            break;
        case 13780:            
            $fileType = 'png';
            break;        
        case 7173:            
            $fileType = 'gif';
            break;
        default:            
            $fileType = 'unknown';
        }    
        return $fileType;
}

*************服務端校驗***********************
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_type = getReailFileType($temp_file);

    if($file_type == 'unknown'){
        $msg = "文件未知,上傳失敗!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上傳出錯!";
        }
    }
}

[分析]

這里只允許上傳圖片文件,圖片馬走起;js腳本通過讀文件的前2個字節判斷文件類型

[思路]

這里需要利用文件包含來進行圖片馬的解析

Pass-14

[源碼]

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        //file_exists() 檢測文件是否存在
        $info = getimagesize($filename);
        // getimagesize() 獲取圖像大小及相關信息,成功返回一個數組

        // [寬度,高度,type(返回數值),…………]
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

****************************************************

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上傳失敗!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上傳出錯!";
        }
    }
}

[分析]

這里在客戶端利用getimagesize()函數獲取圖像的信息(返回的數組第三個元素)進行校驗類型;

[思路]

圖片馬

Pass-15

[源碼]

function isImage($filename){
    //需要開啟php_exif模塊
    $image_type = exif_imagetype($filename);
    switch ($image_type) {
        case IMAGETYPE_GIF:
            return "gif";
            break;
        case IMAGETYPE_JPEG:
            return "jpg";
            break;
        case IMAGETYPE_PNG:
            return "png";
            break;    
        default:
            return false;
            break;
    }
}

*************************************************

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上傳失敗!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上傳出錯!";
        }
    }
}

[分析]

這里利用PHP_exif模塊進行判斷文件類型; exif_imagetype()獲取圖片類型

[思路]

圖片馬

Pass-16

[源碼]

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
    // 獲得上傳文件的基本信息,文件名,類型,大小,臨時文件路徑
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    $target_path=UPLOAD_PATH.'/'.basename($filename);

    // 獲得上傳文件的擴展名
    $fileext= substr(strrchr($filename,"."),1);

    //判斷文件后綴與類型,合法才進行上傳操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上傳的圖片生成新的圖片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "該文件不是jpg格式的圖片!";
                @unlink($target_path);
            }else{
                //給新圖片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                //顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagejpeg($im,$img_path);
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上傳出錯!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上傳的圖片生成新的圖片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "該文件不是png格式的圖片!";
                @unlink($target_path);
            }else{
                 //給新圖片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                //顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagepng($im,$img_path);

                @unlink($target_path);
                $is_upload = true;               
            }
        } else {
            $msg = "上傳出錯!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上傳的圖片生成新的圖片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "該文件不是gif格式的圖片!";
                @unlink($target_path);
            }else{
                //給新圖片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                //顯示二次渲染后的圖片(使用用戶上傳圖片生成的新圖片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagegif($im,$img_path);

                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上傳出錯!";
        }
    }else{
        $msg = "只允許上傳后綴為.jpg|.png|.gif的圖片文件!";
    }
}

[分析]

這是一道大題,綜合判斷后綴名、content-type、利用imagecreatefromgif等多種方式來驗證是否為圖片;更關鍵的是在判斷是圖片后還會進行第二次圖片渲染,圖片的十六進制內容會發生很多變化。送上一個很好的分析文章(思路)

Pass-17

[源碼]

$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_name = $_FILES['upload_file']['name'];
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . '/' . $file_name;

    if(move_uploaded_file($temp_file, $upload_file)){
        if(in_array($file_ext,$ext_arr)){
             $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             $is_upload = true;
        }else{
            $msg = "只允許上傳.jpg|.png|.gif類型文件!";
            unlink($upload_file);
        }
    }else{
        $msg = '上傳出錯!';
    }
}

[分析]

條件競爭繞過(參考文章)

Pass-19

CVE-2015-2348 move_uploaded_file() 00截斷

Pass-20

Pass-20參考文章


Upload-labs是在去年接觸並做了部分Pass,近期整理資料發現了這份沒有完成的稿子,於是閑暇之余所有Pass整理匯總(部分Pass由於環境的約束和時間不適沒有測試,轉載了前輩的文章)

Upload-labs考查了絕大多數目前主流的文件上傳方法和解析,在測試學習的過程中更可以學會避開文件上傳的風險。

防御文件上傳的方法離不開:

  • 前端限制:利用Js代碼限制上傳的文件類型,但這是不可靠且不可不用的方法,前端的一切防御都可以經過數據抓包進行繞過

  • 檢查擴展名:后端檢查MIME的意義並不大,但可以對文件的擴展名進行檢測(繞過方法也是多種),這里建議首先將PHP升級到5.4以上的版本規避00字節繞過、按照安全的配置要求配置Apache(同時也是建議Nginx);對擴展名進行完整的截取檢查比對黑名單(或采用白名單機制也可以,具體需求具體設計,當然了,白名單是更加安全);記住是嚴格的后綴名檢查!!!

  • 檢查解析漏洞:檢查是否存在解析漏洞,如果存在解析漏洞繞過白/黑名單是輕而易舉的;筆者從各處收集了一些解析漏洞的文章,供參考:

  • 分析文件頭內容來檢查文件類型:這類方法不是檢查文件后綴那般的簡單,而是對文件內容進行檢查;利用各類文件特定類型都會有不一樣的標志位和開頭;可利用php的exif_imagetype()函數(也可以自己編寫)

  • 其他防御策略非常的多,筆者暫未深入研究安全防御的策略建立


免責聲明!

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



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