环境
A8+集团版V8.0SP2
补丁对比
下载官网补丁
![]() |
|---|
通过与A8+集团版V8.0SP2 中的seeyon-apps-edoc/com/seeyon/apps/govdoc/gb/util/OfdJavaZipUtil.class 对比
![]() |
|---|
漏洞分析
发现在OfdJavaZipUtil.class#unzip方法中有一处明显改动
在遍历压缩包内文件时,最新补丁做了如下改动
1 | public static void unzip(String fileName, String path, Map<String, String> charsetMap) { |
判断当前遍历的文件路径是否在指定的路径path下,如果不在则不继续执行操作。
V8.0SP2 中的unzip方法
1 | public static void unzip(String fileName, String path, Map<String, String> charsetMap) { |
在之前的版本中,并没有验证压缩包中文件路径是否存在传入的path路径中,并且在104行处
![]() |
|---|
直接将压缩包内文件名拼接到文件路径当中,然后再将压缩包内文件写入到该path+zn.getName()的新创建的文件中。
这里如果我们在压缩包内构造一个文件名为../../test.jspx文件,在没有更新补丁情况下,可以将文件写入到任意路径下。
以上,我们通过对比补丁,发现了OfdJavaZipUtil#unzip 解压文件名路径可控的漏洞利用的点,接下来,我们还需要找到fileName变量可控一条漏洞利用的路径。
查找OfdJavaZipUtil调用
![]() |
|---|
在com/seeyon/apps/govdoc/gb/manager/impl/GovdocGBManagerImpl.java#getOfdMetadata中调用了OfdJavaZipUtil#unzip
1 | private Map<String, Object> getOfdMetadata(Long fileId, File sourceFile, boolean onlyRead, Map<String, String> charsetMap) throws BusinessException { |
这个方法作用就是传入fileId和sourceFile,调用OfdJavaZipUtil.unzip对sourceFile解压,并获得解压路径下的OFD.xml中的数据。我们主要关注sourceFile变量是否可控,很显然,这个调用这个方法需要传入sourceFile。
接下来,继续看哪儿调用了com/seeyon/apps/govdoc/gb/manager/impl/GovdocGBManagerImpl.java#getOfdMetadata
![]() |
|---|
总共两个调用
1 | 1102行 |
这里主要通过1102行的GovdocGBManagerImpl.java#getOfdMetadata(Long fileId)方法来触发调用。
继续回溯
GovdocGBManagerImpl.java#getOfdMetadata(Long fileId)
1 | public Map<String, Object> getOfdMetadata(Long fileId) throws BusinessException { |
在这个方法中,通过fileId获取了对应的文件,这里获取到的文件则会传入到this.getOfdMetadata(fileId, file, true, (Map)null)中,即可作为我们上一个方法的sourceFile变量。
所以,到这里,如果fileId可以通过外部控制,我们我们只需要找到一个可以上传zip文件的地方,并且获取到fileId,我们即可实现任意文件任意路径写入
继续回溯
在com/seeyon/apps/edoc/api/EdocApiImpl.java#getOfdMetaDataFromOfdFile(Long ofdFileId)调用了getOfdMetadata(ofdFileId)方法
![]() |
|---|
继续回溯
在com/seeyon/ctp/common/content/mainbody/MainbodyController.java#invokingForm中
1 | public ModelAndView invokingForm(HttpServletRequest request, HttpServletResponse response) throws BusinessException { |
在374行调用了EdocApiImpl.java#getOfdMetaDataFromOfdFile
![]() |
|---|
在369行可以发现fileId外部可控。
经过分析,执行到Map metaDataMap = this.edocApi.getOfdMetaDataFromOfdFile(Long.parseLong(ofdFileId));需要满足几个条件。
isNew参数值不能为Null和falsesubApp只能等于2
继续回溯可发现调用到com/seeyon/ctp/common/content/mainbody/MainbodyController.java的路由/content/content.do
![]() |
|---|
熟悉致远的朋友应该都知道,通过路由调用MainbodyController类的invokingForm方法时,只需要在指定method参数为invokingForm即可
如
1 | http://xx.xx.xx.xx/seeyon/content/content.do?method=invokingForm |
到这里,我们一条漏洞利用的路线就走通了,
- 请求
http://xx.xx.xx.xx/seeyon/content/content.do?method=invokingForm- 传入zip的
fileId、isNew=ture、subApp=2。
- 传入zip的
- 调用
this.edocApi.getOfdMetaDataFromOfdFile(Long.parseLong(ofdFileId)); - 调用
this.govdocGBManager.getOfdMetadata(ofdFileId); - 调用
this.getOfdMetadata(fileId, file, true, (Map)null); - 调用
OfdJavaZipUtil.unzip(sourceFilePath, unzipFilePath, charsetMap); - 控制
fos = new FileOutputStream(path + zn.getName());中的zn.getName()为../../../../ApacheJetspeed/webapps/ROOT/mzr.jspx实现在任意文件写入。
到目前为止,我们距离getshell只完成了一半,接下来,我们还需要制作一个特殊的zip文件,并且找到一个可以上传zip,并且获得该文件得fileId的功能点。
zip制作
主要问题是在操作系统无法使用/作为文件名使用,所以,这里通过制作一个携带webshell的压缩包,通过010 editor修改文件名即可。(需要修改两处)
![]() |
|---|
![]() |
|---|
接下来,最最重要的,就是需要找到一个上传zip的地方。
那么要怎么找呢?
在com/seeyon/apps/govdoc/gb/manager/impl/GovdocGBManagerImpl.java#getOfdMetadata方法中
![]() |
|---|
通过fileId获取文件,那么是否也可以保存文件呢?
在FileManager.java接口中
![]() |
|---|
可以发现是有save接口的
接下来全局搜索fileManager.save,还挺多
![]() |
|---|
在com/seeyon/ctp/rest/resources/EditContentResource.java#saveFile()方法中
1 | public Response saveFile() throws BusinessException { |
saveFile方法中从request请求中获取了fileId、createDate、notJinge2StandardOffice等参数
![]() |
|---|
84-103行从reques请求中获取了上传的文件,并通过调用this.saveTempFile(tempFile, file)将上传的文件保存在Seeyon/A8/base/temporary/路径下
![]() |
|---|
121行会先通过fileId去查找V3XFile对象,如果没有则重新创建,再设置fileId、category等属性值。
![]() |
|---|
最后161行调用了this.fileManager.save(v3xfile);
![]() |
|---|
通过该接口即可上传我们的zip文件,并且fileId是我们可以控制的。
请求该接口的路径为
1 | http://xx.xx.xx.xx/seeyon/rest/editContent/saveFile |
漏洞利用
漏洞利用脚本
1 | import requests |
![]() |
|---|
![]() |
|---|


















