上传伪技术~很多人都以为判断了后缀,判断了ContentType,判断了头文件就真的安全了吗?
今天群里有人聊图片上传,简单说下自己的经验(大牛勿喷)
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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /// <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依旧是图片格式,最后保存就玩完了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /// <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
*/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /// <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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /// <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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异