最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java SPEL表達式注入漏洞原理解析

 更新時間:2023年10月31日 09:21:15   作者:鄭瀚Andrew  
SpEL簡稱Spring表達式語言,在Spring 3中引入,SpEL能在運行時構(gòu)建復(fù)雜表達式、存取對象圖屬性、對象方法調(diào)用等等,可以與基于XML和基于注解的Spring配置還有bean定義一起使用,本文給大家介紹Java SPEL表達式注入漏洞原理研究,感興趣的朋友一起看看吧

Java SPEL表達式注入漏洞原理研究

一、Java SpEL表達式基本原理

SpEL(Spring Expression Language)簡稱Spring表達式語言,在Spring 3中引入。

SpEL能在運行時構(gòu)建復(fù)雜表達式、存取對象圖屬性、對象方法調(diào)用等等,可以與基于XML和基于注解的Spring配置還有bean定義一起使用。

在Spring系列產(chǎn)品中,SpEL是表達式計算的基礎(chǔ),實現(xiàn)了與Spring生態(tài)系統(tǒng)所有產(chǎn)品無縫對接。Spring框架的核心功能之一就是通過依賴注入的方式來管理Bean之間的依賴關(guān)系,而SpEL可以方便快捷的對ApplicationContext中的Bean進行屬性的裝配和提取。由于它能夠在運行時動態(tài)分配值,因此可以為我們節(jié)省大量Java代碼。

SpEL有許多特性:

  • 使用Bean的ID來引用Bean
  • 可調(diào)用方法和訪問對象的屬性
  • 可對值進行算數(shù)、關(guān)系和邏輯運算
  • 可使用正則表達式進行匹配
  • 可進行集合操作

SpEL是單獨模塊,只依賴于core模塊,不依賴于其他模塊,可以單獨使用。

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>

0x1:SpEL定界符

SpEL使用#{}作為定界符,所有在大括號中的字符都將被認為是SpEL表達式,在其中可以使用SpEL運算符、變量、引用bean及其屬性和方法等。

這里需要注意#{}和${}的區(qū)別:

  • #{}就是SpEL的定界符,用于指明內(nèi)容為SpEL表達式并執(zhí)行
  • ${}主要用于加載外部屬性文件中的值
  • 兩者可以混合使用,但是必須#{}在外面,${}在里面,如#{'${}'},注意單引號是字符串類型才添加的

0x2:SpEL用法

SpEL常見的三種用法:

  • 在@Value注解中使用
  • 在XML配置中使用
  • 在代碼中創(chuàng)建Expression對象,利用Expression對象來執(zhí)行SpEL

1、在@Value注解中使用

@Configuration("testConfig11")
public class TestConfig {
    @Bean(name = "user11")
    public User user11() {
        User user = new User();
        user.setId("123");
        return user;
    }
}
@RestController
public class TestController {
    @Value("#{user11.id}")
    private String userId;
}

2、在XML配置中使用1)

示例一、字面值

最簡單的SpEL表達式就是僅包含一個字面值,下面我們在XML配置文件中使用SpEL設(shè)置類屬性的值為字面值,此時需要用到#{}定界符,注意若是指定為字符串的話需要添加單引號括起來。

直接用Spring的HelloWorld例子。

HelloWorld.java

package com.example.spring_helloworld;
public class HelloWorld {
    private String message;
    public void setMessage(String message){
        this.message  = message;
    }
    public void getMessage(){
        System.out.println("Your Message : " + message);
    }
}

SpringHelloworldApplication.java

package com.example.spring_helloworld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SpringHelloworldApplication {
    public static void main(String[] args) {
        // SpringApplication.run(SpringHelloworldApplication.class, args);
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
        obj.getMessage();
    }
}

Beans.xml:

<?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-3.0.xsd ">
    <bean id="helloWorld" class="com.example.spring_helloworld.HelloWorld">
        <property name="message" value="#{'littlehann'} is #{666}" />
    </bean>
</beans>

