0x01 前言

我们通常把反序列化漏洞和反序列化利用链分开来看,有反序列化漏洞不一定有反序列化利用链(经常用shiro反序列化工具的人一定遇到过一种场景就是找到了key,但是找不到gadget,这也就是在这种场景下没有可利用的反序列化利用链)。如果我们向某个漏洞提交平台提交一个反序列化漏洞,但是不给反序列化利用链,那么平台大概率是不会接受这种漏洞的。

反序列化利用链是整个反序列化利用过程中最关键的一环,通常反序列化利用链需要借助常用的第三方jar包,其中最有名的就是CommonCollections利用链(简称CC链)。

往期推荐

告别脚本小子系列丨JAVA安全(1)——JAVA本地调试和远程调试技巧

告别脚本小子系列丨JAVA安全(2)——JAVA反编译技巧

告别脚本小子系列丨JAVA安全(3)——JAVA反射机制

告别脚本小子系列丨JAVA安全(4)——ClassLoader机制与冰蝎Webshell分析

告别脚本小子系列丨JAVA安全(5)——序列化与反序列化

0x02 反序列化环境准备

为了更方便初学者来学习反序列化利用链,我们首先要准备当前需要的实验环境。学习反序列化利用链最好的办法是参考ysoserial(https://github.com/frohoff/ysoserial),ysoserial是一个开源的集成化反序列化利用链工具,可以快速生成反序列化利用链payload。

如果是不追求细节的小伙伴,可以直接按照参照ysoserial使用文档来生成payload,如下图所示。

本文的主要目的是教会小伙伴们告别脚本小子,所以就不直接使用编译好的jar包,但是我们后面很多利用代码会参考ysoserial中的代码,ysoserial中关于反序列化利用链的代码都在ysoserial.payloads包中。

我们的目的是不通过ysoserial框架,自己实现相应的反序列化利用链。我们搭建反序列化测试的基础环境,新建maven项目,添加项目依赖的jar包。

为了模拟序列化和反序列化的过程,编写两个公用的静态方法,这两个方法将在后面的反序列化利用链中被多次使用。其中searialize方法的作用是把对象obj序列化之后保存到文件filename中,unserialize方法的作用是把文件filename中的数据读出来反序列化为对象。

通过读写文件来模拟序列化和反序列化的过程是最直观的实现方式,但是在实际环境中,我们遇到的都是基于POST输入的字符输入流来进行反序列化,如下图所示。本地读写文件的反序列化和基于POST输入字符的反序列化是完全没有区别的,所以我们后面在研究反序列化利用链的时候都是通过本地文件读写的方式来实现,而不会准备专门的WEB漏洞环境。

某系统反序列化漏洞实例

0x03 反序列化利用链

0x3.1 URLDNS利用链

URLDNS链是JAVA众多利用链中最简单的一条利用链,非常适合初学者研究学习,具有下面的特点:

1)利用链只依赖jdk本身提供的类,不依赖其他第三方类,所以具有很高的通用性,可以用于判断目标是否存在反序列化漏洞。

2)利用链本身只能执行域名解析的操作,不能执行系统命令或者其他恶意操作。如果向漏洞提交平台提交反序列化漏洞,但是利用链是URLDNS的利用链,那么漏洞提交平台可能会拒绝这个漏洞。

Ysoserial中关于URLDNS链的主要代码如下图3.1.1,图3.1.2所示。这里面有一个很关键的点是使用了自定义的SilentURLStreamHandler类,为什么要使用这个自定义的类,我将在后面说明原因。

图3.1.1 生成URLDNS利用链对象

图3.1.2 自定义SilentURLStreamHandler类

我们先把ysoserial的代码搬运到本地,做一名合格的搬运工。直接搬运过来之后运行payload可以查看到DNSLOG的日志,证明搬运工没有问题了。搬运过来之后需要去掉ysoserial中的反射调用,我们这里没有Reflections类,所以自己写关于反射调用的代码,如图3.1.3所示,运行结果如图3.1.4所示

图3.1.3 本地引用URLDNS利用链

图3.1.4 运行之后可以在DNSLOG查看DNS请求日志

为了理清楚利用链的整个流程,我们在java.net. URLStreamHandler类的getHostAddress方法中下断点调试一下,如图3.1.5所示。

图3.1.5 下断点调试URLDNS利用链

运行整个payload,根据debug查看栈调用情况,如图3.1.6所示。其中最关键的红色的框中的部分,可以看出整个利用链非常简单。

图3.1.6 URLDNS利用链的栈调用情况

首先反序列化入口是在HashMap的readObject,在这个方法中会调用本类的hash方法,如图3.1.7所示。

