Spring 框架RCE 安全漏洞及解决方式

阅读  ·  发布日期 2022-06-03

0x00 前言

趁着五一休息,分析了一下Spring的历史漏洞,这里记录一下对Spring-Messaging远程代码执行漏洞(CVE-2018-1270)的分析。该漏洞涉及到如下概念:

1、WebSocket协议,是HTML5提供的一种可在单个TCP连接上进行全双工通讯的协议。即允许服务端主动向客户端推送数据,详情可以参考这里,讲的挺好的。

2、SockJS,一个JavaScript库,它在浏览器和Web服务器之间创建了一个低延迟、全双工、跨域通信通道。另外由于WebSocket是HTML5新增的特性,一些浏览器可能不支持,因此回退机制就很必要。SockJS就提供了该功能,即优先使用WebSocket,当浏览器不支持WebSocket时,会自动降为轮询或其他方式。

3、STOMP(Simple Text Orientated Messaging Protocol),简单面向文本的消息传递协议,可以理解为它是对WebSocket协议的封装,可以类比http与tcp的关系。


0x01 环境搭建

使用Spring提供的基于WebSocket协议构建交互式Web应用程序的demo。

1、将其下载到本地,如下的complete文件夹下是一个完整的SpringBoot项目,可以使用Maven或Gradle的方式在本地构建该项目:


2、将complete文件夹导入到IDEA中,我用的Maven构建的项目,等到依赖都下载完后,就可以运行项目了,项目不大,其中的每个类的作用在github上都有说明,这里就不赘述了:



0x02 漏洞利用

这里参考的这篇文章。

该漏洞影响的Spring的版本如下:

Spring Framework 5.0 to 5.0.4

Spring Framework 4.3 to 4.3.14

1、由于在2.2.2.RELEASE版本的SpringBoot中,使用的Spring版本为5.2.2.RELEASE,因此需修改上面环境中的SpringBoot版本为2.0.0.RELEASE:


这时对应的Spring的版本为5.0.4.RELEASE。

2、在static/app.js文件中的connect方法中添加如下代码:


var header = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"};

1

如下图所示:


T(java.lang.Runtime).getRuntime().exec('calc.exe')是个Spring表达式,可通过如下方式来解析SpEL:


String selector = "T(java.lang.Runtime).getRuntime().exec('calc.exe')";

Expression expression = new SpelExpressionParser().parseExpression(selector);

expression.getValue();

1

2

3

3、访问该项目,先点击Connect按钮与服务端建立连接,这时selector头也发送到了服务端:


4、发送消息与服务端通信时,之前发送的Spring表达式会被解析,从而造成rce。



0x03 漏洞原理

1、从上面参考的这篇文章知道,解析SpEL发生在org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#filterSubscriptions方法中,查看该方法发现只有selectorHeaderInUse属性值为true时,才能执行后续逻辑,但该属性值默认为false:


2、通过搜索发现在org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#addSubscriptionInternal方法中将selectorHeaderInUse属性值设置成了true,于是在如下位置设置断点,当在页面点击Connect按钮时,会执行到该断点处:


从上面的代码可以看到sessinId(“txaqe0mm”)、subsId(“sub-0”)、订阅地址(“/topic/greetings”)、selector值(“T(java.lang.Runtime).getRuntime().exec(‘calc.exe’)”)均被使用addSubscription方法加入到了this.subscriptionRegistry属性中。

3、将断点放行,在org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#filterSubscriptions方法中设置断点,在页面点击Send按钮往服务端发送消息,程序则会运行至该断点处,如下先从参数中拿到sessionId(“txaqe0mm”):


4、然后根据该sessionId(“txaqe0mm”)值***终从this.subscriptionRegistry属性中获取到了前面用addSubscription方法添加进去的那些信息:


5、获取到前面添加的使用selector值(“T(java.lang.Runtime).getRuntime().exec(‘calc.exe’)”)创建的Expression对象,在执行该对象的getValue方法时,表达式即被解析:


需要注意到上图中执行expression.getValue方法时传入的EvaluationContext对象类型为StandardEvaluationContext,该Context类型的对象支持执行任意SpEL表达式。


0x04 漏洞修复

1、修改SpringBoot的版本为2.0.1.RELEASE,其对应的Spring版本为5.0.5.RELEASE,在该版本中漏洞已被修复:


2、查看org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#filterSubscriptions方法中解析SpEL处代码如下:


可以看到调用expression.getValue方法时传入的Context对象类型替换为了SimpleEvaluationContext:


3、通过调试***终发现在org.springframework.expression.spel.ExpressionState#findType方法中获取SpEL表达式中的被解析的类的类型(本例中为java.lang.Runtime)时,会调用SimpleEvaluationContext类的findType方法:


4、该findType方法的实现即为下图所示的Lambda表达式,可以看到只有一个抛出异常的操作,因此在使用SimpleEvaluationContext后,即阻止了在SpEL表达式中对java.lang.Runtime、java.lang.ProcessBuilder等类的解析:



0x05 参考

https://www.zhihu.com/question/20215561

https://github.com/spring-guides/gs-messaging-stomp-websocket

https://cert.360.cn/warning/detail?id=3efa573a1116c8e6eed3b47f78723f12

————————————————

版权声明:本文为CSDN博主「hldfight」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qsort_/article/details/105906256