Spring 全家桶各种 RCE 漏洞浅析

做者: 深信服千里目安全实验室
原文连接:https://mp.weixin.qq.com/s/gfCtSJoefYLjJpaksbKLrQ
html

1、Spring全家桶简介

Spring发展到如今,全家桶所包含的内容很是庞大,这里主要介绍其中关键的5个部分,分别是spring framework、 springboot、 spring cloud、spring security、spring mvc。 其中的spring framework就是你们经常提到的spring, 这是全部spring内容最基本的底层架构,其包含spring mvc、springboot、spring core、IOC和AOP等等。Spring mvc就是spring中的一个MVC框架,主要用来开发web应用和网络接口,可是其使用以前须要配置大量的xml文件,比较繁琐,因此出现springboot,其内置tomcat而且内置默认的XML配置信息,从而方便了用户的使用。下图就直观表现了他们之间的关系。java

而spring security主要是用来作鉴权,保证安全性的。Spring Cloud基于Spring Boot,简化了分布式系统的开发,集成了服务发现、配置管理、消息总线、负载均衡、断路器、数据监控等各类服务治理能力。git

整个spring家族有四个重要的基本概念,分别是IOC、Context、Bean和AOP。 其中IOC指控制反转,在spring中的体现就是将对象属性的建立权限回收,而后统一配置,实现解耦合,便于代码的维护。在实际使用过程当中能够经过autowired注解,不是直接指定某个类,将对象的真实类型放置在XML文件中的bean中声明,具体例子以下:github

<bean?name="WelcomeService"?class="XXX.XXX.XXX.service.impl.WelcomeServiceImpl"/>
public?class?WelcomeController?{??
????@Autowired??
????private?WelcomeService?service;??
????@RequestMapping("/welcome")??
????public?String?welcome()?{??
????????return?service.retrieveWelcomeMessage();??
????}??
}??

Spring将全部建立或者管理的对象称为bean,并放在context上下文中统一管理。至于AOP就是对各个MVC架构的衔接层作统一处理,加强了代码的鲁棒性。下面这张图就形象描述了上述基本概念。web

2、各子组件介绍

Spring发展至今,整个体系不断壮大,子分类很是庞大,这里只对本次涉及的一些组件作简单的介绍。正则表达式

首先是Spring Websocket,Spring内置简单消息代理。这个代理处理来自客户端的订阅请求,将它们存储在内存中,并将消息广播到具备匹配目标的链接客户端。Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,其主要目标是使数据库的访问变得方便快捷。Spring Data Commons是Spring Data下全部子项目共享的基础框架,Spring Data家族中的全部实现都是基于Spring Data Commons。简单点说,Spring Data REST把咱们须要编写的大量REST模版接口作了自动化实现,并符合HAL的规范。Spring Web Flow是Spring MVC的扩展,它支持开发基于流程的应用程序,能够将流程的定义和实现流程行为的类和视图分离开来。spring

3、使用量及使用分布

根据全网数据统计,使用Spring的网站多达80万余,其中大部分集中在美国,中国的使用量排在第二位。其中香港、北京、上海、广东四省市使用量最高。经过网络空间搜索引擎的数据统计和柱状图表,以下图所示。数据库

4、漏洞背景介绍(SpEL使用)

0x10 SpEL是什么

SpEL是基于spring的一个表达式语言,相似于struts的OGNL,可以在运行时动态执行一些运算甚至一些指令,相似于Java的反射功能。就使用方法上来看,一共分为三类,分别是直接在注解中使用,在XML文件中使用和直接在代码块中使用。express

0x20 SpEL能作什么

  • 基本表达式

包括逻辑运算,三目运算和正则表达式等等。bootstrap

  • 类操做表达式

对象方法调用,对象属性引用,自定义函数和类实例化等等。

  • 集合操做表达式

字典的访问,投影和修改等等。

  • 其余表达式

模板表达式

0x30 SpEL demo

0x31 基于注解的SpEL

能够结合sping的@Value注解来使用,能够直接初始化Bean的属性值

