Skip to content

Commit 050a183

Browse files
committed
feat: support filterProbe for WebLogic
1 parent 4a2d901 commit 050a183

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package com.reajason.javaweb.probe.payload.filter;
2+
3+
import javax.management.MBeanServer;
4+
import java.io.ByteArrayOutputStream;
5+
import java.io.PrintStream;
6+
import java.lang.management.ManagementFactory;
7+
import java.lang.reflect.Field;
8+
import java.lang.reflect.Method;
9+
import java.util.*;
10+
11+
/**
12+
* @author ReaJason
13+
*/
14+
public class WebLogicFilterProbe {
15+
16+
@Override
17+
public String toString() {
18+
String msg = "";
19+
Map<String, List<Map<String, String>>> allFiltersData = new LinkedHashMap<String, List<Map<String, String>>>();
20+
Set<Object> contexts = null;
21+
try {
22+
contexts = getContext();
23+
} catch (Throwable throwable) {
24+
msg += "context error: " + getErrorMessage(throwable);
25+
}
26+
if (contexts == null || contexts.isEmpty()) {
27+
msg += "context not found\n";
28+
} else {
29+
for (Object context : contexts) {
30+
String contextRoot = getContextRoot(context);
31+
List<Map<String, String>> filters = collectFiltersData(context);
32+
allFiltersData.put(contextRoot, filters);
33+
}
34+
msg += formatFiltersData(allFiltersData);
35+
}
36+
return msg;
37+
}
38+
39+
private List<Map<String, String>> collectFiltersData(Object context) {
40+
List<Map<String, String>> result = new ArrayList<>();
41+
try {
42+
Object filterManager = getFieldValue(context, "filterManager");
43+
Map<String, Object> filters = (Map<String, Object>) getFieldValue(filterManager, "filters");
44+
List<Object> filterPatternList = (ArrayList<Object>) getFieldValue(filterManager, "filterPatternList");
45+
if (filterPatternList == null || filterPatternList.isEmpty()) {
46+
return Collections.emptyList();
47+
}
48+
for (Object filterInfo : filterPatternList) {
49+
Map<String, String> info = new HashMap<>();
50+
Object urlMap = getFieldValue(filterInfo, "map");
51+
String filterName = (String) getFieldValue(filterInfo, "filterName");
52+
if (filterName == null) {
53+
// WebLogic 10.3.6
54+
Object[] mapValues = (Object[]) invokeMethod(urlMap, "values", null, null);
55+
filterName = ((String) mapValues[0]);
56+
}
57+
Object filterWrapper = filters.get(filterName);
58+
String filterClassName = null;
59+
try {
60+
filterClassName = (String) getFieldValue(filterWrapper, "filterClassName");
61+
} catch (NoSuchFieldException e) {
62+
// WebLogic 10.3.6
63+
filterClassName = (String) getFieldValue(filterWrapper, "filterclass");
64+
}
65+
if (filterClassName == null) {
66+
Object filter = getFieldValue(filterWrapper, "filter");
67+
if (filter != null) {
68+
filterClassName = filter.getClass().getName();
69+
}
70+
}
71+
info.put("filterName", filterName);
72+
info.put("filterClass", filterClassName);
73+
String[] urlPatterns = (String[]) invokeMethod(urlMap, "keys", null, null);
74+
info.put("urlPatterns", Arrays.toString(urlPatterns));
75+
result.add(info);
76+
}
77+
} catch (Exception e) {
78+
e.printStackTrace();
79+
}
80+
return result;
81+
}
82+
83+
@SuppressWarnings("all")
84+
private String formatFiltersData(Map<String, List<Map<String, String>>> allFiltersData) {
85+
StringBuilder output = new StringBuilder();
86+
for (Map.Entry<String, List<Map<String, String>>> entry : allFiltersData.entrySet()) {
87+
String context = entry.getKey();
88+
List<Map<String, String>> filters = entry.getValue();
89+
output.append("Context: ").append(context).append("\n");
90+
if (filters.isEmpty()) {
91+
output.append("No filters found\n");
92+
} else if (filters.size() == 1 && filters.get(0).containsKey("error")) {
93+
output.append(filters.get(0).get("error")).append("\n");
94+
} else {
95+
for (Map<String, String> info : filters) {
96+
appendIfPresent(output, "", info.get("filterName"), "");
97+
appendIfPresent(output, " -> ", info.get("filterClass"), "");
98+
appendIfPresent(output, " -> URL:", info.get("urlPatterns"), "");
99+
output.append("\n");
100+
}
101+
}
102+
}
103+
return output.toString();
104+
}
105+
106+
private void appendIfPresent(StringBuilder sb, String prefix, String value, String suffix) {
107+
if (value != null && !value.isEmpty()) {
108+
sb.append(prefix).append(value).append(suffix);
109+
}
110+
}
111+
112+
@SuppressWarnings("all")
113+
private String getContextRoot(Object context) {
114+
String r = null;
115+
try {
116+
r = (String) invokeMethod(context, "getContextPath", null, null);
117+
} catch (Exception ignored) {
118+
}
119+
String c = context.getClass().getName();
120+
if (r == null) {
121+
return c;
122+
}
123+
if (r.isEmpty()) {
124+
return c + "(/)";
125+
}
126+
return c + "(" + r + ")";
127+
}
128+
129+
130+
/**
131+
* weblogic.servlet.internal.WebAppServletContext
132+
* /opt/oracle/wls1036/server/lib/weblogic.jar
133+
* /u01/oracle/wlserver/modules/com.oracle.weblogic.servlet.jar
134+
*/
135+
public static Set<Object> getContext() throws Exception {
136+
Set<Object> webappContexts = new HashSet<Object>();
137+
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
138+
Map<String, Object> objectsByObjectName = (Map<String, Object>) getFieldValue(platformMBeanServer, "objectsByObjectName");
139+
for (Map.Entry<String, Object> entry : objectsByObjectName.entrySet()) {
140+
String key = entry.getKey();
141+
if (key.contains("Type=WebAppComponentRuntime")) {
142+
Object value = entry.getValue();
143+
Object managedResource = getFieldValue(value, "managedResource");
144+
if (managedResource != null && managedResource.getClass().getSimpleName().equals("WebAppRuntimeMBeanImpl")) {
145+
webappContexts.add(getFieldValue(managedResource, "context"));
146+
}
147+
}
148+
}
149+
try {
150+
Object workEntry = getFieldValue(Thread.currentThread(), "workEntry");
151+
Object request = null;
152+
try {
153+
Object connectionHandler = getFieldValue(workEntry, "connectionHandler");
154+
request = getFieldValue(connectionHandler, "request");
155+
} catch (Exception x) {
156+
// WebLogic 10.3.6
157+
request = workEntry;
158+
}
159+
if (request != null) {
160+
webappContexts.add(getFieldValue(request, "context"));
161+
}
162+
} catch (Throwable ignored) {
163+
}
164+
return webappContexts;
165+
}
166+
167+
@SuppressWarnings("all")
168+
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
169+
try {
170+
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
171+
Method method = null;
172+
while (clazz != null && method == null) {
173+
try {
174+
if (paramClazz == null) {
175+
method = clazz.getDeclaredMethod(methodName);
176+
} else {
177+
method = clazz.getDeclaredMethod(methodName, paramClazz);
178+
}
179+
} catch (NoSuchMethodException e) {
180+
clazz = clazz.getSuperclass();
181+
}
182+
}
183+
if (method == null) {
184+
throw new NoSuchMethodException("Method not found: " + methodName);
185+
}
186+
187+
method.setAccessible(true);
188+
return method.invoke(obj instanceof Class ? null : obj, param);
189+
} catch (Exception e) {
190+
throw new RuntimeException("Error invoking method: " + methodName, e);
191+
}
192+
}
193+
194+
@SuppressWarnings("all")
195+
public static Object getFieldValue(Object obj, String name) throws Exception {
196+
Class<?> clazz = obj.getClass();
197+
while (clazz != Object.class) {
198+
try {
199+
Field field = clazz.getDeclaredField(name);
200+
field.setAccessible(true);
201+
return field.get(obj);
202+
} catch (NoSuchFieldException var5) {
203+
clazz = clazz.getSuperclass();
204+
}
205+
}
206+
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
207+
}
208+
209+
@SuppressWarnings("all")
210+
private String getErrorMessage(Throwable throwable) {
211+
PrintStream printStream = null;
212+
try {
213+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
214+
printStream = new PrintStream(outputStream);
215+
throwable.printStackTrace(printStream);
216+
return outputStream.toString();
217+
} finally {
218+
if (printStream != null) {
219+
printStream.close();
220+
}
221+
}
222+
}
223+
}

