【漏洞分析】spring-messaging远程代码执行漏洞分析报告(CVE-2018-1270&CVE-2018-1275)

Spring框架版本5.0-5.0.4,4.3-4.3.15存在CVE-2018-1270和CVE-2018-1275漏洞可导致远程代码执行。在引入且使用spring-messaging组件时,攻击者可通过WebSocket向服务器端发送携带有恶意代码的STOMP消息,从而导致远程代码执行攻击。

注意由于可能官方版本发布流程或代码管理上所犯低级错误,导致4.3.14-4.3.15版本升级中该漏洞所涉及文件并未更新,所以CVE-2018-1270在4.3.14版本中并未修复,就有了最新的CVE-2018-1275漏洞,并在4.3.16版本中得到了修复。

该漏洞情况请参考:【预警通告】CVE-2018-1270:spring-messaging模块远程代码执行漏洞

一、机制分析

STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议,用于服务器在客户端之间进行异步消息传递。STOMP帧由命令,一个或多个头信息、一个空行及负载(文本或字节)所组成

其中可用的COMMAND 包括:CONNECT、SEND、SUBSCRIBE、UNSUBSCRIBE、BEGIN、COMMIT、ABORT、ACK、NACK、DISCONNECT

客户端可以使用SEND命令来发送消息以及描述消息的内容,用SUBSCRIBE命令来订阅消息以及由谁来接收消息。这样就可以建立一个发布订阅系统,消息可以从客户端发送到服务器进行操作,服务器也可以推送消息到客户端。

通讯过程:

1、客户端与服务器进行HTTP握手连接

2、客户端通过发送CONNECT帧建立连接

3、服务器端接收到连接尝试返回CONNECTED帧

4、客户端通过SUBSCRIBE向服务端订阅消息主题

5、客户端通过SEND向服务端发送消息

要从浏览器连接,对于SockJS,可以使用sockjs-client。对于STOMP来说,许多应用程序都使用了jmesnil/stomp-websocket库(也称为STOMP.js),它是功能完备的,已经在生产中使用了多年,但不再被维护。目前,jsteunou/webstom-client是该库最积极维护和发展的继承者。

示例代码:

connect接受一个可选的headers参数用来标识附加的头部,默认情况下,如果没有在headers额外添加,这个库会默认构建一个独一无二的ID。用户定义的headers通常用于允许使用者在进行订阅帧中的selector来过滤基于应用程序定义的headers消息。

二、补丁分析

分析补丁情况,主要涉及文件DefaultSubscriptionRegistry.java

这里发现主要修改文件为DefaultSubscriptionRegistry,去掉了StandardEvaluationContext,改用SimpleEvaluationContext,继续查看补丁信息

可以看到补丁对比代码中使用了expression.getValue()方法,熟悉spel表达式注入的同学知道这里很可能会有问题,并且context来自于引用StandardEvaluationContext处理过的message,那接下来的分析就重点关注这里了我们找到。

三、漏洞触发分析

我们先找到expression.getValue()处下断点,查看调用栈,用到了sendMessageToSubscribers函数,也就是说客户端在发送send message时会调用getValue。

在sendMessageToSubscribers处下断点详细跟踪,发现后端通过客户端的订阅ID subId来确认具体会话,并从会话headers中获取selector值当作expression最后执行,我们说expression.getValue()存在表达式执行的风险,那么这里的headers在之前的STOMP机制介绍中有提到过,headers可以在客户端进行定义,也就是说只要我们在headers当中的selector传入poc就可以达到表达式注入的目的。

而headers根据我们之前的介绍,它会在CONNECT阶段就定义好了,也就是说这个漏洞需要两步触发:

  • 在客户端定义headers并且其中包含selector,这里传入spel,服务端和客户端建立起连接后,服务端接收到SUBSCRIBE订阅消息并获取headers
  • 客户端发送消息,服务端在当前会话中查找headers中的selector值最终执行

接下来我们根据之前的分析传入poc,并分析建立CONNECT的过程中时候获取headers中selector的整个过程

注册订阅消息,并从客户端获取headers和selector进行绑定

接下来send message的流程和之前一样,通过subId获取到会话headers中的selector,调用filterSubscription对消息进行过滤,最终到通过expression.getValue()执行了POC

四、处理建议

5.0.x版本用户升级至5.0.5

4.3.x版本用户升级至4.3.16

五、参考链接

https://pivotal.io/security/cve-2018-1270

https://pivotal.io/security/cve-2018-1275

https://stomp.github.io/stomp-specification-1.2.html

 

Spread the word. Share this post!

Meet The Author

Leave Comment