CVE-2022-26134 Confluence OGNL RCE 漏洞深入分析和高版本绕过沙箱实现命令回显
2022-6-5 18:52:48 Author: mp.weixin.qq.com(查看原文) 阅读量:469 收藏

漏洞信息

最近 Confluence 官方通报了一个严重漏洞 CVE-2022-26134 :

从漏洞描述来看,这仍然是一个 OGNL 表达式注入漏洞。影响版本如下:

  •   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

补丁描述:

主要是修改了 `xwork-1.0.3-atlassian-10.jar` 。下面将深入分析漏洞原理,并尝试绕过沙箱构造命令执行结果回显。

漏洞分析

新版本主要是修改了 `xwork-1.0.3-atlassian-10.jar` 。首先简单进行一下补丁对比:

改动的地方很多,但是最关键的地方位于 `ActionChainResult#execute` 函数,对提取 `finalNamespace` 和 `finalActionName` 的过程进行了更新。

Confluence 基于 Struts 架构进行开发。我们首先以登录请求为例,对 Confluence 请求处理的流程进行动态调试。访问 `/login.action` ,经过一系列 `Filter` 处理后,将进入Servlet 的分发器 `ServletDispatcher` (本质上是其子类 `ConfluenceServletDispatcher` 对象):

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {    try {        if (this.paramsWorkaroundEnabled) {            request.getParameter("foo");        }
request = this.wrapRequest(request); this.serviceAction(request, response, this.getNameSpace(request), this.getActionName(request), this.getRequestMap(request), this.getParameterMap(request), this.getSessionMap(request), this.getApplicationMap()); } catch (IOException var5) { String message = "Could not wrap servlet request with MultipartRequestWrapper!"; log.error(message, var5); this.sendError(request, response, 500, new ServletException(message, var5)); }}

分别通过函数 `getNameSpace` 、 `getActionName` 、 `getRequestMap` 、 `getSessionMap` 、 `getApplicationMap` 提取相应参数,对应关系如下:

  •   `getNameSpace(request)` -> `namespace`

  •   `getActionName(request)`  -> `actionName`

  •   `getRequestMap(request)` -> `requestMap`

  •   `getParameterMap(request)` -> `parameterMap`

  •   `getSessionMap(request)` -> `sessionMap`

  •   `getApplicationMap()` -> `applicationMap`

然后调用 `serviceAction` 函数,进入子类 `ConfluenceServletDispatcher` 的 `serviceAction` 函数:

实例化 `DefaultActionProxy` 对象,调用其 `execute` 函数:

进入 `DefaultActionInvocation#invoke` :

这里开始调用 Struts Interceptor 拦截器对象对请求进行处理, `DefaultActionInvocation` 对象拦截器集合 `interceptors` 一共有 32 个:

函数 `invoke` 尝试通过 `next` 获取下一个拦截器对象,然后调用其 `intercept` 方法,大部分 `Interceptor` 对象的 `intercept` 函数格式如下所示:

继续调用 `DefaultActionInvocation#invoke` ,从而形成迭代循环。但是在调试中我们发现也有特殊的一些 `Interceptor` ,比如 `ConfluenceAccessInterceptor` :

当满足一定条件时并不会继续调用 `DefaultActionInvocation#invoke` ,而是返回字符串 `notpermitted` ,我们分析一下 `isAccessPermitted` 函数:

主要是通过请求的 `*.action` 和 `methdName` ,来判断当前用户 `currentUser` 是否有访问权限。也就是说,当访问一个无权访问的 `*.action` 时,`DefaultActionInvocation#invoke` 在迭代调用到 `ConfluenceAccessInterceptor#intercept` 后,将返回 `notpermitted`  并赋值给 `resultCode`,从而跳出迭代。我们替换测试请求为 `/index.action`:

当处理到 `ConfluenceAccessInterceptor` 拦截器时,将不会继续迭代调用下一个拦截器,而是继续往下走,进入 `executeResult` 函数:

进入 `ActionChainResult#execute` :

提取 `namespace` 参数,并调用 `translateVariables` 函数,进入:

典型的 OGNL 表达式解析过程,前面分析中可知, `namespace` 参数通过 `ServletDispatcher#getNameSpace` 函数获取,查看定义:

可见 `namespace` 取值为请求 `servletPath` 最后一个 `/` 之前的部分。

根据上面正则表达式规则,要想触发 OGNL 解析,我们很容易构造出相应的 URL :

成功触发 OGNL 表达式注入。

沙箱绕过与命令执行

网上现在已经公开的一些利用方式简单粗暴,只能针对 v7.14 及以下系列有效,因为从 v7.15 系列开始,Confluence 在 OGNL 表达式解析时加入了沙箱设置:

进入 `isSafeExpression` 函数:

主要的黑名单如下:

(1) `unsafePropertyNames`

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`

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`

0 = "getClass"1 = "getClassLoader"

白名单 `allowedClassNames` 如下:

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` 函数 ):

小伙伴看到这里,应该很容易想到多种绕过的方法,感兴趣的小伙伴可以进入漏洞空间站进行交流。其中一种实现命令执行结果回显的方式如下(适用全部受影响的版本):

往期精彩文章
MSSQL不出网文件落地上线方式
Windows支持诊断工具(MSDT)远程代码执行漏洞(CVE-2022-30190)分析复现/修复
团队招人进行时!期待优秀的你加入
新华三magicR100存在未授权RCE攻击
MSF监听之加密流量下的后门上线
技术支持:白帽子社区团队
— 扫码关注我们 

文章来源: http://mp.weixin.qq.com/s?__biz=MzUyMTAyODYwNg==&mid=2247500039&idx=1&sn=ec67a7f789a4a8a3d529e502215c74e7&chksm=f9e3d796ce945e8029194a0ee0f5aefbe5de8094cbf4f59182f77db6a448f8648e6b076ad643&mpshare=1&scene=1&srcid=0605N7uTTz9zF3ZupEkqtZo9&sharer_sharetime=1654426357840&sharer_shareid=205c037363a9188e37dfb6bb4436f95b#rd
如有侵权请联系:admin#unsafe.sh