图3.1.7 在HashMap的readObject方法中调用

HashMap的hash方法

继续跟进hash方法,在这个方法中会调用key的hashCode方法。Key对应的值是java.net.URL类的对象,如图3.1.8所示。

图3.1.8 通过hash方法调用URL类的hashCode方法

继续跟进会调用java.net.URL类的hashCode方法,会调用handler字段的hashCode方法。而handler定义来自于URLStreamHandler类,所以会调用URLStreamHandler类的handler方法,如图3.1.9所示。

图3.1.9 URL类的hashCode方法调用URLStreamHandler类的hashCode方法

继续跟进java.net.URLStreamHandler类的hashCode方法,如图3.1.10所示。这里可以看出里面就直接调用了getHostAddress方法,该方法执行之后会进行域名到IP地址的解析请求。

图3.1.10 最终调用了域名解析相关的方法getHostAddress方法

到这里已经看完了整个调用栈的流程,但是还是没有找到任何理由必须要用自定义的SilentURLStreamHandler类。为了理清楚原因,我们对原来的payload进行修改,去除自定义的SilentURLStreamHandler类,如图3.1.11所示。

图3.1.11 修改后的URLDNS利用链

还是在java.net. URLStreamHandler类的getHostAddress方法中下断点。可以看到整个栈调用情况如图3.1.12所示。可以看出触发getHostAddress方法的入口点是从generalURLDNS2这个方法,而不是unserialize方法,也就是在生成序列化对象的时候就已经触发了域名解析的请求。由于JAVA内部对DNS请求存在缓存机制,那么在反序列化的时候会优先从DNS缓存中查找域名解析记录,那么反序列化的时候就收不到正确的DNS请求数据。

图3.1.12 修改后的URLDNS利用链

我们跟一下在生成payload时候触发getHostAddress的流程,其中最关键的是执行HashMap中的put方法,如图3.1.13所示。

图3.1.13 URLDNS利用链生成对象时调用HashMap的put方法

跟踪put方法的定义,如图3.1.14所示。里面会调用hash方法

图3.1.14 HashMap的put方法会调用本类的hash方法

剩下的流程和上面分析调用栈一样,最终会导致触发执行getHostAddress方法。

为了避免在生成payload的时候触发DNS请求,影响反序列化时DNS请求的执行。有两种解决办法。

1)第一种就是像ysoserial代码中的方式一样,定义一个类继承自URLStreamHandler,并且在类中重写getHostAddress等方法,使得在序列化的时候不会执行getHostAddress方法。

2)第二种办法是通过通过java.net.URL类中的hashCode方法中的逻辑来避免执行后续的getHostAddress方法。

第一种方法在ysoserial的代码中已经写很清楚了,这里主要再说一下第二种方式。在整个利用链中一个很重要的步骤是会调用java.net.URL类中的hashCode方法。如图3.1.15所示。

图3.1.15 java.net.URL类中的hashCode方法代码逻辑

这里有一个很重要的判断语句是,如果hashCode不等于-1,则直接返回对应hashCode的值,否则调用hashCode方法计算对应的值。这里需要特别注意的一点是这里的hashCode既是java.net.URL类的方法,也是java.net.URL类的字段。所以我们需要

1)第一次put的时候把hashCode字段设置为不是-1的值,避免运行下面的handler.hashCode(this)。

2)生成序列化对象的时候又需要把hashCode字段设置为-1,因为反序列化的时候需要运行下面的handler.hashCode(this)。

所以我们也可以通过反射修改hashCode字段的方式来避免在序列化的时候触发DNS请求,再次修改后的代码如图3.1.16所示。这里最主要的是增加了通过反射修改hashCode字段的操作。

图3.1.16 通过控制hashCode字段避免序列化时候触发DNS请求

这样之后也能达到和ysoserial代码一样的效果。也能正常触发URLDNS的请求,如图3.1.17所示。

图3.1.17 通过修改的payload触发URLDNS链的DNS请求

0x3.2 CC链

CC链是最早出现的影响较大的java反序列化利用链,原作者一共给出7条利用链,但是后来有很多大牛在此基础上给出了一些改进的利用链,对初学者而言,学习CC链是属于学习JAVA反序列化利用链的重要基础。

关于CC链的内容很多,限于篇幅有限,这次的文章先开头对部分CC链进行讲解,关于CC链的更多内容将在下一篇文章中详述。本次我们主要先讲关于CC链中的Transform链。

在学习Transform链之前,首先需要说明JAVA中命令执行的方式。JAVA中最典型的运行操作系统命令的办法是通过Runtime类来执行,如图3.2.1所示。

