持续补充 ...
URLCLassLoader
ClassLoader#loader(URL[])远程加载class
URLCLassLoader
实际上是我们平时默认使用的AppClassLoader
的父类,所以,我们解释URLClassLoader
的工作过程实际上就是在解释默认的Java类加载器的工作流程。
java 对路径的处理:
- URL未以斜杠 / 结尾,则认为是一个JAR文件,使用 JarLoader 来寻找类,即为在Jar包中寻 找.class文件
- URL以斜杠 / 结尾,且协议名是 file ,则使用 FileLoader 来寻找类,即为在本地文件系统中寻 找.class文件
- URL以斜杠 / 结尾,且协议名不是 file ,则使用最基础的 Loader 来寻找类
也就是协议不是file且以 / 结尾,会使用Loader寻找类,最常见的是http协议。可以通过这种方法直接加载远端的class文件,所以如果我们控制了目标Java ClassLoader的基础路径为一个http服务器,即可RCE
CLassLoader#definClass 直接加载字节码
无论加载远程class还是本地class或者jar文件,调用过程都是下面三个方法:
- loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机 制),在前面没有找到的情况下,执行 findClass
- findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像上一节中说到的,可能会在 本地文件系统、jar包或远程http服务器上读取字节码,然后交给
defineClass
- defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类
核心在defineClass,决定了如何将一段字节流转变成一个Java类,Java默认的ClassLoader#defineClass
是一个native方法,逻辑在JVM的C语言代码中。
我们无法直接外部调用这些方法来加载字节码,但是有一些库中包含了部分代码完成了这个过程,我们可以利用这些库达到目的。
加载方式一: TemplatesImpl
非常常用的Java反序列化利用链组成部分,poc:
public static void main(String[] args) throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] code = base64Decoder.decodeBuffer("");
TemplatesImpl obj = new TemplatesImpl();
Field f1 = obj.getClass().getDeclaredField("__bytecodes");
f1.setAccessible(true);
f1.set(obj, new byte[][]{code});
Field f2 = obj.getClass().getDeclaredField("__name");
f2.setAccessible(true);
f2.set(obj, "HelloTemplatesImpl");
Field f3 = obj.getClass().getDeclaredField("__tfactory");
f3.setAccessible(true);
f3.set(obj, new TransformerFactoryImpl());
obj.newTransformer();
}
注意:
设置了三个属性: _bytecodes 、 _name 和 _tfactory 。
- _bytecodes 是由字节码组成的数组;
- _name 可以是任意字符串,只要不为null即可;
- _tfactory 需要是一个 TransformerFactoryImpl 对象,因为
emplatesImpl#defineTransletClasses()
方法里有调用到tfactory.getExternalExtensionsMap()
,如果是null会出错。
另外,TemplatesImpl 中对加载的字节码是有一定要求的:这个字节码对应的类必须是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
的子类。因此需要构造一个特殊的类。
加载方式二: BCELClassLoader
BCEL Classloader去哪了
在java 8u251前可用。
使用的是com.sun.org.apache.bcel.internal.util.ClassLoader;
// 原生字节码转BCEL
JavaClass cls = Repository.lookupClass(exp.class);
String code = Utility.encode(cls.getBytes(), true);
System.out.println(code);
// 加载
new ClassLoader().loadClass("exp").newInstance();
待补充...