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()先看TemplatesImpl#defineTransletClasses()方法:
private void defineTransletClasses()        throws TransformerConfigurationException {        if (_bytecodes == null) {            ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);            throw new TransformerConfigurationException(err.toString());        }        TransletClassLoader loader = (TransletClassLoader)            AccessController.doPrivileged(new PrivilegedAction() {                public Object run() {                    return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());                }            });        try {            final int classCount = _bytecodes.length;            _class = new Class[classCount];            if (classCount > 1) {                _auxClasses = new HashMap<>();            }            for (int i = 0; i < classCount; i++) {                _class[i] = loader.defineClass(_bytecodes[i]);//在这里调用了defineClass                final Class superClass = _class[i].getSuperclass();                // Check if this is the main class                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {                    _transletIndex = i;                }                else {                    _auxClasses.put(_class[i].getName(), _class[i]);                }            }            if (_transletIndex < 0) {                ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);                throw new TransformerConfigurationException(err.toString());            }        }        catch (ClassFormatError e) {            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);            throw new TransformerConfigurationException(err.toString());        }        catch (LinkageError e) {            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);            throw new TransformerConfigurationException(err.toString());        }    }

推荐阅读