今天群里有人聊圖片上傳,簡單說下自己的經驗(大牛勿噴)
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; }