图3.2.1 JAVA中执行系统命令的方式

但是在反序列化的过程中不能直接通过Runtime来执行,因为Runtime类没有继承Serializable接口,如图3.2.2所示。

图3.2.2 Runtime类没有继承Serializable接口

但是我们可以通过反射的方式来调用Runtime类执行,并且里面涉及到的全部类都继承了Serializable接口,如图3.2.3所示。这里有一个坑是,通过反射来执行方法的返回值类型一定是Object类型,需要做强制类型转换,把Objectl类型转化为Runtime类型。后续的Transform利用链基本上都是通过执行这段代码来执行系统命令的。

图3.2.3 通过反射的方式来执行Runtime类的exec方法

严格来说Transform链属于整个CC链中的Sink点,CC链基本上都是通过Transform来最终执行系统命令的。学习Tranform链是掌握CC链的基础前提,最典型的Transform链如图3.2.4所示。

图3.2.4 典型Transform链运行情况

直接运行上面的代码是可以弹出计算器的,多数CC链最终也是通过调用类似的代码来执行系统命令。为了理解Transforml链的内容,需要分开来看里面涉及到的几个类:InvokerTransformer类、ConstantTransformer类和ChainedTransformer类。

在InvokerTransformer类中,最主要的是transform方法。该方法中通过反射的方式执行任意一个类的方式,如图3.2.5所示。Transform执行的方法必须是public修饰符的。

图3.2.5 通过InvokerTransformer类的transform方法执行任意public方法

InvokerTransformer类的transform方法提供了一种执行任意其他方法的路径,我们写一个简单的例子来帮助大家理解transform方法,如图3.2.6所示。定义一个类TEST,类中定义一个方法Hello,那么我们就可以通过tranform方法来执行TEST类的Hello方法。

图3.2.6 典型的transform方法调用

可能有的小伙伴会疑惑,我要执行Hello,为什么不直接调用,非要通过transform来调用呢?试想一下,如果我们要执行的是“Runtime.getRuntime()”这样的方法,但是整个系统中都没有任何地方直接调用了这个,那么我们是不是就可以通过transform来间接的执行这个方法呢。

但是现在通过transform来执行方法还有一个很明显的不足,就是只能执行单个对象的单个方法,不能链式调用。形象一点来说明这个问题,我们可以执行”Runtime.getRuntim()”,但是我们不能执行“Runtime.getRuntime().exec(xxxx)”。我们需要找到链式调用的方式,幸运的是CommonsCollections中提供了另一个类ChainedTransformer。

ChainedTransformer类中提供了链式调用transform方法的办法,如图3.2.7所示。ChainedTransformer类的构造方法是传入Transformer类型的数组,通过transform方法依次遍历数组中的每一个元素,上一步的方法调用的输出作为下一步的方法调用的输入,完美的链式调用解决办法。

图3.2.7 通过ChainedTransformer类链式调用transform方法

现在已经可以链式调用来执行命令了,但是还有一个问题没有解决,从图3.2.3中可以看出要执行命令,第一步是获取Runtime.class对象,如何通过某个类的transform对象来获取Runtime.class对象呢?这个时候就要用到CommonCollections包里面另一个类ChainedTransformer。

ChainedTransformer类的transform方法很简单,就是直接返回构造函数中的iConstant字段,如图3.2.8所示。这完美地解决了我们想要Runtime.class对象的愿望。

图3.2.8 通过ChainedTransformer类获取Runtime.class对象

通过上面的分析可以知道通过Transform链已经可以达到执行操作系统命令的效果,如图3.2.4所示。目前的情况是如果反序列化的时候,可以调用ChainedTransformer类的transform方法,则可以导致命令执行,但是反序列化的时候会自动调用的是readObject方法,如何通过readObject方法来调用transform方法呢?这是下一次分享的内容,未完待续。

0x04 总结

这次主要分享的是关于反序列化利用链的知识,主要介绍了URLDNS利用链和CC链中的Transform链。这些都是属于JAVA反序列化的基础知识,后续的知识都会在此基础上进行延展。从文中可以看出反序列化利用链通常包含比较复杂的逻辑知识,就算是最简单的URLDNS链也能让初学者难受,分析反序列化利用链不能太快,理解很重要。后续还有更多关于反序列化利用链的分享,欢迎交流沟通。

参考链接

https://xz.aliyun.com/t/9873

https://github.com/frohoff/ysoserial