@RestController
class Sangfor {
    @Value(value = "${'aaa'.toUpperCase()}")
    private String test;
    public String getTest(){return test;}
    public void setTest(String value){this.test = value;}
}

在这种状况下能够直接将test的值初始化为AAA. 此外,还有不少其余注解的使用方式,能够结合上面提到的表达式的四种使用模式。

0x32 基于XML的SpEL

能够直接在XML文件中使用SpEL表达式以下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="world" class="java.lang.String">
        <constructor-arg value="#{' World!'}"/>
    </bean>
    <bean id="hello" class="java.lang.String">
        <constructor-arg value="#{'Hello'}#{world}"/>
    </bean>
</beans>
public class SpEL {
    public static void main(String[] args){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");
        String hello = ctx.getBean("hello", String.class);
        System.out.println(hello);
    }
}

上面的代码将会输出Hello World!, 能够看到递归往下找到world的值,最终成功返回。

0x33 字符串操做

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpEL {
    public static void main(String[] args){
        ExpressionParser parser = new SpelExpressionParser();
        // Expression exp = parser.parseExpression("'Hello '.concat('World')");
        Expression exp = parser.parseExpression("'Hello ' + 'World'");
        String message = (String) exp.getValue();
        System.out.println(message);
    }
}

注:相似的字符串操做好比toUpperCase(), substr()等等

0x34 类相关操做

使用T(class)来表示类的实例,除了java.lang的包,剩下的包须要指明。此外还能够访问类的静态方法和静态字段,甚至实例化类。

public class SpEL {
    public static void main(String[] args){
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression("T(Runtime).getRuntime().exec('calc.exe')");
        Object message = exp.getValue();
        System.out.println(message);
    }
}

如上述操做,最终就能够执行命令,弹出计算器。这也是后面SpEL RCE漏洞的利用形式。

0x35 集合相关操做

public class SpEL {
    public static void main(String[] args){
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression("{'sangfor', 'busyer', 'test'}");
        List<String> message = (List<String>) exp.getValue();
        System.out.println(message.get(1));  //busyer
    }
}

经过上面的操做,能够将字符串转化成数组,最终能够输出busyer。

0x36 SpEL原理

SpEL原理

首先来了解几个概念:

  • 表达式

能够认为就是传入的字符串内容

  • 解析器

将字符串解析为表达式内容

  • 上下文

表达式对象执行的环境

  • 根对象和活动上下文对象

根对象是默认的活动上下文对象,活动上下文对象表示了当前表达式操做的对象

具体的流程以下,其实就是编译原理里面的词法分析和句法分析:

(1)首先给定表达式1+2

(2)而后给定SpelExpressionParser解析器,该解析器就实现了上图中的分析

(3)定义上下文对象,这个是可选的,默认是StandardEvaluationContext

(4)使用表达式对象求值,例如getValue

具体代码以下:

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("{'sangfor', 'busyer', 'test'}");
//StandardEvaluationContext context = new StandardEvaluationContext();
String message = (String)exp.getValue(context, String.class);

root和this

SpEL中#root老是指的刚开始的表达式对象,而#this老是指的当前的表达式对象,用他们能够直接操做当前上下文。

SimpleEvaluationContext和StandardEvaluationContext

SimpleEvaluationContext: 不包含类相关的危险操做,比较安全

StandardEvaluationContext: 包含全部功能,存在风险

5、高危漏洞介绍

经过对Spring漏洞的收集和整理,过滤出其中影响较大的远程代码执行高危漏洞,能够得出以下列表:

漏洞名称 漏洞ID 影响版本 漏洞披露日期
Spring Websocket 远程代码执行漏洞 CVE-2018-1270 Spring Framework 5.0 - 5.0.5
Spring Framework 4.3 - 4.3.15
2018/4/5
Spring Data 远程代码执行漏洞 CVE-2018-1273 Spring Data Commons 1.13 - 1.13.10
Spring Data REST 2.6 - 2.6.10
Spring Data Commons 2.0 - 2.0.5
Spring Data REST 3.0 - 3.0.5


2018/4/10
SpringBoot 远程代码执行漏洞 CNVD-2016-04742 SpringBoot 1.1.0-1.1.12
SpringBoot 1.2.0-1.2.7
SpringBoot 1.3.0