2)示例二、引用Bean、屬性和方法

SpEL表達式能夠通過其他Bean的ID進行引用,直接在#{}符號中寫入ID名即可,無需添加單引號括起來。如:

<!--原來的寫法,通過構(gòu)造函數(shù)實現(xiàn)依賴注入-->
<!--<constructor-arg ref="test"/>-->
<constructor-arg value="#{test}"/>

SpEL表達式也能夠訪問類的屬性,比如,carl參賽者是一位模仿高手,kenny唱什么歌,彈奏什么樂器,他就唱什么歌,彈奏什么樂器:

<bean id="kenny" class="com.spring.entity.Instrumentalist"
    p:song="May Rain"
    p:instrument-ref="piano"/>
<bean id="carl" class="com.spring.entity.Instrumentalist">
    <property name="instrument" value="#{kenny.instrument}"/>
    <property name="song" value="#{kenny.song}"/>
</bean>

key指定kenny<bean> 的id,value指定kenny<bean>的song屬性。其等價于執(zhí)行下面的代碼:

Instrumentalist carl = new Instrumentalist();
carl.setSong(kenny.getSong());

3)示例三、引用類方法

SpEL表達式還可以訪問類的方法。

假設(shè)現(xiàn)在有個SongSelector類,該類有個selectSong()方法,這樣的話carl就可以不用模仿別人,開始唱songSelector所選的歌了:

<property name="song" value="#{SongSelector.selectSong()}"/>

carl有個癖好,歌曲名不是大寫的他就渾身難受,我們現(xiàn)在要做的就是僅僅對返回的歌曲調(diào)用toUpperCase()方法:

<property name="song" value="#{SongSelector.selectSong().toUpperCase()}"/> 

3、在代碼中創(chuàng)建Expression對象,利用Expression對象來執(zhí)行SpEL

SpEL 在求表達式值時一般分為四步,其中第三步可選:

  • 首先構(gòu)造一個解析器:SpEL 使用 ExpressionParser 接口表示解析器,提供 SpelExpressionParser 默認實現(xiàn)。
  • 其次解析器解析字符串表達式:使用 ExpressionParser 的 parseExpression 來解析相應(yīng)的表達式為 Expression 對象。
  • 再次構(gòu)造上下文:準(zhǔn)備比如變量定義等等表達式需要的上下文數(shù)據(jù)。
  • 最后根據(jù)上下文得到表達式運算后的值:通過 Expression 接口的 getValue 方法根據(jù)上下文獲得表達式值。
package com.example.spring_helloworld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@SpringBootApplication
public class SpringHelloworldApplication {
    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("('Hello' + ' Littlehann').concat(#end)");
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("end", "!");
        System.out.println(expression.getValue(context));
    }
}

涉及到的主要接口如下,

  • ExpressionParser 接口:表示解析器,默認實現(xiàn)是 org.springframework.expression.spel.standard 包中的 SpelExpressionParser 類,使用 parseExpression 方法將字符串表達式轉(zhuǎn)換為 Expression 對象,對于 ParserContext 接口用于定義字符串表達式是不是模板,及模板開始與結(jié)束字符;
  • EvaluationContext 接口:表示上下文環(huán)境,默認實現(xiàn)是 org.springframework.expression.spel.support 包中的 StandardEvaluationContext 類,使用 setRootObject 方法來設(shè)置根對象,使用 setVariable 方法來注冊自定義變量,使用 registerFunction 來注冊自定義函數(shù)等等。
  • Expression 接口:表示表達式對象,默認實現(xiàn)是 org.springframework.expression.spel.standard 包中的 SpelExpression,提供 getValue 方法用于獲取表達式值,提供 setValue 方法用于設(shè)置對象值。

二、SpEL命令執(zhí)行漏洞原理分析

表達式語言主要是解析表達式為AST語法樹計算每個樹節(jié)點,當(dāng)用戶可以控制輸入的表達式時,并且繞過黑名單限制則可達到RCE。

