漏洞介绍

GitLab是一款Ruby开发的Git项目管理平台。在8.9版本后添加的“导出、导入项目”功能,因为没有处理好压缩包中的软连接,已登录用户可以利用这个功能读取服务器上的任意文件。

漏洞范围

GitLab CE/EE 8.9

GitLab CE/EE 8.10

GitLab CE/EE 8.11

GitLab CE/EE 8.12

GitLab CE/EE 8.13

漏洞靶场

使用vulhub的靶场:

使用环境为GitLab 8.13.1

vulhub-master/gitlab/CVE-2016-9086/

启动靶场环境:

1
docker-compose up -d

环境运行后,访问http://your-ip:8080即可查看GitLab主页,其ssh端口为10022,默认管理员账号、密码是rootvulhub123456

漏洞原理

8.9.0版本开始,GitLab新增了导入导出项目的功能。
一个空的gitlab项目导出后结构如下:

export

其中VERSION文件内容为GitLab的导出模块的版本,project.json则包含了项目的配置文件。

当我们导入GitLab的导出文件的时候,GitLab会按照如下步骤处理: 1.服务器根据VERSION文件内容检测导出文件版本,如果版本符合,则导入。 2.服务器根据Project.json文件创建一个新的项目,并将对应的项目文件拷贝到服务器上对应的位置。

检测VERSION文件的代码位于:/lib/gitlab/import_export/version_checker.rb中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
def check!
version = File.open(version_file, &:readline)
verify_version!(version)
rescue => e
shared.error(e)
false
end
...
def verify_version!(version)
if Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
raise Gitlab::ImportExport::Error.new("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}")
else
true
end
end
...

gitlab-v8.13.0-ee/lib/gitlab/import_export/version_checker.rb#L12-L32

我们可以看到这里的逻辑是读取VERSION文件的第一行赋值给变量version,然后检测verison与当前版本是否相同,相同返回true,不相同则返回错误信息(错误信息中包括变量version的值). 于是漏洞发现者Jobert Abma巧妙的使用了软链接来达到读取任意文件的目的。首先,我们给VERSION文件加上软链接并重新打包。

1
2
ln -sf /etc/passwd VERSION
tar zcf change_version.tar.gz ./

img

这样,读取VERSION文件的时候服务器就会根据软链接读取到/etc/passwd的第一行内容并赋值给version。但是由于version与当前版本不相同,所以会输出version的值,也就是/etc/passwd第一行的内容。

访问之前搭建好的GitLab服务器,创建一个新的项目,填写完项目名称后在Import project from一栏中选择GitLab export,上传我们修改后的导入包,然后就可以看到/etc/passwd文件第一行

img

但是,如果只读取任意文件的第一行,能做的事情还是太少了。漏洞发现者显然不满足这一结果,他继续找了下去.
读取Project.json这一配置文件的代码位于:/lib/gitlab/import_export/project_tree_restorer.rb中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
def restore
json = IO.read(@path)
tree_hash = ActiveSupport::JSON.decode(json)
project_members = tree_hash.delete('project_members')

ActiveRecord::Base.no_touching do
create_relations
end
rescue => e
shared.error(e)
false
end
...

gitlab-v8.13.0-ee/lib/gitlab/import_export/project_tree_restorer.rb#L11-L22

在这里,我们可以再次使用软链接使变量json获取到任意文件的内容,但是由于获取的文件不是json格式,无法decode,导致异常抛出,最终在前端显示出任意文件的内容。 添加软链接并打包:

1
2
ln -sf /etc/passwd project.json
tar zcf change_version.tar.gz ./

img

上传导出包,页面上显示的结果:

img

漏洞复现

注册并登录用户,新建一个项目,点击GitLab export

image-20250514163109707

在导入页面,将test.tar.gz上传

image-20250514163155514

点击Import repository

image-20250514163328205

读取到/etc/passwd文件内容:

image-20250514163411259

参考

https://github.com/vulhub/vulhub/blob/master/gitlab/CVE-2016-9086/README.zh-cn.md

https://paper.seebug.org/104/