告别脚本小子系列丨JAVA安全(6)——反序列化利用链(上)相关推荐

  1. 告别脚本小子系列丨JAVA安全(7)——反序列化利用链(中)

    0x01 前言 距离上一次更新JAVA安全的系列文章已经过去一段时间了,在上一篇文章中介绍了反序列化利用链基本知识,并阐述了Transform链的基本知识.Transform链并不是一条完整的利用链, ...

  2. 魔兽世界python脚本拍卖行_Python大法之告别脚本小子系列—信息资产收集类脚本编写(上)...

    0×01 前言 在采集到URL之后,要做的就是对目标进行信息资产收集了,收集的越好,你挖到洞也就越多了----当然这一切的前提,就是要有耐心了!!!由于要写工具较多,SO,我会分两部分写-- 0×02 ...

  3. Python大法之告别脚本小子系列——信息资产收集类脚本编写附源码

    关注头条号,私信回复资料会有意外惊喜呦------最后一张照片有资料呦. 前言 在采集到URL之后,要做的就是对目标进行信息资产收集了,收集的越好,你挖到洞也就越多了............当然这一切 ...

  4. Python大法之告别脚本小子系列—各类URL采集器编写

    本文作者:i春秋签约作家--阿甫哥哥 系列文章专辑:https://bbs.ichunqiu.com/forum.php?mod=collection&action=view&ctid ...

  5. Java学习系列(十)Java面向对象之I/O流(上)

    IO流 我们知道应用程序运行时数据是保存在内存中的,但由于内存中的数据不可持久保存(如断电或程序退出时数据会丢失),因此需要一种手段将数据写入硬盘或读入内存.面向IO流编程就是一种很好的选择.IO:I ...

  6. java 异或_脚本语言系列之Java | Java中的运算符

    本文主要介绍java中的常见运算符,如算术运算符.赋值运算符.比较运算符.逻辑运算符.位运算符.三目运算符等. 一.几个概念 int a = 3 + 4; +.=就是操作符,是算术运算符,我们还有其他 ...

  7. Java代码审计基础——RMI原理和反序列化利用链

    目录 (一)何为RMI (二). RMI的模式与交互过程 0x01 设计模式 0x02 交互过程 0x03  Stub和Skeleton (三)简单的 RMI Demo 1.Server 2.Regi ...

  8. 6-java安全——java反序列化漏洞利用链

    本篇将结合一个apache commons-collections组件来学习java反序列化漏洞原理,以及如何构造利用链. 我们知道序列化操作主要是由ObjectOutputStream类的 writ ...

  9. 脚本小子最爱的20款黑客工具

    在过去的一年里,你都用了哪些黑客工具呢?统计了全球各大网站数据(浏览量.下载量.使用量等等),为大家总结出了最受欢迎的 20 款黑客工具.涉及范围主要集中在 信息收集.Android黑客工具.自动化工 ...

最新文章

  1. 马斯克明年送3人到太空站旅游:票价3.8亿,仅剩2席,手慢无
  2. 外媒:社交媒体的大量使用与精神疾病有关
  3. LibreOJ #2006. 「SCOI2015」小凸玩矩阵 二分答案+二分匹配
  4. Windows下有关信息收集的命令
  5. Android working with volley
  6. Atitit 前端 dom 的艺术 attilax著 目录 1. 概念 1 2. 发展历程 1 2.1. 厂商各自为政 2 2.2. 1.4 制定标准 标准化 w3cdom 2 2.3. 1.4.
  7. Python读取微信朋友圈
  8. sql查询日期格式化
  9. Java程序员培训班有用吗?是否专业
  10. oracle database link
  11. 医学统计学第六版笔记
  12. matlab函数im2bw_答复同学Matlab roipoly函数的用法
  13. scala中case的用法
  14. mysql禁止空密码登录_PhpMyadmin空密码登录被禁止登录问题解决办法
  15. 小程序scroll-view 实现两行滑动金刚区,滑动进度条
  16. tableau应用——某汽车销售分析
  17. 浙江大学计算机学院 00级,浙江大学教师划分为13个等级
  18. 工作中的完美主义心态
  19. 第三份工作(一)编写中
  20. 动态修改log4net组件的日志文件名

热门文章

  1. 自定义View——弹性滑动
  2. Web安全(任意文件下载)
  3. Oracle SQL性能优化 SQL优化
  4. EXCEL中空白单元格如何快速填充为0
  5. linux手机内存碎片整理软件,Linux不需要磁盘碎片整理
  6. 【PS教程】PS照片做旧的方法,证书换照片教程
  7. ACM-ICPC 知识点 经验
  8. Python每日一练-----整数转罗马数字
  9. Input系统学习-----move事件的合并
  10. mysql自定义变量比较大小_MySQL 自定义变量@ 常用案例