在XML中配置使用SpEL只要修改Beans.xml中value中類類型表達式的類為Runtime并調(diào)用其命令執(zhí)行方法即可:

<?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-3.0.xsd ">
    <bean id="helloWorld" class="com.example.spring_helloworld.HelloWorld">
        <!--<property name="message" value="#{'littlehann'} is #{666}" />-->
        <property name="message" value="#{T(java.lang.Runtime).getRuntime().exec('open -a Calculator')}" />
    </bean>
</beans>

但是大多數(shù)實戰(zhàn)環(huán)境下,很多種Spring CVE漏洞都是基于Expression形式的SpEL表達式注入。

和前面XML配置的用法區(qū)別在于程序會將這里傳入parseExpression()函數(shù)的字符串參數(shù)當(dāng)初SpEL表達式來解析,而無需通過#{}符號來注明:

package com.example.spring_helloworld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@SpringBootApplication
public class SpringHelloworldApplication {
    public static void main(String[] args) {
        // 操作類彈計算器,當(dāng)然java.lang包下的類是可以省略包名的
        String spel = "T(java.lang.Runtime).getRuntime().exec(\"open -a Calculator\")";
        // String spel = "T(java.lang.Runtime).getRuntime().exec(\"calc\")";
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(spel);
        EvaluationContext context = new StandardEvaluationContext();
        System.out.println(expression.getValue(context));
    }
}

org.springframework.expression.spel.standard.SpelExpression.getValue()首先會解析生成三個AST節(jié)點,

  • java.lang.Runtime的TypeReference
  • 2個MethodReference分別是getRuntime和exec

通過SpelNodeImpl.getValue()調(diào)用CompoundExpression.getValueInternal()處理,首先通過getValueRef獲取ref,再調(diào)用ref.getValue計算最后的結(jié)果。

跟進getValueRef()看下,循環(huán)計算除前n-1個node的結(jié)果,然后調(diào)用nextNode.getValueRef(state)獲取最終的ref。

這里nextNode就是MethodReference,調(diào)用MethodReference.getValueRef()返回MethodReference$MethodValueRef實例。

跟進ref.getValue會調(diào)用getValueInternal,getValueInternal調(diào)用ReflectiveMethodExecutor.execute()通過執(zhí)行方法。

ReflectiveMethodExecutor.execute()通過反射執(zhí)行方法調(diào)用method.invoke。

參考鏈接:

https://www.mi1k7ea.com/2020/01/10/SpEL%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/
https://blog.51cto.com/u_14120/6802047
https://r17a-17.github.io/2021/11/22/Java%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5/#SpEL

三、檢測與防御方法

0x1:檢測方法

全局搜索關(guān)鍵特征:

//關(guān)鍵類
org.springframework.expression.Expression
org.springframework.expression.ExpressionParser
org.springframework.expression.spel.standard.SpelExpressionParser
//調(diào)用特征
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(str);
expression.getValue()
expression.setValue()

0x2:防御方法

最直接的修復(fù)方法是使用SimpleEvaluationContext替換StandardEvaluationContext。

官方文檔:https://docs.spring.io/spring/docs/5.0.6.RELEASE/javadoc-api/org/springframework/expression/spel/support/SimpleEvaluationContext.html 

//關(guān)鍵類
org.springframework.expression.Expression
org.springframework.expression.ExpressionParser
org.springframework.expression.spel.standard.SpelExpressionParser
//調(diào)用特征
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(str);
expression.getValue()
expression.setValue()

