对ysoserial工具及java反序列化的一个阶段性理解

经过一段时间的琢磨与反思,以及重读了大量之前看不懂的反序列化文章,目前为止算是对java反序列化这块有了一个阶段性的小理解。

目前为止,发送的所有java反序列化的漏洞中。主要需要两个触发条件:

1、反序列化的攻击入口

2、反序列化的pop攻击链

这两个条件缺一不可。网上大量分析gadgets的文章方法,让人误以为有攻击链就可以反序列化。其实这块是有一定的误导性的。在我最初研究反序列化的时候,我觉得攻击链是最重要的。其实不然,反序列化的攻击入口才是至关重要的。因为现阶段的java环境加上java所在的金融、保险等大型项目中,往往组件jar包和jdk版本是更新最缓慢的,因为这块需要测试大量的兼容性,对大型java项目来说,攻击链基本上一定存在,除非该java程序最近才被开发、部署,且开发人员喜欢用最新的版本软件包。

0X01攻击入口

目前其实在白盒过程中要比较快的查找攻击入口的话。可以全局搜索是否有以下这类方法函数:

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

通过搜到到这些函数方法所在位置,再反向追踪是否可控输入流,如果可控,那么恭喜,反序列化的第一步已经完成。

拿我前几篇的文章来讲,XMLDecoder.readObject这块函数其实可以当做入口来看,至于为啥可以执行命令,你就当他当做system()函数来看,如果要更加深层的知道为啥可以执行命令,就可以跟下去,不过跟下去的话,属于更加底层的研究。

我们做安全其实可以分好几个攻击层次。

1、攻击普通开发者(这里的普通开发者可以指敏捷开发人员,大部分使用框架,或者现有函数库来使用,极大的加快了开发速度,但是对于框架具体底层功能实现可能不会特意去了解,大部分通过开发手册和api接口即可了解表层意义上的功能实现)

2、框架、底层库开发者(这类开发人员往往开发不是为了某个特定的东西,往往是为了帮助大量普通开发者更快、更短的代码实现一个功能,减少普通开发者的代码量,降低程序开发门槛)

3、系统层函数开发者(这类的开发者衔接的是高级面向对象的语言与底层汇编,甚至指令集之间的库函数)

随着攻击层次的加高,我们的攻击范围也会越广。

在这三个层次里,往往攻击入口点在1和2都会出现,更多的是出现在1层,而gadget在1和2也会出现,但是往往出现更多的在2层。

拿weblogic的xml反序列化来讲,把oracle的开发人员当做普通的拿现有开源框架库基础上开发的人员来看的话,他们造成反序列化的攻击入口点就在1层面,序列化的gadget在2层面。

在cve-2019-2725中,_async目录下的其实就是一个攻击入口,因为攻击入口和wls-wsat相似,所以后续分析和10271很像。

但是由于有10271补丁在,所以在2725中需要利用未在黑名单里的标签来绕过补丁。

0X02POP攻击链

java的pop攻击链不是说寻找难度不大,而是在现有yso工具在的情况下,对已有的漏洞利用来说,已经方便了很多。但是寻找一个新的pop链还是很困难的。

CommonsCollections1的gadget来说吧,因为网上这个的分析文章最多,所以也是我了解的比较熟悉的一个gadget。

这个漏洞的最关键地方是org.apache.commons.collections.functors.InvokerTransformer类下面的transform函数

public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException("InvokerTransformer: The method ‘" + this.iMethodName + "‘ on ‘" + input.getClass() + "‘ does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method ‘" + this.iMethodName + "‘ on ‘" + input.getClass() + "‘ cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method ‘" + this.iMethodName + "‘ on ‘" + input.getClass() + "‘ threw an exception", var7);
            }
        }
    }

然后更关键的地方是

 Class cls = input.getClass();
 Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
 return method.invoke(input, this.iArgs);

为啥这块危险呢,首先这个地方利用了java的反射机制,如果你不了解,你可以直接死记硬背或者理解成这样子写就是有一定问题的,等同于system(xxx),而xxx根据transform的形参来看,也就是input是我们可控的。且之间没有任何过滤。

this.iMethodName, this.iParamTypes

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

这两个读一下InvokerTransformer类的实现代码,发现初始化InvokerTransformer的时候,这两个也可控。所以这块是有可能成为POP链的。

