复现1
参考https://landgrey.me/blog/22/,仅记录一些知识点
- 利用背景:一个正在运行中的 spring boot 项目如果存在本地任意写文件漏洞,怎么升级成 RCE 漏洞
现在生产环境部署
spring boot
项目一般都是将其打包成一个FatJar
,即把所有依赖的第三方 jar 也打包进自身的app.jar
中,最后以java -jar app.jar
形式来运行整个项目。运行时项目的
classpath
包括app.jar
中的BOOT-INF/classes
目录和BOOT-INF/lib
目录下的所有 jar,因此无法在其运行的时候往 classpath 中增加文件。
- 类装载和初始化
java 程序运行的命令行加上参数 -XX:+TraceClassLoading
,可以观察到类装载过程日志。
当经过 java.lang.Classloder.defineClass
方法的时候,即读取字节码并定义Class类型后,类装载 Loaded
操作就算完成了。
类初始化(也可以叫类实例化)一定发生在类装载之后,当调用类中的静态属性或者静态方法时,会被动触发类的初始化;调用 new
关键词、newInstance
方法、Class.forName
等方法时,会主动触发类的初始化。
- Up to RCE
如果没办法写入文件到应用的 classpath 目录,可以写入文件到更底层的 **”系统的 classpath 目录”**,即 JDK HOME 目录下。
jvm 为了避免一下加载太多暂时用不到或者以后都用不到的类,不会在一开始运行时把所有的 JDK HOME 目录下自带的 jar 文件全部加载到类中,存在 “懒加载“ 行为。主要特征就是相关 jar 文件的
Opened
操作没有在一开始就发生,而是调用到相关类时被触发。
所以思路就是:替换 JDK HOME 目录下的系统 jar 文件(系统启动后未被opened
),再主动触发 jar 文件里的类初始化来达到执行任意代码的方法。
触发加载
- 原生 Spring:对于每一次请求,spring 框架都会尝试解析
Accept
头的值,设置相应的字符集编码
在
org.springframework.web.accept.HeaderContentNegotiationStrategy
中:最终走到:
如果加了对应请求头,即会触发
依赖库:
- fastjson1.2.76:
1
2
3
4
5
6{
"x":{
"@type":"java.nio.charset.Charset",
"val":"500"
}
}- jackson开启 enableDefaultTyping
1
["sun.nio.cs.ext.IBM33722",{"x":"y"}]
- jdbc url connection
1
jdbc:mysql://127.0.0.1:3306/test?statementInterceptors=sun.nio.cs.ext.IBM33722
- 原生 Spring:对于每一次请求,spring 框架都会尝试解析
复现2
参考https://www.cnblogs.com/wh4am1/p/14681335.html利用 SPI 机制
初始化java.lang.String(byte[],String)
的构造函数时会一步步到lookupViaProviders
方法中,根据 SPI 慢慢写就好