upload - labs (下)
Pass - 11:
1.檢視原始碼,發現進行了一次對字尾名替換成空格,因此考慮雙寫繞過,
2.上傳成功,
關鍵程式碼:
$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","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 . '資料夾不存在,請手工建立!';
}
}
Pass -12:
1.檢視原始碼,看到是白名單判斷,但是$img_path直接拼接嘗試%00截斷,但在進行move_uploaded_file前。利用$_GET['save_path']和隨機時間函式進行拼接,拼接成檔案儲存路徑。
因此構造檔案儲存路徑利用了_GET傳入:(注意%00要進行編碼)
2.上傳成功
檢視原始碼:
$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型別檔案!";
}
}
補充:
截斷條件:
1、php版本小於5.3.4
2、php.ini的magic_quotes_gpc為OFF狀態
Pass - 13:
同Pass - 12,唯一的區別就是GET換成了POST,
但這次需要在二進位制中進行修改,因為post不會像get對%00進行自動解碼。
關鍵程式碼:
$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 - 14:
1.任務要求上傳圖片馬,
2.製作圖片馬,檢視原始碼可知,通過讀檔案的前2個位元組判斷檔案型別,也可以在一句話檔案內容前面加圖片的標識例如GIF89a。
3.上傳成功,
關鍵程式碼:
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 = "上傳出錯!";
}
}
}
補充:
unpack() 函式從二進位制字串對資料進行解包。
Pass - 15:
1.檢視一下提示,
2.檢視原始碼,用getimagesize獲取檔案型別,還是直接就可以利用圖片馬就可進行繞過,
關鍵程式碼:
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 = "上傳出錯!";
}
}
}
補充:
getimagesize() 函式用於獲取影象大小及相關資訊,成功返回一個數組,失敗則返回 FALSE 併產生一條 E_WARNING 級的錯誤資訊。
語法格式:
array getimagesize ( string $filename [, array &$imageinfo ] )
getimagesize() 函式將測定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 影象檔案的大小並返回影象的尺寸以及檔案型別及圖片高度與寬度。
Pass - 16:
1.檢視提示,
2.檢視原始碼,用到php_exif模組來判斷檔案型別,還是直接就可以利用圖片馬就可進行繞過
關鍵程式碼:
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 = "上傳出錯!";
}
}
}
補充:
exif_imagetype — 判斷一個影象的型別
exif_imagetype ( string $filename ) : int
exif_imagetype() 讀取一個影象的第一個位元組並檢查其簽名。
本函式可用來避免呼叫其它 exif 函式用到了不支援的檔案型別上或和 $_SERVER['HTTP_ACCEPT'] 結合使用來檢查瀏覽器是否可以顯示某個指定的影象。
Pass - 17:
(重點!!!)
1.檢視原始碼,
判斷檔案型別Content-Type:
$filetype = $_FILES['upload_file']['type'];
獲取檔案字尾副檔名:
$fileext= substr(strrchr($filename,"."),1);
然後根據不同的檔案字尾副檔名進行二次渲染:
jpg:
$im = imagecreatefromjpeg($target_path); #使用上傳的圖片生成新的圖片(二次渲染)
png:
$im = imagecreatefrompng($target_path);
gif:
$im = imagecreatefromgif($target_path);
2.上傳方法與前幾關類似。
關鍵程式碼:
$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的圖片檔案!";
}
}
補充:
imagecreatefromgif — 由檔案或 URL 建立一個新圖象。 說明 imagecreatefromgif ( string $filename ) : resource imagecreatefromgif() 返回一影象識別符號,代表了從給定的檔名取得的影象。
參考:
Pass - 18:
思路:
檢視程式碼,需要程式碼審計,檢視原始碼,先move_uploaded_file函式將上傳檔案臨時儲存,再進行判斷,如果不在白名單裡則unlink刪除,在的話就rename重新命名,所以這裡存在條件競爭。
因此可以上傳aaa.php只需要在它刪除之前訪問即可,可以利用burp的intruder模組不斷上傳,然後不斷地訪問重新整理該地址即可。
步驟:
1.用burp開啟兩個intruder模組,一個用於重複上傳,另一個用於重複訪問。先設定上傳請求,記住此處的檔名,等下要用來拼接訪問請求的url。
因為此處沒有什麼引數需要爆破,只是需要重複發起請求,所以payload設定為Null payloads,設定訪問次數10000次,執行緒50個
3.設定訪問請求:
①瀏覽器構造請求url:http://127.0.0.1/upload-labs/upload/aaa.php,進行訪問,然後用burp抓包②burp抓包後傳送至intruder模組,然後設定payload,這一步和上傳請求設定差不多,都是Null payloads、10000次、50個執行緒
4.設定好兩個模組後同時啟動,觀察結果,因為我們傳入的php程式碼是phpinfo();
,所以如果訪問成功的話,會返回php的配置資訊。
因為條件競爭繞過存在一定概率,實踐中如果一次不成功,可以多試幾次。
關鍵程式碼:
$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 = '上傳出錯!';
}
}
補充:
strrpos() 函式查詢字串在另一字串中最後一次出現的位置(區分大小寫)。
註釋:strrpos() 函式是區分大小寫的。
相關函式:
- strpos() - 查詢字串在另一字串中第一次出現的位置(區分大小寫)
- stripos() - 查詢字串在另一字串中第一次出現的位置(不區分大小寫)
- strripos() - 查詢字串在另一字串中最後一次出現的位置(不區分大小寫)
move_uploaded_file() 函式把上傳的檔案移動到新位置。 如果成功該函式返回 TRUE,如果失敗則返回 FALSE。 語法 move_uploaded_file(file,newloc)
註釋:該函式僅用於通過 HTTP POST 上傳的檔案。
註釋:如果目標檔案已經存在,將會被覆蓋。
Pass - 19:
思路:
檢視原始碼,先將上傳的檔案儲存(move函式),再rename重新命名一下。所以也存在條件競爭,繞過方法和上面Pass-18差不多
關鍵程式碼:
//index.php
$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" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
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 flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
View Code
Pass - 20:
(重要!!!)
1.檢視原始碼,要繞過.jpg,
2.move_uploaded_file()函式會忽視檔名末尾的 /.
因此可以構造aaa.php/.進行繞過,
post: save_name = aaa.php%00.jpg
post: save_name = aaa.php/.
3.上傳成功,
關鍵程式碼:
$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 . '資料夾不存在,請手工建立!';
}
}
補充:
pathinfo() 函式
定義和用法
pathinfo() 函式以陣列的形式返回關於檔案路徑的資訊。
返回的陣列元素如下:
[dirname]: 目錄路徑
[basename]: 檔名
[extension]: 檔案字尾名
[filename]: 不包含字尾的檔名
語法
pathinfo(path,options)
path | 必需。規定要檢查的路徑。 |
options | 可選。規定要返回的陣列元素。預設是 all。
可能的值:
|
如果不是請求所有的元素,則 pathinfo() 函式返回字串
Pass - 21:
1.看檢視原始碼,第5-6行先進行了一個Content-Type判斷
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
2.10-13行,如果save_name是字串的話就通過explode函式,將post進去的save_name轉成小寫後按’.'打散成陣列。
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
3.第20行,由於後面filename構成的過程中由於file[count(file[count(file[count(file) - 1]的作用,導致$file[1] = NULL,所以構造檔名後相當於直接就是xx.php/.,
$file_name = reset($file) . '.' . $file[count($file) - 1];
關鍵程式碼:
$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 = "請選擇要上傳的檔案!";
}
參考:
https://blog.csdn.net/weixin_41182861/article/details/105546654