致远OA-pdfservlet任意文件上传分析
POC:
POST /seeyon/pdfservlet HTTP/1.1
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=1C3FAA7142D2405FABFE527C0DD51ABC;
Host: xxx.xxx.xxx.xxx
Content-type: application/x-www-form-urlencoded
Content-Length: 388
Connection: close
DBSTEP V3.0 330 0 104 DBSTEP=OKMLlKlV
OPTION=S3WYOSyMLKS6
newPdfFileId=wV66
CREATEDATE=wUghPB3szB3Xwg66
RECORDID=qLSGw4SXzLeGw4V3wUw3zUoXwid6
originalFileId=wV66
originalCreateDate=wUghPB3szB3Xwg66
FILENAME=qfTdqfTdqfTdVaxJeAJQBRl3dExQyYOdNAlfeaxsdGhiyYlTcATdN1liN4KXwiVGzfT2dEg6
needReadFile=yRWZdAS6
originalCreateDate=wLSGP4oEzLKAz4=iz=66
abcd
这个POC很早有人放出来了,但是大HW被人提交成了0day。
其实19年htmlofficeservlet出来的时候,就肯定有人去分析了那几个接口。
主要写在WEB.XML里面的servlet就那几个,但是其实这三个只是可以未授权访问,但是除了htmlofficeservlet以外,其他两个都需要认证后才可以正常使用。
最近在写一款工具,致远的漏洞集合利用工具。准备放到知识星球上去,哈哈哈哈哈。(打码的都是市面上未公开的)
补丁分析
言归正传,我这次分析的是A6-V5-V6.0SP1的,具体是V6_0SP1_2016-11-09版本的。
可以产看网站源码,从源码中查看。
我下了对应V6.0SP1的补丁,看了下,不知道我自己分析错了还是什么,我认为我的版本没有这个漏洞,我版本号比给出的补丁版本新一些。大致看了下这个补丁,相当于拦截了".."这类跨目录的符号。但是我还是不明白,为啥我版本的SAVEFILE方式跟他的不怎么一样。
补丁信息:
一、BUG编号:
二、修改问题:
HW21-0148-PdfServelt未授权RCE任意上传
三、适用版本:
影响范围:V6.0sp1_20161108、V6.1至V8.0sp2全版本
依赖版本: 适用于A8、A6、A8-N的V6.0sp1_20161108版本
数据库:所有数据库
四、操作步骤:
第一步:停止V5服务
第二步:备份文件、文件夹(注意是备份,不是将原文件、文件夹更名或删除)
1、 备份 ApacheJetspeed\webapps\seeyonseeyon-apps-common.jar
第三步:覆盖文件、文件夹
1、找到安装目录ApacheJetspeed下的webapps\seeyon\WEB-INF\lib 下的seeyon-apps-common.jar,使用解压缩工具打开(注意是打开不是解压),
然后把补丁包中seeyon-apps-common文件下的com文件 拖到打开后的seeyon-apps-common.jar里面进行合并操作。
然后关闭解压缩后的seeyon-apps-common.jar
第四步:清空A8的\Apachejetspeed\work目录下所有内容
第五步:重启V5服务
第六步:清空客户端浏览器缓存
制作日期: 2021-06-16
分析过程
但是我还是分析下这个版本,看看为啥没这个洞。
可以先使用文件监控软件,监控WEB目录。
断点处: Pdfservlert 32行
先获取了OPTION参数,通过POC来判断
OPTION=S3WYOSyMLKS6
通过解密得到
OPTION= SAVEFILE
所以它就会进到handWriteManager的saveFile方法。
紧跟着,会获取newPdfFileId这个参数。为了方便,我在402行继续下了断点。
F8下一步后获取到403行,这时候获取到的值为1,因为是我们传的。
判断了下newPdfFileId参数是否为空()
继续调试,获取到我们传的CREATEDATE参数。
用了bakOldPath变量设置一个根据创建时间设置的临时路径
filePath变量为today参数(2021/6/30/)+"/" + fileid(我们传的1);
所以filePath参数值为
C:\Seeyon\A6\base\upload\2021\06\30\1
继续走
用了个布尔型变量,方便判断是否保存成功。
429行,接收editType参数。但是这里我们没传。
431行,对editType进行判断,是否为clearDocument。这里感觉有可能可以出现个任意文件删除啥的,没去看。
来到了464行,设置了个tempFile变量,可以看到这个变量的组成。最后是用UUID结尾的,
UUIDLong的代码:
继续走,来到466行,isSuccessSave参数接收保存状态,这个时候我们可以看变量区,tempFile的值为
C:\Seeyon\A6\base\temporary\2956475605437926103
这个时候,文件已经创建成功了,只不过是在路径:
C:\Seeyon\A6\base\temporary\2956475605437926103
使用文件监控软件也发现创建了该文件,该文件是临时的。
调试来到482行,使用了CoderFactory.getInstance().encryptFile方法,来讲tempfile移到最终的目录filepath下面。
CoderFactory.getInstance().encryptFile方法。
最终上传完成,这是整个上传流程。
我们能改变的是fileId这个参数,一开始打算利用这个控制文件名跨目录的,后来发现,根本不行。
newPdfFileId进来传其他的,也会被转换成Long类型。
而Long.valueOf()方法传的不是数字string的话就会报错。
后面还会分析其他版本的pdfservlet情况,先就这样吧。
POC来自这个博客
https://www.o2oxy.cn/2891.html
但是我们公司的大佬去年就研究出来发出来了!还是很牛逼!主要那时候也没想着深入研究,就没管,最近要写个工具,想着来研究研究~