今天群里有人聊圖片上傳,簡單說下自己的經驗(大牛勿噴)
0.如果你的方法里面是有指定路徑的,記得一定要過濾../,比如你把 aa文件夾設置了權限,一些類似於exe,asp,php之類的文件不能執行,那么如果我在傳路徑的時候,前面加了一個../呢,這樣這種服務器端的限制就跳過了。(DJ音樂站基本上都有這個問題,以及用某編輯器的同志)
1.常用方法:這種就是根據后綴判斷是否是圖片文件,需要注意的是這種格式:文件:1.asp;.jpg 1.asp%01.jpg 目錄: 1.jpg/1.asp 1.jpg/1.php 等等,IIS和Nginx部分版本是有解析漏洞的(不要用文件原有名稱,eg:1.asp.jpg=》去后綴后的名字就是1.asp)
/// <summary> /// 圖片上傳 /// </summary> /// <param name="file"></param> /// <returns></returns> public JsonResult UploadA(HttpPostedFileBase file) { if (file == null) { return Json(new { status = false, msg = "圖片提交失敗" }); } if (file.ContentLength > 10485760) { return Json(new { status = false, msg = "文件10M以內" }); } string filterStr = ".gif,.jpg,.jpeg,.bmp,.png"; string fileExt = Path.GetExtension(file.FileName).ToLower(); if (!filterStr.Contains(fileExt)) { return Json(new { status = false, msg = "圖片格式不對" }); } //todo: md5判斷一下文件是否已經上傳過,如果已經上傳直接返回 return Json(new { status = true, msg = sqlPath }); string path = string.Format("{0}/{1}", "/lotFiles", DateTime.Now.ToString("yyyy-MM-dd")); string fileName = string.Format("{0}{1}", Guid.NewGuid().ToString("N"), fileExt); string sqlPath = string.Format("{0}/{1}", path, fileName); string dirPath = Request.MapPath(path); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } try { //todo:縮略圖 file.SaveAs(Path.Combine(dirPath, fileName)); //todo: 未來寫存數據庫的Code } catch { return Json(new { status = false, msg = "圖片保存失敗" }); } return Json(new { status = true, msg = sqlPath }); }
2.Context-Type的方法(很多人說這個安全性比上一個高。。。。。呃,也許吧,上面至少還有個文件后綴硬性判斷,contentType這玩意抓個包,本地代理一開,直接就可以串改,傳的是1.asp,你收的contextType依舊是圖片格式,最后保存就玩完了)
/// <summary> /// 圖片上傳 /// </summary> /// <param name="file"></param> /// <returns></returns> public JsonResult UploadB(HttpPostedFileBase file) { if (file == null) { return Json(new { status = false, msg = "圖片提交失敗" }); } if (file.ContentLength > 10485760) { return Json(new { status = false, msg = "文件10M以內" }); } //判斷文件格式(MimeMapping) var contentType = file.ContentType; if (contentType == null) { return Json(new { status = false, msg = "圖片提交失敗" }); } contentType = contentType.ToLower(); var extList = new Dictionary<string, string>() { { "image/gif", ".gif" }, { "image/jpeg", ".jpg" }, { "image/bmp", ".bmp" }, { "image/png", ".png" } }; if (!extList.ContainsKey(contentType)) { return Json(new { status = false, msg = "圖片格式不對" }); } //todo: md5判斷一下文件是否已經上傳過,如果已經上傳直接返回 return Json(new { status = true, msg = sqlPath }); string path = string.Format("{0}/{1}", "/lotFiles", DateTime.Now.ToString("yyyy-MM-dd")); string fileName = string.Format("{0}{1}", Guid.NewGuid().ToString("N"), extList[contentType]); string sqlPath = string.Format("{0}/{1}", path, fileName); string dirPath = Request.MapPath(path); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } try { //todo:縮略圖 file.SaveAs(Path.Combine(dirPath, fileName)); //todo: 未來寫存數據庫的Code } catch { return Json(new { status = false, msg = "圖片保存失敗" }); } return Json(new { status = true, msg = sqlPath }); }
如果非要用這個,建議和第一個一起用
3.頭文件判斷,很多人都以為這是最終方案。。。。。。呃,也許吧。。。
先貼代碼:
/*頭文件參考:(我自己測是如有偏差請聯系我)
7790:exe,dll
5666:psd
6677:bmp
7173:gif
13780:png
255216:jpg,jpeg
239187:js
6787:swf
7368:mp3
4838:wma
6063:xml
8297:rar
55122:7z
8075:docx,xlsx,pptx,vsdx,mmap,xmind,“zip”
208207:doc,xls,ppt,mpp,vsd
*/
/// <summary> /// 判斷擴展名是否是指定類型---默認是判斷圖片格式,符合返回true(沒有釋放stream,請手動:file.InputStream.Dispose();) /// eg:圖片+壓縮+文檔:"7173", "255216", "6677", "13780", "8297", "55122", "8075", "208207" /// eg:img,"7173", "255216", "6677", "13780" //gif //jpg //bmp //png /// eg:file,"8297", "55122", "8075", "208207" //rar //7z //zip + 文檔系列 /// </summary> /// <param name="stream">文件流</param> /// <param name="fileTypes">文件擴展名</param> /// <returns></returns> public static bool CheckingExt(this Stream stream, params string[] fileTypes) { if (fileTypes == null || fileTypes.Length == 0) { fileTypes = new string[] { "7173", "255216", "6677", "13780" }; } bool result = false; string fileclass = ""; #region 讀取頭兩個字節 var reader = new BinaryReader(stream); byte[] buff = new byte[2]; try { reader.Read(buff, 0, 2);//讀取每個文件的頭兩個字節 fileclass = buff[0].ToString() + buff[1].ToString(); } catch (System.Exception ex) { stream.Dispose(); reader.Dispose(); return false; } #endregion #region 校驗 for (int i = 0; i < fileTypes.Length; i++) { if (fileclass == fileTypes[i]) { result = true; break; } } #endregion return result; }
/// <summary> /// 圖片上傳(理論上需要二次渲染下圖片,微軟Save的時候有應該有一定的驗證[我把含有一句話木馬的圖片上傳,最后會返回一張空圖片]) /// </summary> /// <returns></returns> public JsonResult UploadC(HttpPostedFileBase file) { if (file == null) { return Json(new { status = false, msg = "圖片提交失敗" }); } if (file.ContentLength > 10485760) { return Json(new { status = false, msg = "文件10M以內" }); } string filterStr = ".gif,.jpg,.jpeg,.bmp,.png"; string fileExt = Path.GetExtension(file.FileName).ToLower(); if (!filterStr.Contains(fileExt)) { return Json(new { status = false, msg = "圖片格式不對" }); } //防止黑客惡意繞過,頭文件判斷文件后綴 if (!file.InputStream.CheckingExt()) { //todo:一次危險記錄 return Json(new { status = false, msg = "圖片格式不對" }); } //todo: md5判斷一下文件是否已經上傳過,如果已經上傳直接返回 return Json(new { status = true, msg = sqlPath }); string path = string.Format("{0}/{1}", "/lotFiles", DateTime.Now.ToString("yyyy-MM-dd")); string fileName = string.Format("{0}{1}", Guid.NewGuid().ToString("N"), fileExt); string sqlPath = string.Format("{0}/{1}", path, fileName); string dirPath = Request.MapPath(path); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } try { //todo:縮略圖 + 水印 file.SaveAs(Path.Combine(dirPath, fileName)); //todo: 未來寫存數據庫的Code } catch { return Json(new { status = false, msg = "圖片保存失敗" }); } return Json(new { status = true, msg = sqlPath }); }
其實這個很好欺騙的,好幾種方法,簡單說2種:
第1個,用Copy命令
生成了一句話圖片木馬
第2個,用edjpgcom 打開一張圖片就可以直接插入一句話木馬了
圖片跟之前看起來沒什么不同的
用WinHex看看~
上傳測試
成功上傳了
有人說把圖片另存為其他格式就能消除一句話木馬。。。。。呃,好吧,你可以這樣理解~看圖:
滲透的時候一般遇到這種圖片上傳后再二次渲染的,一般直接放棄,因為內部的一句話已經不存在了
至於二次渲染是什么鬼,可以先自行研究會,先睡了~~~
public static bool CheckingExt(this Stream stream, params string[] fileTypes) { if (fileTypes == null || fileTypes.Length == 0) { fileTypes = new string[] { "7173", "255216", "6677", "13780" }; } bool result = false; string fileclass = "";
#region 讀取頭兩個字節 var reader = new BinaryReader(stream); byte[] buff = new byte[2]; try { reader.Read(buff, 0, 2);//讀取每個文件的頭兩個字節 fileclass = buff[0].ToString() + buff[1].ToString(); } catch (System.Exception ex) { stream.Dispose(); reader.Dispose(); return false; } #endregion
#region 校驗 for (int i = 0; i < fileTypes.Length; i++) { if (fileclass == fileTypes[i]) { result = true; break; } } #endregion return result; }