CVE-2018-1270

cve-2018-1270

STOMP协议

STOMP是一个简单的可互操作的协议, 被用于通过中间服务器在客户端之间进行异步消息传递。它定义了一种在客户端与服务端进行消息传递的文本格式.

STOMP协议与HTTP协议很相似,它基于TCP协议,使用了以下命令:

CONNECT
SEND
SUBSCRIBE
UNSUBSCRIBE
BEGIN
COMMIT
ABORT
ACK
NACK
DISCONNECT

STOMP的客户端和服务器之间的通信是通过“帧”(Frame)实现的,每个帧由多“行”(Line)组成。

第一行包含了命令,然后紧跟键值对形式的Header内容。
第二行必须是空行。
第三行开始就是Body内容,末尾都以空字符结尾。

STOMP的客户端和服务器之间的通信是通过MESSAGE帧、RECEIPT帧或ERROR帧实现的,它们的格式相似。

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

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

分析补丁

https://github.com/spring-projects/spring-framework/commit/e0de9126ed8cf25cf141d3e66420da94e350708a#diff-ca84ec52e20ebb2a3732c6c15f37d37a

把StandardEvaluationContext换成了SimpleEvaluationContext

使用了expression.getValue()猜测可能是SpEL表达式注入

删除了StandardEvaluationContext引用,采用了SimpleEvaluationContext,StandardEvaluationContext可以执行任意SpEL表达式,Spring官方在5.0.5之后换用SimpleEvaluationContext,用于实现简单的数据绑定,保持灵活性减少安全隐患

https://github.com/spring-projects/spring-framework/blob/v5.0.5.RELEASE/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java

https://github.com/spring-projects/spring-framework/blob/v5.0.5.RELEASE/spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java

漏洞分析

在expression.getValue()处设置断点

跳入继续可以看到命令被表达式处理后执行

然后再从expression.getValue()向上查看调用链

<---- filterSubscriptions(result, message) <----return findSubscriptionsInternal(destination, message);(findSubscriptions) <---- this.subscriptionRegistry.findSubscriptions(message);(sendMessageToSubscribers)

上游是sendMessageToSubscribers函数,send操作调用了getValue

在this.subscriptionRegistry.findSubscriptions处设置断点,然后继续调试

两层for循环把allMatches里的数据提取出来

然后sub.getSelectorExpression取出Selector里的数据

接着expression中的命令就被getValue触发执行了

截止到这里,完成了从发送到执行的过程
但是header是在connect阶段定义的

先通过app.js添加header并构造执行语句

在handleMessageInternal对消息进行处,跟踪header到了registerSubscription



addSubscriptionInternal(sessionId, subscriptionId, destination, message);

继续往下跟又回到了messaging/simp/broker/DefaultSubscriptionRegistry.java

可以看到header中的数据已经被赋予给selector

此时的sessionId和subsId如下图,到此connect过程就完成了,当send message的时候,就会根据sessionId和subsId来获取selector

下图就是send message操作,sessionId和subsId与上图一致

模块代码略读


http://www.cnblogs.com/davidwang456/p/4446796.html

修复方案

5.0.x users should upgrade to 5.0.5
4.3.x users should upgrade to 4.3.16
Older versions should upgrade to a supported branch

参考:
https://pivotal.io/security/cve-2018-1270
https://github.com/spring-projects/spring-framework/commit/e0de9126ed8cf25cf141d3e66420da94e350708a#diff-ca84ec52e20ebb2a3732c6c15f37d37a
http://blog.nsfocus.net/spring-messaging-analysis/
https://xz.aliyun.com/t/2252
http://www.cnblogs.com/davidwang456/p/4446796.html
https://segmentfault.com/a/1190000006617344
https://cert.360.cn/warning/detail?id=3efa573a1116c8e6eed3b47f78723f12