2016/7/15
Spring Data REST 远程代码执行漏洞 CVE-2017-8046 Spring Data REST prior to 3.0.1 and Spring Boot versions prior to 1.5.9
Spring Data REST prior to 2.6.9 Spring Boot versions prior to 1.5.9
2017/9/21
Spring Web Flow 远程代码执行漏洞 CVE-2017-4971 Spring Web Flow 2.4.0 - 2.4.4
Spring Web Flow 2.4.4 - 2.4.8
2017/5/31
SpringBoot 远程代码执行漏洞 CNVD-2019-11630 Spring Boot 1-1.4
Spring Boot 2.x
2019/4/22

从上表能够看出,这些漏洞分布在Spring不一样的子分类之间,且大多都是较低的版本,用户只要及时升级高版本并及时关注新的漏洞信息。尽管近期没有出现相关漏洞,可是这些高风险漏洞依然不可忽视。这里面出现的漏洞大多不须要复杂的配置就能够直接攻击成功,从而执行任意代码,危害较大。因此,开发者在使用Spring进行开发的过程当中,必定要关注其历史风险点,尽可能规避高危漏洞,减小修改没必要要的配置信息。

6、漏洞利用链

上述漏洞基本不依赖其余Spring漏洞便可直接获取权限,下图对其利用方式作了简要概述:

7、高可利用漏洞分析

1. CVE-2018-1270

1.1 威胁等级

严重

1.2 影响范围

Spring Framework 5.0 - 5.0.5

Spring Framework 4.3 - 4.3.15

1.3 利用难度

简单

1.4 漏洞描述

在上面描述的存在漏洞的Spring Framework版本中,容许应用程序经过spring-messaging模块内存中STOMP代理建立WebSocket。攻击者能够向代理发送消息,从而致使远程执行代码攻击。

1.5 漏洞分析

点击connect,首先将触发DefaultSubscriptionRegistry.java中的addSubscriptionInternal方法,

第80行将首部的selector字段的值取出,就是咱们以前传入的恶意表达式,接着到83行,这一步就很熟悉了,使用解析器去解析表达式,显然这个时候再有一个getValue方法触发而且没有使用simpleEvaluationContext就可以直接执行咱们传入的表达式了。

监听网络流量,发现后面send信息的时候,将会将消息分发给不一样的订阅者,而且转发的消息还会包含以前connect的上下文,即这里的expression将会包含在内。

因而,尝试随便在文本框中输入一些内容,而后点击Send,最终能够触发SimpleBrokerMessageHandler.java中的sendMessageToSubscribers方法以下:

继续进入findSubscriptions方法,而且不断往下走,最终能够发如今DefaultSubscriptionRegistry.java中filterSubscriptions方法中对上下文中的expresion作了提取,并使用StandardEvaluationContext指定了上下文,也就是说这里面能够直接执行代码,没有任何限制。并最终在第164行使用getValue方法触发漏洞,弹出计算器。

1.6 补丁分析

补丁中直接将上面的StandardEvaluationContext替换成SimpleEvaluationContext,使用该方法可以避免了恶意类的加载。

2. CVE-2018-1273

2.1 威胁等级

严重

2.2 影响范围

Spring Data Commons 1.13 - 1.13.10 (Ingalls SR10)

Spring Data REST 2.6 - 2.6.10 (Ingalls SR10)

Spring Data Commons 2.0 to 2.0.5 (Kay SR5)

Spring Data REST 3.0 - 3.0.5 (Kay SR5)

2.3 利用难度

简单

2.4 漏洞描述

Spring Data Commons组件中存在远程代码执行漏洞,攻击者可构造包含有恶意代码的SPEL表达式实现远程代码攻击,直接获取服务器控制权限。

2.5 漏洞分析

从上述/users入口,最终会调用到MapPropertyAccessor静态类中对用户名进行处理。而在该类中包含了进行SpEL注入须要知足的条件以下:

  • 首先建立解析器:

  • 接着使用Standard上下文

  • 而后包含待解析表达式

  • 最后使用setValue触发

