资产测绘
fofa:
body="JHSoft.Web.AddMenu" || app="金和网络-金和OA" || app="Jinher-OA"
漏洞分析
首先进入到JHSoft.Web.Ask
下的SignUpload
类中
我们分析ProcessRequest
方法中的部分内容
分析:我们看到在if中是判断前端请求中的token
和filename
参数是否为空,若不为空就进入if语句中,在if语句里首先是将查询字符串中获取 token
和 filename
参数,并将它们分别赋值给 _token
和 _fileName
变量,之后使用Split
方法将_token
的内容按照_
进行分割存储在数组arry
中,若数组array
的长度大于1,就将下标为0和1的元素赋值分别赋值给text2
和value
参数,紧接着就是判断这两个参数是否为空,若不为空就将text2作为参数调用getDocInfo
方法进行数据库的查询操作将结果赋值给docInfo
,然后从 docInfo
的第一行中获取 UserName
和 FileName
字段的值,并将它们分别赋值给 text3
和 text4
变量。
接下来我们进入到getDocInfo
方法中,查看有关数据库执行的内容
分析:该方法先是创建一个StringBuilder
对象用于存储sql语句;我们发现参数AskID
直接拼接到了sql语句中,这说明我们的text2
也是直接拼接到sql语句中的,之后就调用了DBOperatorFactory
下的GetDBOperator
方法,该方法如下图所示是实现数据库操作对象的工厂模式,根据配置的数据库类型返回相应的数据库操作对象
之后就是根据得到的数据库对象来调用相应的ExecSQLReDataTable
方法,执行sql语句
接下来我们进入到ExecSQLReDataTable
方法中
分析:该方法创建了一个DataTable对象用于存储sql语句执行的结果,之后创建一个SqlDBOperator.ReturnMethord
类型的实例returnResult
,并将当前方法的ReturnDataTable
委托赋值给它,这是一个回调函数,接着调用ExecSQL
方法执行sql语句
接着我们进入到ExecSQL
方法中
分析:该方法首先是调用ClearErrorMessage
方法清除之前的错误方法,接着就是创建了一个StackTrace对象记录了当前线程的调用栈,后面就是获取StackTrace对象获取调用栈的第三个帧,然后获取该帧的方法名以及其反射类型(即包含该方法的类)的全名分别赋值给成员变量CallMethodName
和CallClassName
,接着检查是否处于事务中,若处于事务中调用ExecSQLInTrans
方法,反之调用ExecSQLNotInTrans
方法
其实ExecSQLInTrans
和ExecSQLNotInTrans
是差不多的,都会执行黄框中的内容,只是异常处理有稍微不同
我们着重分析一下共同部分
this.comm = new SqlCommand(ProcedureName, this.conn, this.trans);// 设置命令文本为传入的 QueryString。 this.comm.CommandType = CommandType.StoredProcedure;// 指定命令类型为文本,即普通的SQL语句。 this.comm.CommandTimeout = 90;//设置命令执行的超时时间为90秒 try { if (!this.OpenConn()) {//尝试数据连接,若连接失败返回-1 return -1; }//连接成功,调用 ReturnResult 委托,传入当前的命令对象 this.comm 和 ReValue。这个委托负责执行SQL命令并返回结果。 this.SqlCommAddParameter(this.comm, ParaValues); ReValue = ReturnResult(this.comm, ReValue); this.conn.Close(); }
而我们的 ReturnResult
委托是在之前的ExecSQLReDataTable
方法中定义好的
我们进入该方法查看 ReturnResult
方法是怎样实现的
分析:该方法是一个私有方法,接收了两个参数(comm
和ReValue
),创建一个新的 SqlDataAdapter
对象 sqlDataAdapter
,使用 comm
作为其命令对象。接着调用 sqlDataAdapter.Fill
方法,传入 ReValue
强制转换为 DataTable
类型,执行sql语句并返回
接着我们进入到Fill
方法中
分析:声明一个 IntPtr
类型的变量 intPtr
,接着调用 Bid.ScopeEnter
方法,传入 intPtr
、一个格式化字符串,以及 base.ObjectID
。声明一个 int
类型的变量 result
,用于存储填充操作的结果。在try语句中创建了一个dataTable数组,传入的dataTable
参数就在其中;接着获取 IDbDataAdapter
的 SelectCommand
属性,指的是sql查询指令;获取 FillCommandBehavior
属性,最后调用另一个Fill方法以及清理资源并返回结果
接着我们进入到另一个Fill
方法中
分析:声明一个 IntPtr
类型的变量 intPtr
,之后调用 Bid.ScopeEnter
方法,传入 intPtr
、一个格式化字符串,以及 base.ObjectID
和 behavior
的枚举值;接着声明一个 int
类型的变量 result
,用于存储填充操作的结果,然后就是对一些参数的判断,若不满足要求就抛出异常,最后调用FillInternal
方法将结果返回给result
进入到FillInternal
方法中
分析:这是一个私有方法,这个方法的目的是执行数据库查询并将结果填充到 DataTable
数组或 DataSet
中。
首先定义一个 int
类型的局部变量 result
并初始化为 0,用于存储填充操作影响的行数;使用 flag
布尔变量检查传入的 IDbCommand
对象 command
是否没有设置连接,接着调用DbDataAdapter.GetConnection3
方法尝试获取数据库连接;使用 DbDataAdapter.QuietOpen
方法尝试打开数据库连接,并记录原始状态,如果基类的 MissingSchemaAction
属性是 AddWithKey
,则将 CommandBehavior.KeyInfo
添加到 behavior
。接着调用ExecuteReader
方法(C#语言中执行sql语句的方法),来执行sql语句。最后根据传来的datatables
参数是否为空,调用不同的Fill方法
漏洞分析总结
该漏洞的形成是因为在getDocInfo
方法中是直接将我们能够控制参数直接拼接到了sql语句中,然后经过一次次的方法调用我们追踪到了FillInternal
方法,在该方法中调用了ExecuteReader
方法进行sql语句的执行,全程没有对sql语句进行任何的过滤,进而导致了sql注入漏洞的形成
漏洞复现
poc
GET /C6/Jhsoft.Web.ask/SignUpload.ashx?token=1%3BWAITFOR+DELAY+%270%3A0%3A%201%27+--%20and%201=1_123_123&filename=1
来源:https://xz.aliyun.com/ 感谢【xhys】
原文始发于微信公众号(船山信安):金和OA SignUpload SQL注入分析
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论