到此這篇關(guān)于Java SPEL表達式注入漏洞原理研究的文章就介紹到這了,更多相關(guān)Java SPEL表達式注入漏洞內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis返回類型為Map時遇到的類型轉(zhuǎn)化的異常問題

    Mybatis返回類型為Map時遇到的類型轉(zhuǎn)化的異常問題

    這篇文章主要介紹了Mybatis返回類型為Map時遇到的類型轉(zhuǎn)化的異常問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 基于Java語言的遞歸運算例題詳解

    基于Java語言的遞歸運算例題詳解

    一個方法在執(zhí)行過程中調(diào)用自身, 就稱為 "遞歸"。本文將通過幾個例題帶大家深入了解一下Java語言中的遞歸運算,感興趣的可以了解一下
    2022-08-08
  • Java調(diào)用dll文件的實現(xiàn)解析

    Java調(diào)用dll文件的實現(xiàn)解析

    這篇文章主要介紹了Java調(diào)用dll文件的實現(xiàn)解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Springboot使用CXF進行WSDL轉(zhuǎn)換及WebService服務(wù)調(diào)用過程

    Springboot使用CXF進行WSDL轉(zhuǎn)換及WebService服務(wù)調(diào)用過程

    本文介紹如何利用Apache CXF將WSDL轉(zhuǎn)換為Java代碼,提供bat命令和Maven插件兩種方法,并詳解生成類的屬性(如name、targetNamespace)及調(diào)用接口的參數(shù)傳遞方式,助力高效集成Web服務(wù)
    2025-09-09
  • Java Set簡介_動力節(jié)點Java學(xué)院整理

    Java Set簡介_動力節(jié)點Java學(xué)院整理

    Set最大的特性就是不允許在其中存放的元素是重復(fù)的。接下來通過本文給大家分享java set常用方法和原理分析,需要的的朋友參考下吧
    2017-05-05
  • Java 中的自引用詳解

    Java 中的自引用詳解

    本文介紹了Java中“自引用”概念,即類中包含指向自身類型的成員變量,常用于鏈表、樹等遞歸結(jié)構(gòu),自引用依賴于引用類型和JVM內(nèi)存模型,避免無限遞歸,支持靈活安全的數(shù)據(jù)結(jié)構(gòu)構(gòu)建,并由GC自動管理內(nèi)存,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • RabbitMQ?延遲隊列實現(xiàn)訂單支付結(jié)果異步階梯性通知(實例代碼)

    RabbitMQ?延遲隊列實現(xiàn)訂單支付結(jié)果異步階梯性通知(實例代碼)

    這篇文章主要介紹了RabbitMQ?延遲隊列實現(xiàn)訂單支付結(jié)果異步階梯性通知,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • java時區(qū)時間轉(zhuǎn)為UTC的代碼示例和詳細解釋

    java時區(qū)時間轉(zhuǎn)為UTC的代碼示例和詳細解釋

    作為一名經(jīng)驗豐富的開發(fā)者,我經(jīng)常被問到如何將Java中的時間轉(zhuǎn)換為UTC時間,這篇文章主要介紹了java時區(qū)時間轉(zhuǎn)為UTC的代碼示例和詳細解釋,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-09-09
  • Java基礎(chǔ)之代碼死循環(huán)詳解

    Java基礎(chǔ)之代碼死循環(huán)詳解

    這篇文章主要介紹了Java基礎(chǔ)之代碼死循環(huán)詳解,文中有非常詳細的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • springboot starter介紹與自定義starter示例詳解

    springboot starter介紹與自定義starter示例詳解

    Spring Boot 的 Starter 是一組比較方便的依賴描述符,可以通過 Maven 將其打成jar包,并在你的項目中直接引用,這篇文章主要介紹了springboot starter介紹與自定義starter示例,需要的朋友可以參考下
    2025-05-05

最新評論

德钦县| 广昌县| 鄄城县| 安泽县| 昭苏县| 潼关县| 平泉县| 东台市| 怀柔区| 黄浦区| 南川市| 曲周县| 长垣县| 华池县| 仁化县| 湟源县| 泗洪县| 仙居县| 郓城县| 高要市| 汤阴县| 麟游县| 长兴县| 金华市| 云和县| 土默特左旗| 瓮安县| 行唐县| 拉萨市| 呼图壁县| 息烽县| 鹤峰县| 句容市| 尖扎县| 广汉市| 富阳市| 大安市| 四川省| 淳化县| 五河县| 马尔康县|