2.6 补丁分析

补丁依旧直接将StandardEvaluationContext替换成了SimpleEvaluationContext,使用该方法可以避免了恶意类的加载。

3. CNVD-2016-04742

3.1 威胁等级

严重

3.2 影响范围

Springboot 1.1.0-1.1.12

Springboot 1.2.0-1.2.7

Springboot 1.3.0

3.3 利用难度

简单

3.4 漏洞描述

低版本的springboot在处理内部500错误时,使用了spel表达式,而且递归向下解析嵌套的,其中message参数是从外部传过来的,用户就能够构造一个spel表达式,达到远程代码执行的效果。

3.5 漏洞分析

访问上面的URL,能够进入到咱们的控制器,并紧接着抛出异常以下:

进入异常的代码,通过冗长的代码调试,最终能够来到关键点的render方法:

接着进入render方法查看,这里面的replacePlaceholders方法将会进行形如${}的spel表达式替换:

进入该方法查看,最后进入parseStringValue方法,该方法会循环将带有${}的错误页面的HTML字符串中的一个个{message}是咱们传入的值。

因而能够就此构造咱们的payload,借助他的循环,继续解析spel,最终形成任意代码执行。其中,解析spel的代码以下:

3.6 补丁分析

经过添加一个NonRecursivePropertyPlaceholderHelper类,对于二次解析的值进行限制:

4. CVE-2017-8046

4.1 威胁等级

严重

4.2 影响范围

Spring Data REST prior to 3.0.1 and Spring Boot versions prior to 1.5.9

Spring Data REST prior to 2.6.9 Spring Boot versions prior to 1.5.9

4.3 利用难度

简单

4.4 漏洞描述

用户在使用PATCH方法局部更新某个值的时候,其中的path参数会被传入SpEL表达式,进而致使代码执行。

4.5 漏洞分析

执行上述payload,定位到程序的入口以下:

(注:这个类在springmvc里面,名字为JsonPatchHandler)

重点看这个三目运算,其中的判断是看HTTP方法是否为PATCH和content-type是否为咱们上面提到的那个,而后会进入this.applyPatch方法,接着根据咱们指定的replace字段进入对应的处理器:

而后实例化patchOperation,并初始化spel解析器:

最后再调用setValue触发:

4.6 补丁分析

这里用2.6.9中的修复方案举例子,在perform中不是直接setvalue,而是先作一个参数合法性校验(此处添加了SpelPath类),将path中的参数用'.'分割,而后依次判断是不是类的属性,只要有一个不是就直接报错,从而解决了上述问题,部分补丁图片以下:

5. CVE-2017-4971

5.1 威胁等级

中危

5.2 影响范围

Spring Web Flow 2.4.0 ~ 2.4.4

Spring Web Flow 2.4.4 ~ 2.4.8

5.3 利用难度

较高

5.4 漏洞描述

当用户使用Spring Web Flow受影响的版本时,若是配置了view-state,可是没有配置相应的binder,而且没有更改useSpringBeanBinding默认的false值,当攻击者构造特殊的http请求时,就能够致使SpEL表达式注入,从而形成远程代码执行漏洞。

5.5 漏洞分析

首先经过执行confirm请求,断点到以下位置:

这里能够发现能够经过判断binderConfiguration是否为空来选择进入哪一个处理方法,这里的binderConfiguration值指的是在配置文件中配置的binder内容。深刻查看这两个处理方法。其实都用了SpEL表达式,不过addModelBindings方法传入的参数的是上面提到的binder,是写死在xml文件中的,没法去更改,因此这里面就考虑当没配置binder的状况下走进addDefaultMapping方法的状况。

addDefaultMappings方法如上,其做用是遍历全部的参数,包括GET参数和POST中的参数,而后一个个判断其是否以"_"开头,若是符合就进入addEmptyValueMapping方法进行处理,不然就进入addDefaultMapping方法进行处理。本次漏洞的触发点是上面这一个,因此咱们深刻查看一下addEmptyValueMapping方法。

能够看到该方法用SpEL表达式解析了传入的变量名,并在后面使用了get操做,从而能够致使漏洞的产生。

