[备份]CVE-2018-1270分析

简介

Spring框架中通过spring-messaging模块来实现STOMP(Simple Text-Orientated Messaging Protocol),STOMP是一种封装WebSocket的简单消息协议。攻击者可以通过建立WebSocket连接并发送一条消息造成远程代码执行

STOMP

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

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

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

通讯过程:

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

漏洞复现

下载官方教程:https://github.com/spring-guides/gs-messaging-stomp-websocket 需要使用到旧版本,clone后checkout到老分支:git checkout 6958af0b02bf05282673826b73cd7a85e84c12d3

使用其中的complete项目,Gradle+SpringBoot项目,较容易搭建

修改resources/static/app.js文件(注意:这里修改app.js代码不是修改源码,appjs是返回给用户交给浏览器执行的,用户可以随意修改。之所以在代码中修改,是为了方便做复现)

访问localhost:8080后点击connect,然后随便发一个消息即可弹出计算器 img

漏洞分析

首先分析js中定义的header:selector是什么,找下STOMP协议的细节。当发送订阅命令时,Stomp支持选择器标头,该选择器充当基于内容路由的筛选器

img

点击connect后,js发送建立订阅的stomp请求,代码获取这个header的地方:org\springframework\messaging\simp\broker\DefaultSubscriptionRegistry.java

下断点分析,selector是payload。另外这里构造了一个expression,看到这个敏感词汇,大概率洞来了。这里只是初始化了expression,并没有getValue或setValue,所以RCE触发不在这里 img

当我们点击Send发送后,会调用org\springframework\messaging\simp\broker\SimpleBrokerMessageHandler.java

断点调试跟入this.subscriptionRegistry.findSubscriptions 至org/springframework/messaging/simp/broker/AbstractSubscriptionRegistry.java

层层跟入到filterSubscriptions,需要返回给用户的messagesendMessageToSubscribers传入到filterSubscriptions,这里的allMatchesfindSubscriptionsInternal中获取到的所有订阅信息,其中包含建立连接时候的selector,也就是说sub包含着payload

img

获取到的expression正是connect时构造的expression,在后面的expression.getValue处成功触发,到此分析结束,又一个SPEL的RCE

补丁分析

这个漏洞官方似乎没有修复成功,造成了CVE-2018-1275这个新漏洞,最终的修复方案如下,使用了SimpleEvaluationContext代替StandardEvaluationContext,SimpleEvaluationContext仅支持SPEL的部分功能,实现了防御