integration-test/src/test/java/com/reajason/javaweb/integration/probe/DetectionTool.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,8 @@ public static String getGlassFishFilterProbe() {
5858
public static String getApusicFilterProbe() {
5959
return getBase64Class(ApusicFilterProbe.class);
6060
}
61+
62+
public static String getWebLogicFilterProbe() {
63+
return getBase64Class(WebLogicFilterProbe.class);
64+
}
6165
}

integration-test/src/test/java/com/reajason/javaweb/integration/probe/weblogic/WebLogic1036ContainerTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
import com.reajason.javaweb.integration.ProbeAssertion;
55
import com.reajason.javaweb.integration.VulTool;
66
import com.reajason.javaweb.integration.probe.DetectionTool;
7+
import com.reajason.javaweb.memshell.ShellTool;
8+
import com.reajason.javaweb.memshell.ShellType;
9+
import com.reajason.javaweb.packer.Packers;
10+
import com.reajason.javaweb.utils.CommonUtil;
711
import lombok.SneakyThrows;
812
import lombok.extern.slf4j.Slf4j;
913
import net.bytebuddy.jar.asm.Opcodes;
14+
import org.junit.jupiter.api.AfterAll;
1015
import org.junit.jupiter.api.Test;
1116
import org.testcontainers.containers.GenericContainer;
1217
import org.testcontainers.containers.wait.strategy.Wait;
@@ -15,9 +20,13 @@
1520