5.6 补丁分析

查看官方补丁源码以下:

将表达式类型换成了BeanWrapperExpressionParser,由于该类型内部实现不可以处理类因此避免了该问题的发生。

然而上述还提到若是参数类型不是以"_"开头的将会进入addDefaultMapping方法,下面咱们进入该方法进行查看:

能够看到这里也对传入的参数进行了解析可是没有看到明显的get方法来触发,继续往下寻找get方法。首先这里面将解析器放入了mapper中,下面就重点追踪这个mapper的使用便可。

首先发现一步步回到以前的bind方法,能够发现最后一行对该mapper进行了操做,跟进该map方法:

在这里就进行了get操做,从而再次触发了漏洞。

对此,也可能跟这个不要紧,官方最终将全局的解析器换成SimpleEvaluationContext来完全解决此问题。

6. CNVD-2019-11630

6.1 威胁等级

严重

6.2 影响范围

Spring Boot 1-1.4

Spring Boot 2.x

6.3 利用难度

简单

6.4 漏洞描述

用户在经过env路径修改spring.cloud.bootstrap.location的位置,将该地址设置为一个恶意地址时,并在后面使用refresh接口进行触发就能够致使靶机加载恶意地址中的文件,远程执行任意代码。

6.5 漏洞分析

搭建环境并按上述方式进行攻击,并搜索到spring-cloud-context-1.2.0.RELEASE.jar中的environment和refresh,而后下断点跟进,能够发现首先的env改变会将下面体现:

其实就是将环境中该变量的属性值进行更新。

以后看一下关键点refresh接口,首先一旦refresh接口被触发,就会将有变化的信息以及一些基本信息挑选出来,以下图能够看到以前变化的值已经被挑选出来:

接着进入到addConfigFilesToEnvironment方法进行处理,先获取到全部的环境值,而后设置一个监听器,依次处理变化的信息:

这里咱们直接跳转处处理这个恶意地址的关键部分,首先进入ConfigFileApplicationListener的load方法:

这里面先判断url是否存在文件路径,若是存在才进入处理该地址,不然将name的参数设置成searchName进行处理,这里的值为“bootstrap”,后面会强行加上后缀。而后一直深刻到PropertySourcesLoader类中的load方法:

首先会发送一个head请求判断文件是否存在,以及是不是一个文件,而后会根据文件后缀来判断是否能解析,这里面就是yml文件,因此判断能够用YamlPropertySourceLoader类来处理。而后进入该类的load方法中:

在这里将会加载远程yml文件,并处理里面的内容,而致使远程代码执行的发生。

6.6 补丁分析

在springboot 1.5及之后,官方对这些接口添加了受权验证,不可以再肆意的调用他们了。

8、漏洞利用

漏洞利用视频,请转到原文观看,连接:https://mp.weixin.qq.com/s/gfCtSJoefYLjJpaksbKLrQ

9、参考连接

1.https://leokongwq.github.io/2019/04/17/spring-spel.html

2.http://rui0.cn/archives/1043

3.https://misakikata.github.io/2020/04/Spring-%E6%A1%86%E6%9E%B6%E6%BC%8F%E6%B4%9E%E9%9B%86%E5%90%88/#CNVD-2016-04742-Spring-Boot%E6%A1%86%E6%9E%B6SPEL%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E

4.https://chybeta.github.io/2018/04/07/spring-messaging-Remote-Code-Execution-%E5%88%86%E6%9E%90-%E3%80%90CVE-2018-1270%E3%80%91/

5.https://www.cnblogs.com/hac425/p/9656747.html

6.https://www.cnblogs.com/litlife/p/10183137.html

7.https://www.cnblogs.com/co10rway/p/9380441.html

8.https://github.com/spring-guides/gs-accessing-data-rest/tree/2.0.3.RELEASE

9.https://paper.seebug.org/597/

10.https://www.mi1k7ea.com/2020/02/09/%E6%B5%85%E6%9E%90Spring-WebFlow%E4%B9%8BCVE-2017-4971/


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1422/

相关文章
相关标签/搜索