public class InvokerTransformer implements Transformer, Serializable {

根据类的接口来看,这个类是可以序列化的,所以条件都达成。

但是这个类没有readObject的方法,所以payload还需要一个能触发这个反序列化类的地方。

根据对yso的源码查看,我发现CommonsCollections系列的readObject都是在sun.reflect.annotation.AnnotationInvocationHandler里触发readObject。

至于这个类,是在gadget.java里写死的。

public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";

在CommonsCollections1里

final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

但是目前的构造还需要依赖于触发Map中某一项去调用setValue(),我们需要想办法通过readObject()直接触发
我们观察到java运行库中有这样一个类AnnotationInvocationHandler,这个类有一个成员变量memberValues是Map类型,如下所示
\openjdk\jdk\src\share\classes\sun\reflect\annotation\AnnotationInvocationHandler.java

因此,我们只需要使用前面构造的Map来构造AnnotationInvocationHandler,进行序列化,当触发readObject()反序列化的时候,就能实现命令执行。另外需要注意的是,想要在调用未包含的package中的构造函数,我们必须通过反射的方式,综合生成任意代码执行的payload的代码如下

...待续

以上解释了如何通过Apache Commons Collections 3这个库中的代码,来构造序列化对象,使得程序在反序列化时可以立即实现任意代码执行,值得注意的是,POC之所以是通用的原因在于,只要目标系统中使用了Apache Commons Collections 3存在漏洞的代码库,并且调用了readObject(),不管具体的使用场景是怎么样的(即不管反序列化后强制转换为任何的对象),在readObject()被调用的时候攻击就已经成立了
我们可以直接使用工具ysoserial来生成payload,当中包含了4种通用的payload:Apache Commons Collections 3和4,Groovy,Spring,只要目标应用的Class Path中包含这些库,ysoserial生成的payload即可让readObject()实现任意命令执行。
ysoserial当中针对Apache Commons Collections 3的payload也是基于TransformedMap和InvokerTransformer来构造的,而在触发时,并没有采用上文介绍的AnnotationInvocationHandler,而是使用了java.lang.reflect.Proxy中的相关代码来实现触发

原文地址:https://www.cnblogs.com/ph4nt0mer/p/11898461.html

时间: 2024-10-04 19:58:49

对ysoserial工具及java反序列化的一个阶段性理解的相关文章

浅谈java反序列化工具ysoserial

前言 关于java反序列化漏洞的原理分析,基本都是在分析使用Apache Commons Collections这个库,造成的反序列化问题.然而,在下载老外的ysoserial工具并仔细看看后,我发现了许多值得学习的知识. 至少能学到如下内容: 不同反序列化payload玩法灵活运用了反射机制和动态代理机制构造POC java反序列化不仅是有Apache Commons Collections这样一种玩法.还有如下payload玩法: CommonsBeanutilsCollectionsLog

Java反序列化漏洞通用利用分析

2015年11月6日,FoxGlove Security安全团队的@breenmachine 发布的一篇博客[3]中介绍了如何利用Java反序列化漏洞,来攻击最新版的WebLogic.WebSphere.JBoss.Jenkins.OpenNMS这些大名鼎鼎的Java应用,实现远程代码执行. 然而事实上,博客作者并不是漏洞发现者.博客中提到,早在2015年的1月28号,Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上给出了

Java反序列化漏洞分析

相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 http://www.tuicool.com/articles/ZvMbIne http://www.freebuf.com/vuls/86566.html http://sec.chinabyte.com/435/13618435.shtml http://www.myhack58.com/Articl

为什么要用缓存服务器以及在 Java 中实现一个 redis 缓存服务

缓存服务的意义 为什么要使用缓存?说到底是为了提高系统的运行速度.将用户频繁访问的内容存放在离用户最近,访问速度最快的地方,提高用户的响应速度.一个 web 应用的简单结构如下图. web 应用典型架构 在这个结构中,用户的请求通过用户层来到业务层,业务层在从数据层获取数据,返回给用户层.在用户量小,数据量不太大的情况下,这个系统运行得很顺畅.但是随着用户量越来越大,数据库中的数据越来越多,系统的用户响应速度就越来越慢.系统的瓶颈一般都在数据库访问上.这个时候可能会将上面的架构改成下面的来缓解数

java中,一个简单但出错率又大的‘加法’题,1+1+'1'+1+1+1+1+"1"=?

1+1+'1'+1+1+1+1+"1"=? 结果是多少?很多人看了题之后,可能会说结果是71.  当然有的童鞋可能会说很简单,放工具里运行一下就知道结果了,如果不运行代码,你会得出一个什么样的结果呢? 如果告诉你答案是551,会迷惑么?怎么会得出551? 下面我们来看看怎么算的: 1.我们大家都知道1 .'1'."1"的区别,1 表示一个int类型,’1'是表示一个char类型,"1" 表示一个字符串类型. 2.1+1+'1'+1+1+1+1+&

批处理文件工具(java+shell命令实现)

批处理文件工具(java+shell命令实现) 有一堆语料需要处理一下才能使用,本来应该可以直接用shell脚本直接处理的. 但是对shell脚本不熟,只会简单的一些命令. 因此就利用java+shell命令实现. 也许,直接用shell脚本处理是最好的.或许你有什么绝妙的方法也请告诉我哦! 当然,我这个工具有个好处,就是如果通过shell命令实现不了的功能,可以用java实现, 添加相应接口就可以了. 工具里面的功能,Java负责调度,shell负责具体功能. 意思是说,我写的shell命令是

java模拟而一个电话本操作

哈哈,大家平时都在使用电话本,下面使用java来模拟而一个简单的电话本吧... 首先给出联系人的抽象类 package net.itaem.po; /** * * 电话人的信息 * */ public class User { private String name; private String phoneNumber; private String companyName; private String email; private String address; private Strin

主流的单元测试工具之-JAVA新特性-Annotation 写作者:组长 梁伟龙

1:什么是Annotation?Annotation,即“@xxx”(如@Before,@After,@Test(timeout=xxx),@ignore),这个单词一般是翻译成元数据,是JAVA的一个新特性. 主流的单元测试工具之-JAVA新特性-Annotation - groupthreetogether - group博客 2:元数据的简单介绍: @Before:使用了该元数据的方法在每个测试方法执行之前都要执行一次. @After:使用了该元数据的方法在每个测试方法执行之后要执行一次.

批处理文件的工具(java+shell为了实现)

批处理文件的工具(java+shell为了实现) 有一堆语料须要处理一下才干使用,本来应该能够直接用shell脚本直接处理的. 可是对shell脚本不熟,仅仅会简单的一些命令. 因此就利用java+shell命令实现. 也许,直接用shell脚本处理是最好的. 也许你有什么绝妙的方法也请告诉我哦! 当然.我这个工具有个优点,就是假设通过shell命令实现不了的功能,能够用java实现, 加入对应接口就能够了. 工具里面的功能.Java负责调度,shell负责详细功能. 意思是说,我写的shell