Java动态加载字节码的方法
Java动态加载字节码的方法
0x01 前言
继续学习Java反序列化!!!
0x02 Java字节码
Java字节码(ByteCode)指的是Java虚拟机执行使用的一类指令,通常被存储在.class文件中。但是我们所说的”字节码”可以理解的更广义一些—-所有能够恢复成一个类并在JVM虚拟机里加载的字节序列,都在我们的讨论范围之内。比如开发者可以用类似 Scala Kotlin 这样的语言来编写代码,只要最后可以编译成 .class 文件,都可以在JVM虚拟机中运行。这得益于Java的开发思路,一次编译,终身使用,Java的classloader
是用来加载字节码文件最基本的操作,他就是一个加载器,用来告诉java虚拟机如何加载这个类。classloader
方法是根据类名来加载类的,这个类名是完整路径,如java.lang.Runtime
0x03 利用URLClassLoader加载远程class文件
URLClassLoader
类是是我们平时用的AppClassLoader
的父类
其继承关系如下图
1 |
|
常见的类加载器一共有三个
Bootstrap loader(引导类加载器)
Bootstrp
加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib
,-Xbootclasspath
参数指定的路径以及%JAVA_HOME%/jre/classes
中的类。
ExtClassLoader (扩展类加载器)
Bootstrp loader
加载ExtClassLoader
,并且将ExtClassLoader
的父加载器设置为Bootstrp loader.ExtClassLoader
是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader
,ExtClassLoader
主要加载%JAVA_HOME%/jre/lib/ext
,此路径下的所有classes目录以及java.ext.dirs
系统变量指定的路径中类库。
AppClassLoader (系统类加载器)
负责在 JVM 启动时,加载来自在命令 java 中的 classpath 或者 java.class.path 系统属性或者 CLASSPATH 操作系统属性所指定的 JAR 类包和类路径
我们来康康URLClassLoader
的工作流程
正常情况下,Java会根据配置项sun.boot.class.path和java.class.path中列举到的基础路径(这些路径是经过处理后的java.net.URL类)来寻找.class文件加载,这个基础路径分为三种情况:
- URL未以斜杠/结尾,则认为是一个JAR文件,使用JarLoader来寻找类,即为在Jar包中寻找.class文件
- URL以斜杠/结尾,且协议名是file,则使用FildLoader来寻找类,即为在本地系统中寻找.class文件
- URL以斜杠/结尾,且协议名不是file,则使用最基础的Loader来寻找类
使用Loader寻找类,最常见的就是http协议
我们可以来测试一下(测试的时候java版本不宜过高,过高会导致测试失败)
先编写一个恶意类
1 |
|
然后在恶意类所在目录起一个简陋的http服务
然后在另外一个目录编写
1 |
|
可以看到我们这里的URL是以斜杠结尾的且协议是http协议,那么就使用最基础的Loader来寻找类,这里因为我Java版本的原因,所以没有弹出计算器,就直接贴上了网上的师傅的图,滑稽.jpg
0x04 利用ClassLoader#defineClass 直接加载字节码文件
java加载class文件都可以用下面的流程来概况
1 |
|
- loadClass的作用是从已加载的类缓存,父加载器等位置寻找(双亲委派机制),在前面没有找到的情况下,执行findClass
- findClass的作用是根据URL指定的方式来加载类的字节码,可能会在本地系统,jar包或远程http服务器上读取字节码,然后将其交给defineClass
- defineClass的作用是处理前面传入的字节码,将其处理成真正的Java类
0x05 利用TemplatesImp加载字节码
那么根据上面的思路,只要我们可以控制字节码,我们就可以用来执行命令,并且getshell。
由于一般类的defineClass方法作用域是不开放的,所以攻击者很少能直接利用到它。但是TemplatesImp
的defineClass方法是可以在类外部访问的
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
这个类中定义了一个内部类TransletClassLoader
这个类重写了defineClass方法,并且这里没有显式地声明其定义域,所以默认为default, 可以被类外部调用
利用链如图
1 |
|
最前面的两个方法TemplatesImpl#getOutputProperties()
,TemplatesImpl#newTransformer()
他们的作用域是public,可以利用来写一个简单的poc
1 |
|
_bytecodes
是由字节码组成的数组,不能为空且得是由恶意类构造而成的,_name
可以任意赋值 不为空就行;defineTransletClasses()
的run方法调用了_tfactory.getExternalExtensionsMap()
,_tfactory所以需要是一个 TransformerFactoryImpl 对象,如果为空就会报错,无法往下执行;由于 TemplatesImpl 中对加载的字节码是有一定要求的,这个字节码对应的类必须是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类,所以我们构造的恶意类就需要继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 。那么恶意类如下
1 |
|
将其编译成class文件,并将其base64编码输出,生成base64 放入上面的byte[] code,运行就可以执行恶意类里面的任意命令了
TemplatesImpl 在反序列化中是非常常用的,接下来要做的几条 CC 链,ROME 链,以及 fastjson 、jackson 的漏洞里,都有它的参与
0x06 总结
今天睡了午觉起来一直感觉昏昏沉沉的,看类加载器的时候老是看不懂,到晚上脑子清醒了不少,看这些比较抽象的东西比较能看懂,果然对我来说下午不适合学习……以后得调一下自己的学习时间了
参考链接
https://www.xuxblog.top/archives/java-dong-tai-jia-zai-zi-jie-ma-de-fang-fa