Hibernate链分析

Hibernate

简介

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。

前置知识

一个奇怪小知识,就是在java中的Class.this和this的区别

class Outer{
    String data = "Out!";
    public class Inner{
        String data = "In!";
        public String getOuterData(){
            return Outer.this.data; // will return "Out!"
        }
    }
}

第一次见这么写接口实现的

//ValueHolder
public interface DeferredInitializer<T> {
    T initialize();
}
//TypedValue
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
    ois.defaultReadObject();
    this.initTransients();
}
private void initTransients() {
    this.hashcode = new ValueHolder(new ValueHolder.DeferredInitializer<Integer>() {
        public Integer initialize() {
            return TypedValue.this.value == null ? 0 : TypedValue.this.type.getHashCode(TypedValue.this.value);
        }
    });
}

链分析

/**
 *
 * org.hibernate.property.access.spi.GetterMethodImpl.get()
 * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue()
 * org.hibernate.type.ComponentType.getPropertyValue(C)
 * org.hibernate.type.ComponentType.getHashCode()
 * org.hibernate.engine.spi.TypedValue$1.initialize()
 * org.hibernate.engine.spi.TypedValue$1.initialize()
 * org.hibernate.internal.util.ValueHolder.getValue()
 * org.hibernate.engine.spi.TypedValue.hashCode()
 *
 *
 * Requires:
 * - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only)
 *
 * @author mbechler
 */

先看序列化返回数据的类型,是HashMap,看一下内容的存储结构

两对键值对key和value都是TypedValue类,我们又知道在HashMap反序列化的时候会自动对key执行hashCode操作,所以我们就从TypedValue的hashCode方法开始跟入

//org.hibernate.engine.spi.TypedValue
public int hashCode() {
    return (Integer)this.hashcode.getValue();
}

变量内的hashcode为ValueHolder类,这里的ValueHolder和TypedValue就是对其中的value进行了一定的包装,方便我们获取其类型和属性,我们鸡血跟入ValueHolder.getValue

//ValueHolder
public ValueHolder(DeferredInitializer<T> valueInitializer) {
    this.valueInitializer = valueInitializer;
}
public T getValue() {
    if (this.value == null) {
        this.value = this.valueInitializer.initialize();
    }
    return this.value;
}

执行了this.valueInitializer.initialize,这里的valueInitializer是在构造函数中传入的,我们继续跟入

private void initTransients() {
    this.hashcode = new ValueHolder(new ValueHolder.DeferredInitializer<Integer>() {
        public Integer initialize() {
            return TypedValue.this.value == null ? 0 : TypedValue.this.type.getHashCode(TypedValue.this.value);
        }
    });
}

进入到了TypedValue的initTransients中对ValueHolder的DeferredInitializer的实现中,调用了TypedValue.type.getHashCode(TypedValue.this.value),这里value就是构造了恶意字节码的TemplateImpl对象了,跟入

public int getHashCode(Object x) {
    int result = 17;

    for(int i = 0; i < this.propertySpan; ++i) {
        Object y = this.getPropertyValue(x, i);
        result *= 37;
        if (y != null) {
            result += this.propertyTypes[i].getHashCode(y);
        }
    }

    return result;
}
//org.hibernate.type.ComponentType
public int getHashCode(Object x) {
    int result = 17;
    for(int i = 0; i < this.propertySpan; ++i) {
        Object y = this.getPropertyValue(x, i);
        result *= 37;
        if (y != null) {
            result += this.propertyTypes[i].getHashCode(y);
        }
    }
    return result;
}
public Object getPropertyValue(Object component, int i) throws HibernateException {
    return component instanceof Object[] ? ((Object[])((Object[])component))[i] : this.componentTuplizer.getPropertyValue(component, i);
}

调用到this.componentTuplizer.getPropertyValue(恶意字节码对象,i)

//org.hibernate.tuple.component.AbstractComponentTuplizer
public Object getPropertyValue(Object component, int i) throws HibernateException {
    return this.getters[i].get(component);
}
public Object get(Object owner) {
    try {
        return this.getterMethod.invoke(owner);
    } catch (InvocationTargetException var3) {
        throw new PropertyAccessException(var3, "Exception occurred inside", false, this.containerClass, this.propertyName);
    } catch (IllegalAccessException var4) {
        throw new PropertyAccessException(var4, "IllegalAccessException occurred while calling", false, this.containerClass, this.propertyName);
    } catch (IllegalArgumentException var5) {
        LOG.illegalPropertyGetterArgument(this.containerClass.getName(), this.propertyName);
        throw new PropertyAccessException(var5, "IllegalArgumentException occurred calling", false, this.containerClass, this.propertyName);
    }
}

继续调用this.getters[i].get(恶意字节码对象),最终调用到this.getterMethod.invoke(owner)这里的getterMethod就是我们先前构造的getOutputProperties方法,传入恶意字节码对象,完成RCE