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/

相關文章
相關標籤/搜索