当前位置: 代码迷 >> 综合 >> Spring RCE漏洞分析1(CVE-2018-1270)
  详细解决方案

Spring RCE漏洞分析1(CVE-2018-1270)

热度:25   发布时间:2023-12-08 06:06:55.0

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')"};

如下图所示:
在这里插入图片描述
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();

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 参考

  1. https://www.zhihu.com/question/20215561
  2. https://github.com/spring-guides/gs-messaging-stomp-websocket
  3. https://cert.360.cn/warning/detail?id=3efa573a1116c8e6eed3b47f78723f12
  相关解决方案