前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Ysoserial CommonsColletions2 两个问题

Ysoserial CommonsColletions2 两个问题

作者头像
企鹅号小编
发布2018-01-11 14:47:23
7210
发布2018-01-11 14:47:23
举报
文章被收录于专栏:企鹅号快讯企鹅号快讯

本文首发于先知安全技术社区 https://xianzhi.aliyun.com/forum/topic/1756/0x00 背景

前段时间推荐一学弟好好看看Ysoserial,中间他问了我两个问题:

1)queue为什么要先用两个1占位;

2)PriorityQueue的queue 已经使用transient关键字修饰,为什么还能从流中反序列化queue中的元素(参见CommonsCollections2的源码)

这两天有时间看了源码和序列规范,真是惭愧,误人子弟了!

在寻找答案的过程中,同时也尝试通过正向的思路去理解整个payload的构造,这个思路更加直白,感兴趣的可以看看。如果单纯想知道问题答案可以直接看0x03 问题解答

0x01 Gadget chain 分析

1)Gadget chain

2)CommonsCollections2的getObject

3)待序列化反序列化的类

既然是正向思维,自然是从反序列化的本质出发。因此,很自然第一个问题是待序列化反序列化的类是哪一个。

//java.util.PriorityQueue

4)待序列化反序列化的类它的readObject方法做了什么

正如其名,PriorityQueue是优先级队列,既然是一个有优先级的队列,必然存在区分优先级的机制--排序。

在4)中,从heapify-->siftDown-->siftDownUsingComparator

在siftDown中,如果成员comparator不为空,则调用siftDownUsingComparator(名字很直白)。那么comparator(比较器)从哪里来呢?看看PriorityQueue其中一个构造方法:

比较器可以在实例化时指定。

5)CommonsCollections2使用了什么比较器

回顾2),使用了TransformingComparator

//org.apache.commons.collections4.

comparators.TransformingComparator

siftDownUsingComparator方法调用了比较器的compare方法:

this.transformer是Transformer类型(它transform方法被调用,嗅到CommonsCollection1中熟悉的味道)。

6)Transformer具体实现类是哪一个

回顾2),使用了InvokerTransformer,当然还是熟悉的InvokerTransformer!

类比,通过将和串起来完全够用了。即:承载执行命令的;承载;对队列元素排序调用的方法触发。

不知道作者 为什么要复杂化。当然,一方面可能存在某些局限我没有发现;另一方面,更复杂的链的确需要更深的功底,不得不佩服。

(下面还是顺着复杂的继续看下去)

7)PriorityQueue队列中放置了什么元素

一开始放置了两个“1”占位,后面通过反射将其中之一换为templates(这里引出第一个问题)。跟进templates生成过程:

使用javassist修改字节码,javassist是一个使用广泛的

修改字节码的库,另外还有两个常用的库是asm和cglib。

上面代码做了几件事:

整理一下,最重要的命令执行字节码已经插入了,待序列化和反序列化的类已经准备...一切就绪,看看流程是怎么串起来。

8)回头看5),InvokerTransformer的transform方法将会被调用

再接着看defineTransletClasses

12)对象实例化,触发命令执行

获取到对象的字节码之后,就可以实例化对象了:

0x02 流程概括

PriorityQueue承载TemplatesImpl,TemplatesImpl的_bytecodes装载StubTransletPayload字节码,通过javassist修改StubTransletPayload字节码插入命令执行,PriorityQueue的排序使用比较器TransformingComparator,比较器触发InvokerTransformer的transform,transform最终触发StubTransletPayload实例化,进而造成命令执行。

0x03 问题解答

1)queue为什么要先用两个1占位?

实话说,其实我也不知道。但是我最初的说法(比较器要求元素类型一致,payload这么构造是为了防止序列化过程出现异常)肯定不严谨。

简单分析

a.泛型

泛型会不会限制?

finalPriorityQueuequeue=newPriorityQueue(2,newTransformingComparator(transformer));

PriorityQueue指定Object,1会被装箱成Integer,和templates都是Object的子类,因此这里编译不会有问题。

b.比较

i. 如果放进PriorityQueue的元素不一致,会不会在比较时出现问题呢?

ii. 看看this.decorated

iii. ComparatorUtils.NATURAL_COMPARATOR 是何物

iv. 再回头看看i中value1和value2是什么

finalInvokerTransformertransformer=newInvokerTransformer("toString",newClass[],newObject[]);

因为InvokerTransformer在初始化时已经指定toString,所以调用其transform方法就会得到String。既然都是String,比较当然没有问题!

事实上,将CommonsCollections2改造如下,也没有毛病:

所以作者为什么这么写?也许更加优雅吧。

2)PriorityQueue的queue 已经使用transient关键字修饰,为什么还能从流中反序列化queue中的元素?

成员使用transient关键字修饰,的确是为了序列化时不写入流中(该成员可能含有敏感信息,出于保护不写入)。这一点可以从序列化的文件中验证:

但是,序列化规范(https://docs.oracle.com/javase/8/docs/platform/serialization/spec/output.html#a861)允许待序列化的类实现writeObject方法,实现对自己成员序列化的控制。

PriorityQueue的确实现类writeObject方法,将队列中的元素写入流中:

正是因为如下,readObject才可以从输入流中读取队列元素

0x04 参考

http://drops.wooyun.org/papers/14317

https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html

本文来自企鹅号 - 全球大搜罗媒体

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文来自企鹅号 - 全球大搜罗媒体

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档