JAVA内存马
反射
先来一个简单Dog类
1 | package org.example; |
主类
1 | package org.example; |
我们写代码的时候视线知道了Dog类的信息,自然无需反射就可以使用
反射
Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。
如果你不知道类或对象的具体信息,自然也就没办法在编码阶段使用new来创建对象和使用对象。你就可以使用反射来使用Dog类。
比如在spring中,就有使用反射来动态构造类和属性的使用
反射的应用场景
- 在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息
- 比如:log4j,Servlet、SSM框架技术都用到了反射机制
反射操作
- 获取类(相关类:Class.forName)
- 根据类创建对象
a. 调用无参构造方法实例化对象
b. 调用有参构造方法创建对象 - 为对象属性赋值
- 调用对象方法
a.重载情况
反射的代码
1 | package org.example; |
序列化和反序列化
- Java序列化是指把Java对象转换为字节序列的过程便于保存在内存、文件、数据库中。
- Java反序列化是指把字节序列恢复为Java对象的过程。
序列化的应用场景
一种是将对象持久化保存到硬盘/数据库。序列化机制并不是Java语言才有的。我们知道Java对象的数据都是存放在内存中的。但内存不具备持久化特性,一旦进程关闭或设备关机,内存中的数据将永远消失。但有些场景却需要将对象持久化保存,例如用户的Session,如果Session缓存清空,用户就需要重新登陆,为了使缓存系统内存中的Session对象一直有效,就需要有一种机制将对象从内存中保存入磁盘,并且待系统重启后还能将Session对象恢复到内存中,这个过程就是对象序列化与反序列化的过程,从而避免了用户会话的有效性受系统故障的影响。
此外还有一种场景就是需要将一台主机中的对象通过网络传输给另一台机器,如RPC,RMI,网络传输等场景。
序列化相关协议
对象的反序列化技术实现并不唯一。常见的反序列化协议有:
- XML&SOAP
- JSON
- Protobuf
- Java Serializable接口
Dog类
1 | package org.example; |
Ser类,执行序列化和反序列化
1 | package org.example; |
Dog类中重写了readObject方法,并且该方法包含了恶意代码 Runtime.getRuntime().exec("calc")
- 当你调用
unserializable("ser.bin")时 - Java会读取序列化文件并开始反序列化
Dog对象 - 由于
Dog类有自定义的readObject方法,Java会调用它 - 方法中的
Runtime.getRuntime().exec("calc")执行了Windows的计算器程序
JVM类加载器
类加载器
类加载器是一个负责加载类的对象,用于实现类加载过程中的加载这一步。每个Java类都有一个引用指向加载它的 ClassLoader。
简单来说,类加载器的主要作用就是加载 Java 类的字节码(class文件)到 JVM 中(在内存中生成一个代表该类的Class对象)。字节码可以是Java源程序(java文件)经过javac编译得来,也可以是通过工具动态生成或者通过网络下载得来。
类加载器分类
BootStrapClassLoader(启动类加载器)
最顶层的加载类,由C++实现,通常表示为null,并且没有父级,主要用来加载 JDK 内部的核心类库(%JAVA_HOME%/lib目录下的rt. jar、resources. jar、charsets.jar等 jar 包和类)以及被-Xbootclasspath参数指定的路径下的所有类。
Extension ClassLoader(扩展类加载器)
主要负责加载 %JRE_HOME%/lib/ext 目录下的jar包和类以及被 java.ext.dirs 系统变量所指定的路径下的所有类
AppClassLoader(应用程序类加载器)
面向我们用户的加载器,负责加载当前应用classpath下的所有jar包和类。
自定义加载器
除了这三种类加载器之外,用户还可以加入自定义的类加载器来进行拓展,以满足自己的特殊需求。比如,我们可以对Java类的字节码(.class文件)进行加密,加载时再利用自定义的类加载器对其解密。
双亲委派模型
ClassLoader类使用委托模型来搜索类和资源。每个ClassLoader实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机中被称为“bootstrapclassloader”的内置类加载器本身没有父类加载器,但是可以作为ClassLoader实例的父类加载器。
URLClassLoader类加载器
1 | package org.example; |
实例文件
1 | public class Person { |
使用javac -encoding UTF-8 Person.java编译生成Person.class
执行自定义加载Person.class,输出
1 | 静态方法块 |
同时,URL也可以是网络地址,
自定义类加载器
1 | package org.example; |
测试自定义类加载器
1 | package org.example; |
URLDNS反序列化利用链
演示效果
用反序列化工具选择URLDNS链,输入bp的collaborator模块生成的域名,导出bin文件,在事先写好的反序列化函数中进行反序列化,bp上收到请求数据
反序列化函数
1 | package org.example; |
BP效果
介绍
随着安全防护技术的发展,传统的文件型 WebShell 越来越难以逃避检测和清理。因此,攻击者开始植入一种无文件落地的木马——内存马(MemoryShell)
内存马是一种将恶意代码直接注入到应用程序运行时内存中的技术手段。与传统的基于文件的 WebShell 不同,内存马不依赖于磁盘上的任何持久化存储,而是利用 Java、.NET等语言提供的反射机制或动态编译功能,在内存中创建并执行恶意逻辑。这种方式不仅能够绕过大多数基于文件扫描的安全防护措施,而且由于其高度的灵活性和隐蔽性,使得一旦成功植入,很难被发现和清除
常见类型
- 动态注册 servlet/filter/listener(使用servlet-api的具体实现)
- 动态注册 interceptor/controller(使用框架如spring/struts2)
- 动态注册使用职责链设计模式的中间件、框架的实现(例如Tomcat的Pipeline & Valve,Grizzly的FilterChain & Filter 等等)
- 使用java agent技术写入字节码
Servlet-API类
Servlet内存马
Servlet内存马是通过动态注册Servlet来实现的一种内存攻击手段。在JavaWeb应用中,Servlet作为处理客户端请求的核心组件之一,能够直接处理HTTP请求并返回响应。攻击者利用这一点,通过程序化地向Web容器(如Tomcat)在运行时注册恶意的Servlet对象,使得该Servlet能够在没有实际文件存在的情况下执行恶意程序
在编写我们自己的内存马之前,我们需要先搞清楚一个问题:我们写的Servlet类是怎么被装载到Web容器的
实现机制:
- 创建恶意Servlet类
- 获取context:StandardContext(通过反射)
- 从context获取Wrapper对象
- 将自己的Servlet封装进Wrapper对象
1 | <%! |