Java安全之动态加载字节码( 二 )

里面是Hello.class的base64编码
注意:在 defineClass 被调用的时候,类对象是不会被初始化的,只有这个对象显式地调用其构造函数,初始化代码才能被执行 。而且,即使我们将初始化代码放在类的static块中,在 defineClass 时也无法被直接调用到 。所以,如果我们要使用 defineClass 在目标机器上执行任意代码,需要想办法调用构造函数 。

Java安全之动态加载字节码

文章插图
因为系统的 ClassLoader#defineClass 是一个保护属性,所以我们无法直接在外部访问,不得不使用反射的形式来调用 。在实际场景中,因为defineClass方法作用域是不开放的,所以攻击者很少能直接利用到它,但它却是我们常用的一个攻击链 TemplatesImpl 的基石 。
利用TemplatesImpl加载字节码前面分析了defineClass方法并不好直接利用,但是Java底层还是有一些类用到了它,这就是 TemplatesImpl ,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 这个类中定义了一个内部类TransletClassLoader :
static final class TransletClassLoader extends ClassLoader {    private final Map<String,Class> _loadedExternalExtensionFunctions;     TransletClassLoader(ClassLoader parent) {         super(parent);        _loadedExternalExtensionFunctions = null;    }    TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) {        super(parent);        _loadedExternalExtensionFunctions = mapEF;    }    public Class<?> loadClass(String name) throws ClassNotFoundException {        Class<?> ret = null;        // The _loadedExternalExtensionFunctions will be empty when the        // SecurityManager is not set and the FSP is turned off        if (_loadedExternalExtensionFunctions != null) {            ret = _loadedExternalExtensionFunctions.get(name);        }        if (ret == null) {            ret = super.loadClass(name);        }        return ret;     }    /**     * Access to final protected superclass member from outer class.     */    Class defineClass(final byte[] b) {        return defineClass(null, b, 0, b.length);    }}这个类里重写了 defineClass 方法,并且这里没有显式地声明其定义域 。Java中默认情况下,如果一个方法没有显式声明作用域,其作用域为default 。所以也就是说这里的defineClass 由其父类的protected类型变成了一个default类型的方法,可以被类外部调用 。
从 TransletClassLoader#defineClass() 向前追溯一下调用链:
TransletClassLoader#defineClass()-> TemplatesImpl#defineTransletClasses()-> TemplatesImpl#getTransletInstance()-> TemplatesImpl#newTransformer()-> TemplatesImpl#getOutputProperties()

经验总结扩展阅读