1. 项目概述:从一次“无害”的文件写入到系统沦陷
最近在复盘一些历史安全案例时,我反复琢磨一个在特定场景下极具迷惑性和危害性的攻击链: Spring Boot FatJar应用中的文件写入漏洞如何演变为远程代码执行 。这个议题听起来有点老生常谈,毕竟文件上传、目录穿越导致RCE的案例比比皆是。但当你把场景限定在Spring Boot打包的、包含所有依赖的FatJar(或称Uber Jar)上时,整个攻击路径的细节、利用条件和防御盲区就变得非常独特,远不是一句“写个Webshell”那么简单。
很多开发者,甚至部分安全人员,会有一个误区:认为将应用打包成FatJar部署就万事大吉了,外部依赖和运行环境都被“封装”起来了。实际上,FatJar的运行机制恰恰为攻击者从文件写入到代码执行开辟了一条隐蔽的通道。攻击者可能仅仅通过一个允许写入特定路径的漏洞(比如日志配置错误、文件上传未限制路径、模板渲染可控等),就能在服务器上植入一个特殊的文件,最终在应用重启或特定条件下触发整个Spring容器的重新加载,执行任意代码。这个过程涉及对Spring Boot类加载机制、FatJar内部结构以及Spring特定功能点的深度利用。
这篇文章,我就结合自己的分析和测试,拆解这条攻击链的每一个环节。我会从FatJar的特性讲起,分析为什么它更容易“中招”,然后一步步还原攻击者如何利用文件写入点,最终实现RCE。无论你是开发者想加固自己的Spring Boot应用,还是安全研究者想深入理解这类漏洞的机理,相信都能从中获得一些实操性的参考。我们直接进入正题。
2. FatJar运行机制与安全盲区解析
要理解漏洞的根源,必须先搞清楚Spring Boot FatJar是怎么运行的,以及它和传统WAR包部署在安全视角下的关键差异。
2.1 FatJar的“自包含”特性与类加载逻辑
Spring Boot FatJar的核心设计目标是简化部署,真正做到“开箱即用”。它使用 spring-boot-maven-plugin 或Gradle等价插件,将应用本身、所有的第三方依赖库(包括内嵌的Tomcat/Jetty等Web容器)、以及Spring Boot自身的启动加载器( org.springframework.boot.loader.JarLauncher )全部打包进一个单一的JAR文件中。
当你执行 java -jar your-app.jar 时,真正被执行的不是我们编写的 main 方法,而是 JarLauncher 。它的工作流程可以简化为:
- 定位嵌套JAR :FatJar内部,依赖库和我们的应用代码是以“嵌套JAR”(Nested Jar)的形式存放的。
JarLauncher会读取META-INF/MANIFEST.MF中的Main-Class(指向JarLauncher)和Start-Class(指向我们应用的Application类)。 - 创建自定义类加载器 :
JarLauncher会创建一个LaunchedURLClassLoader。这个类加载器的关键能力是,它知道如何从FatJar这个“大文件”内部,去加载那些嵌套的JAR包中的类和资源。它通过JarFile和JarEntryAPI来模拟访问嵌套的JAR文件。 - 启动应用 :最后,用这个自定义的
LaunchedURLClassLoader加载并运行我们指定的Start-Class。
这里的安全盲区就出现了 :这个 LaunchedURLClassLoader 在运行时,对于类路径(classpath)的搜索范围,并不仅仅局限于FatJar内部的那些原始嵌套JAR。在某些条件下,它会去扫描FatJar文件 外部 的特定目录。这就为攻击者从外部植入恶意JAR或类文件提供了理论上的可能性。
2.2 可写路径与Spring的“热”机制
Spring框架,特别是Spring Boot,为了提升开发体验,提供了多种“热”机制,例如:
- 开发工具(DevTools) :在
classpath上存在spring-boot-devtools.jar时,应用会监控类路径变化并自动重启。 - 执行器端点(Actuator) :
/actuator/restart端点(需单独开启)可以触发应用上下文重启。 - 配置文件外部化 :Spring Boot会从多个预设位置(如
./config/,./,classpath:/config/,classpath:/)加载application.properties或application.yml。其中当前目录(./)和./config/目录是位于FatJar外部的。
在攻击视角下,如果应用(通常由于配置错误) 拥有对FatJar所在目录或其子目录的写权限 ,那么上述机制就可能被滥用。例如,攻击者如果能将文件写入到与FatJar同级的 ./config 目录,或者写入到某些被类加载器扫描的特定子目录下,就可能影响应用的行为。
注意 :生产环境通常会对应用目录的写权限进行严格管控,但现实中配置错误屡见不鲜。例如,使用
root用户启动应用后未正确降权,或者为了方便日志收集而赋予了过宽的目录权限。
2.3 从文件写入到代码执行的关键跳板
单纯写入一个文本文件(如配置文件)通常难以直接导致代码执行。攻击链要成立,需要找到一个“跳板”,使得写入的文件能被JVM以“可执行代码”的形式加载。这个跳板通常是:
- 写入一个恶意的JAR文件 :如果类加载器会从某个可写目录加载JAR,那么写入的恶意JAR中的类就会被加载并执行。
- 写入一个Spring Bean定义文件 :例如
@Configuration类或XML配置文件,如果Spring在重启或特定时机重新扫描并加载了这些文件,其中定义的Bean如果包含恶意代码(如利用SpEL表达式、ScriptEngine等),就可能触发RCE。 - 覆盖关键的类或资源文件 :直接覆盖FatJar内部的文件极其困难,但覆盖外部化配置(如
application.properties)来启用危险特性(如动态加载)是可能的路径。



被折叠的 条评论
为什么被折叠?



