简介
Spring Cloud Data Flow 是一个基于微服务的平台,用于在 Cloud Foundry 和 Kubernetes 上进行流数据和批处理数据的处理。该平台存在一个任意文件写入漏洞。该漏洞位于 Skipper 服务器组件,该组件处理包上传请求。由于对上传路径的清理不足,恶意用户通过访问 Skipper 服务器 API 可以利用此缺陷,构造一个特定设计的上传请求。这使得攻击者能够在服务器的文件系统上的任意位置写入任意文件,可能导致整个服务器被攻陷。
什么是 Spring Cloud Dataflow?
Spring Cloud Dataflow 是一个全面的工具包,旨在构建和编排微服务架构中的数据管道。它是 Spring 生态系统的一部分,专注于实现实时和批量数据处理。该平台允许开发人员创建、部署和管理数据处理工作流,这些工作流可以处理各种数据集成和处理任务,如 ETL(提取、转换、加载)操作、流处理和事件驱动的数据处理。
实验环境设置
受影响的版本是 2.11.x 和 2.10.x,因此为了进行分析,任何版本在 2.11.x 和 2.10.x 之前的版本都适合我们使用。我正在使用 2.11.0 进行分析。在 spring-cloud-dataflow-2.11.0/src/docker-compose 目录下,我们可以找到 docker-compose.yml 文件。我们将把 JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 添加到 skipper-server 下的环境部分,以便在动态分析期间进行调试:
现在,让我们部署我们的实验环境:
sudo docker-compose up -d
在这里,我们可以看到仪表板:
以及 Skipper Server API:
分析
如我们所知,该漏洞存在于 PackageService.java 文件中。可以在以下路径找到它:spring-cloud-dataflow-2.11.0/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/service/PackageService.java:
静态分析
由于漏洞位于 upload 方法中,让我们搜索一下这个函数被使用的位置。通过右击并选择“查找用法”:
我们可以看到用法如下:
方法
upload(UploadRequest)
在项目文件中的基本方法使用情况(发现 5 处使用):
未分类(发现 5 处使用):
spring-cloud-skipper-server-core(发现 5 处使用):
org.springframework.cloud.skipper.server.controller(发现 1 处使用):
PackageController(发现 1 处使用):
upload(UploadRequest)(发现 1 处使用):
88 行 return this.packageMetadataResourceAssembler.toModel(this.packageService.upload(uploadRequest));
org.springframework.cloud.skipper.server.controller.docs(发现 1 处使用):
UploadDocumentation(发现 1 处使用):
uploadRelease()(发现 1 处使用):
70 行 when(this.packageService.upload(any(UploadRequest.class))).thenReturn(pkg.getMetadata());
org.springframework.cloud.skipper.server.service(发现 3 处使用):
PackageServiceTests(发现 3 处使用):
upload()(发现 1 处使用):
142 行 PackageMetadata uploadedPackageMetadata = this.packageService.upload(uploadProperties);
testPackageNameVersionMismatch()(发现 1 处使用):
182 行 this.packageService.upload(uploadRequest);
assertInvalidPackageVersion(UploadRequest)(发现 1 处使用):
218 行 PackageMetadata uploadedPackageMetadata = this.packageService.upload(uploadRequest);
接下来,我们查看文件:
spring-cloud-dataflow-2.11.0/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/controller/PackageController.java:
@RequestMapping(path = "/upload", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public EntityModel<PackageMetadata> upload(@RequestBody UploadRequest uploadRequest) {
return this.packageMetadataResourceAssembler.toModel(this.packageService.upload(uploadRequest));
}
我们可以看到这里调用了 upload 方法,它通过 POST 方法通过 /upload 端点接收上传请求体。
@RestController
@RequestMapping("/api/package")
public class PackageController {
private final SkipperStateMachineService skipperStateMachineSer