java之jdk17反射机制绕过深入剖析
1174735059082055 发表于 浙江 WEB安全 212浏览 · 2025-05-11 07:44

java之jdk17反射机制绕过深入剖析

问题分析

先写一个测试代码,通过反射调用 defineclass 方法加载恶意字节码,

然后进行运行的时候发现报错,

图片加载失败


看报错信息不难发现是在 setAccessible 出的问题,跟进该方法

图片加载失败


调用了 checkCanSetAccessible 方法,继续跟进

图片加载失败


看着逻辑有点多,但是发现如果满足下面这几种情况是可以返回 true 就可以进行反射调用了。

图片加载失败


第一个 if:调用者的模块(callerModule)和声明该方法的模块(declaringModule)是同一个模块返回 true

第二个 if:调用者所在的模块是 java.base 返回 true

第三个 if:如果 declaringModule 是一个未命名模块(Unnamed Module),返回 true

图片加载失败


如果声明的类是 public,并且该类所在的包被导出给调用者模块(declaringModule.isExported(pn, callerModule)),且成员是 public,则可以访问

图片加载失败


如果成员是 protected 且 static,并且调用者类是声明类的子类(isSubclassOf(caller, declaringClass)),则可以访问。

图片加载失败


如果声明类的包通过 open 关键字开放给调用者模块(declaringModule.isOpen(pn, callerModule)),则可以访问。

现在知道了只有上面这几种情况才会进行调用。

Unsafe 绕过

在 jdk 9 后的模块化机制提到

sun.misc和sun.reflect包下的我们是可以正常反射的,关键利用点就在这里。

看上面反射调用条件,我们可以通过 unsafe 来修改调用类的 moudle 属性为 java.base 然后使 checkCanSetAccessible 方法返回为 true 进行反射调用。这可以通过 Unsafe 类中的 getAndSetObject 方法(putObject 方法也一样)进行修改 moudule 属性,其和反射赋值功能差不多。

修改代码如下

最终成功反射加载字节码

简单解释几点,下面这段代码是反射反射获得 theUnsafe 属性值,这个属性值就是 Unsafe 对象,

接着是获得 Object.class 类的 module 属性

因为在 checkCanSetAccessible 中,看到 declaringModule 是通过 declaringClass.getModule() 获得的,为 module java.base

图片加载失败


而我们设置的 moudule 属性为 Object.class.getModule(); 的值也为 module java.base

继续看

获得当前类,然后通过 unsafe 的 putObject 来修改当前类的 module 属性,它可以绕过私有属性限制,因为其是直接操作 JVM 内存,就像 C 语言的指针操作一样。

之所以还需要设为同样一 moudle 进行反射,是因为其不能反射调用私有方法。设为同一 module 后 setAccessible(true); 就不会报错了

图片加载失败


TemplatesImpl 的模块机制影响

我们知道因为 jdk17 的模块化机制导致我们无法使用 TemplatesImpl 这个类,那么受否能用上面学的到 unsafe 进行绕过了,这里利用 CB 链进行尝试

cb poc

发现都没法运行,直接就报错了,

图片加载失败


报错信息为

问 GPT 解释是这个是类是直接就不能访问了,和上面不一样的点是,上面类可以访问,只是不能正常利用反射去设置其私有属性和私有方法。下面再细看这之间究竟有什么不同。

这里利用上面的 unsafe 进行反射获取其实列对象

测试 poc

然后就没有报错了,运行,看到非常成功的获得了 TemplatesImpl 实列并进行赋值。

图片加载失败


但是紧接着的反序列化还是报错

图片加载失败


看报错信息最后问题是出在compare 方法那里,看到在调用 getter 方法的时候报错,

图片加载失败


报错信息为

同样是模块化机制问题引起的。

那么再套个 JdkDynamicAopProxy 代理呢,通过调用这个代理类的 getOutputProperties 方法来最后反射调用TemplatesImpl 的getOutputProperties 方法,这是个 public 方法,然后这个代理类可以随便进行调用

最后测试 poc

看到最后成功调用到了getOutputProperties 方法,

图片加载失败


然后一直来到 defineTransletClasses 方法,但是再调用 defineclass 进行类加载的时候报错

图片加载失败


看到是因为我们的恶意类继承了 AbstractTranslet,而 AbstractTranslet 受模块化机制影响,所以导致失败。

图片加载失败


但是我们不继承这个 AbstractTranslet 类的化后面无法通过判断导致不会实列化恶意类,就算加载进去也没法触发。

所以经常说再 jdk 高版本无法使用 TemplatesImpl 类,那么对于 JdbcRowSetImpl 类呢,这个类的 getDatabaseMetaData 方法也可以进行 jndi 注入并且是个 getter 方法,但是的话这个类只有 JdbcRowSetImpl 类中才有,无法用代理类进行绕过。

不过,对于一些方法受模块化影响而且接口类有对应方法,sink点不涉及模块化的倒是可以用这种形式进行绕过

参考

https://pankas.top/2023/12/05/jdk17-反射限制绕过/#JDK17-对反射的限制

https://xz.aliyun.com/news/17257

1 条评论
某人
表情
可输入 255
用户ULVsncRgw3
2025-05-13 15:31 0 回复
兄弟,你是黑客嘛?能联系下我嘛?必重谢