1621
import java.nio.file.Files;
1722
import java.nio.file.Paths;
23+
import java.util.List;
1824

1925
import static com.reajason.javaweb.integration.ContainerTool.getUrlFromWebLogic;
2026
import static com.reajason.javaweb.integration.ContainerTool.warFile;
27+
import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk;
28+
import static org.hamcrest.CoreMatchers.*;
29+
import static org.hamcrest.MatcherAssert.assertThat;
2130
import static org.junit.jupiter.api.Assertions.assertEquals;
2231

2332
/**
@@ -34,6 +43,11 @@ public class WebLogic1036ContainerTest {
3443
.waitingFor(Wait.forHttp("/app"))
3544
.withExposedPorts(7001);
3645

46+
@AfterAll
47+
public static void tearDown() {
48+
log.info(container.getLogs());
49+
}
50+
3751
@Test
3852
void testJDK() {
3953
String url = getUrlFromWebLogic(container);
@@ -69,4 +83,25 @@ void testBytecodeReqParamResponseBody() {
6983
String url = getUrlFromWebLogic(container);
7084
ProbeAssertion.responseBytecodeIsOk(url, Server.WebLogic, Opcodes.V1_8);
7185
}
86+
87+
@Test
88+
void testFilterProbe() {
89+
String url = getUrlFromWebLogic(container);
90+
String data = VulTool.post(url + "/b64", DetectionTool.getWebLogicFilterProbe());
91+
System.out.println(data);
92+
assertThat(data, anyOf(
93+
containsString("Context: ")
94+
));
95+
}
96+
97+
@Test
98+
void testFilterFirstInject() {
99+
String url = getUrlFromWebLogic(container);
100+
shellInjectIsOk(url, Server.WebLogic, ShellType.FILTER, ShellTool.Command, org.objectweb.asm.Opcodes.V1_6, Packers.BigInteger, container);
101+
String data = VulTool.post(url + "/b64", DetectionTool.getWebLogicFilterProbe());
102+
log.info(data);
103+
List<String> filter = ProbeAssertion.getFiltersForContext(data, "/app");
104+
String filterName = ProbeAssertion.extractFilterName(filter.get(0));
105+
assertThat(filterName, anyOf(startsWith(CommonUtil.getWebPackageNameForServer(Server.WebLogic))));
106+
}
72107
}

integration-test/src/test/java/com/reajason/javaweb/integration/probe/weblogic/WebLogic12214ContainerTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import com.reajason.javaweb.integration.ProbeAssertion;
55
import com.reajason.javaweb.integration.VulTool;
66
import com.reajason.javaweb.integration.probe.DetectionTool;
7+
import com.reajason.javaweb.memshell.ShellTool;
8+
import com.reajason.javaweb.memshell.ShellType;
9+
import com.reajason.javaweb.packer.Packers;
10+
import com.reajason.javaweb.utils.CommonUtil;
711
import lombok.SneakyThrows;
812
import lombok.extern.slf4j.Slf4j;
913
import net.bytebuddy.jar.asm.Opcodes;
@@ -16,9 +20,13 @@
1620

1721
import java.nio.file.Files;
1822
import java.nio.file.Paths;
23+
import java.util.List;
1924

2025
import static com.reajason.javaweb.integration.ContainerTool.getUrlFromWebLogic;
2126
import static com.reajason.javaweb.integration.ContainerTool.warFile;
27+
import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk;
28+
import static org.hamcrest.CoreMatchers.*;
29+
import static org.hamcrest.MatcherAssert.assertThat;
2230
import static org.junit.jupiter.api.Assertions.assertEquals;
2331

2432
/**
@@ -75,4 +83,24 @@ void testBytecodeReqParamResponseBody() {
7583
String url = getUrlFromWebLogic(container);
7684
ProbeAssertion.responseBytecodeIsOk(url, Server.WebLogic, Opcodes.V1_8);
7785
}
86+
87+
@Test
88+
void testFilterProbe() {
89+
String url = getUrlFromWebLogic(container);
90+
String data = VulTool.post(url + "/b64", DetectionTool.getWebLogicFilterProbe());
91+
System.out.println(data);
92+
assertThat(data, anyOf(
93+
containsString("Context: ")
94+
));
95+
}
96+
97+
@Test
98+
void testFilterFirstInject() {
99+
String url = getUrlFromWebLogic(container);
100+
shellInjectIsOk(url, Server.WebLogic, ShellType.FILTER, ShellTool.Command, org.objectweb.asm.Opcodes.V1_6, Packers.BigInteger, container);
101+
String data = VulTool.post(url + "/b64", DetectionTool.getWebLogicFilterProbe());
102+
List<String> filter = ProbeAssertion.getFiltersForContext(data, "/app");
103+
String filterName = ProbeAssertion.extractFilterName(filter.get(0));
104+
assertThat(filterName, anyOf(startsWith(CommonUtil.getWebPackageNameForServer(Server.WebLogic))));
105+
}
78106
}

0 commit comments

Comments
 (0)