简单介绍

大家都知道JDK高版本后,限制远程Reference加载的属性是trustURLCodebase

但它其实没有看上去那么简单,具体限制分为两部分

系统属性

第一个是可以用System.setProperty改的系统属性**com.sun.jndi.ldap.object.trustURLCodebase**

其带来的限制较前,而且会在目标控制台弹出警告

什么是系统属性?

使用System.setProperty

分别设置com.sun.jndi.ldap.object.trustURLCodebase/com.sun.naming.internal.VersionHelper12 两个系统属性为ture

然而com.sun.naming.internal.VersionHelper12类实例属性仍然为false

系统属性是特殊的全局变量,和具体的类属性无关,也只能通过System.set/get…..的方式来设置/获取

类私有属性

另一个是**com.sun.naming.internal.VersionHelper12中的trustURLCodebase**

private static final修饰,String类型,初始化为**com.sun.jndi.ldap.object.trustURLCodebase**,Spring和Tomcat下,其类初始化发生在Web应用启动之时

最终影响loadClass是否生效,拦截到了也不会警告,只是默默地不从远程codebase加载类

操纵之术

系统属性

有很多反序列化链都可以修改,具体可见:

BCS2022-探索JNDI攻击 by浅蓝

https://github.com/iSafeBlue/presentation-slides/blob/main/BCS2022-%E6%8E%A2%E7%B4%A2JNDI%E6%94%BB%E5%87%BB.pdf

然而单单改系统属性是不够的,除非是Web应用初始化阶段就改掉

所以继续来看类私有属性

类私有属性

VersionHelper12里也没有setter,这个肯定只能反射改了

考虑以下两个问题

static final修改

针对这种情况,需要先去掉final修饰符

一种适用于 JDK8Java8 至 JDK17Java17 的反射修改 static final 属性的方法

https://blog.csdn.net/wu_weijie/article/details/129251045

获取实例对象

因为加载类的时候要用到,大概率是单例,打个断点,看看加载类的时候是从哪里拿到这个对象的

发现了如下方法调用

VersionHelper.getVersionHelper()

最终payload

由于要反射修改,所以只能在RCE(而且是动态代码执行上下文)条件下实现

适用于LDAP,Java 8 ~ Java11

1
2
3
4
5
6
7
8
9
10
11
12
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");

Class<?> clazz = Class.forName("com.sun.naming.internal.VersionHelper12");

Field field = clazz.getDeclaredField("trustURLCodebase");
field.setAccessible(true);

Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field,field.getModifiers()& ~Modifier.FINAL);

field.set(VersionHelper.getVersionHelper(),"true");

应用场景#非常无用

纯鸡肋: 在JDK高版本,利用具有动态代码执行上下文的反序列化漏洞,使得JNDI注入漏洞可以远程类加载