Bitbucket 6.1.1:从路径遍历到远程代码执行

 

0x00 前言

Bitbucket是目前比较优秀的版本控制软件,可以帮助数百万开发者管理Git仓库,协同开发源代码。Bitbucket由澳大利亚软件公司Atlassian开发,该公司同时开发了Confluence和Jira等多款产品。在本文中,我们将分析Bitbucket中一个常见但经常容易被忽视的安全问题,该问题由RIPS Code Analysis发现,最终可以导致一个严重漏洞(CVE-2019-3397)。这个问题的根源在于Bitbucket并没有安全执行TAR归档文件的解压操作。

 

0x01 漏洞影响

Bitbucket中存在四个不同的用户角色,分别为UserProject CreatorAdmin以及System Admin。如果攻击者具备Admn权限,就可以滥用Bitbucket的“Data Center Migration”(数据中心迁移)工具,将可执行shell脚本释放到任意目录中,该操作利用了TAR归档文件中的路径遍历问题。为了实现远程代码执行(RCE),攻击者可以释放一个Git hook,当仓库上发生特定事件时(比如pull或push请求)就会执行该hook。存在漏洞的Data Center Migration工具位于5.14版的Bitbucket Server,安装Bitbucket Data Center许可证后就可以利用。

演示操作可参考此处视频,点击此处查看详细分析报告。

 

0x02 迁移过程

AdminsSystem Admins可以利用Data Center Migration将Git存储库从Bitbucket Server迁移到Bitbucket Data Center。在开始迁移之前,管理员首先需要从Bitbucket Server实例中导出存储库。在导出过程中,Bitbucket会创建包含如下结构的一个TAR归档文件:

_/repository/hierarchy_begin/c3b3efc5cb93609ad4fc
_/repository/hierarchy_end/c3b3efc5cb93609ad4fc
com.atlassian.bitbucket.server.bitbucket-instance-migration_instanceDetails/instance-details.json.atl.gz
com.atlassian.bitbucket.server.bitbucket-instance-migration_metadata/project_68/project.json.atl.gz
com.atlassian.bitbucket.server.bitbucket-instance-migration_metadata/project_68/repository_59.json.atl.gz
com.atlassian.bitbucket.server.bitbucket-instance-migration_permissions/project/68/all-permissions.json.atl.gz
com.atlassian.bitbucket.server.bitbucket-instance-migration_permissions/project/68/permissions.json.atl.gz
com.atlassian.bitbucket.server.bitbucket-instance-migration_permissions/repository/59/permissions.json.atl.gz
com.atlassian.bitbucket.server.bitbucket-git_git/repositories/59/hooks/hooks.atl.tar.atl.gz
com.atlassian.bitbucket.server.bitbucket-git_git/repositories/59/contents/objects.atl.tar
com.atlassian.bitbucket.server.bitbucket-git_git/repositories/59/metadata/metadata.atl.tar.atl.gz
com.atlassian.bitbucket.server.bitbucket-git-lfs_gitLfsSettings/59/git-lfs-settings.json.atl.gz

我们可以看到,导出的TAR文件中包含多个GZIP以及TAR压缩文件。其中hooks.atl.tar.atl.gz文件看上去比较可疑,因为该文件包含一些Git hook脚本,每当Git仓库中发生特定事件时就会被调用。我们只要使用../../之类的路径来构造这样一个TAR压缩包,那么当开始导入时就可以达到RCE效果。

 

0x03 不安全的解压操作

在仓库导入过程中,存放在hooks.atl.tar.atl.gz文件中的Git hook会被释放到${BITBUCKET_DATA}/shared/data/repositories/${REPO_ID}/imported-hooks/目录中,因此不会被执行,因为对某个仓库的正常hook脚本位于${BITBUCKET_DATA}/shared/data/repositories/${REPO_ID}/hooks/目录中。然而,如果攻击者精心控制hooks.atl.tar.atl.gz文件的内容,那么就有可能跳出服务端预设的目录,将hook释放到任意目录中。这主要是服务端没有对GZip压缩的TAR文件执行安全的解压操作所导致的一个问题。

简化版的extractToDisk函数代码片段如下所示,该函数接受hooks.atl.tar.atl.gz文件路径作为参数,然后在第4-9行通过lambda表达式调用read函数。该lambda表达式实现了IoConsumer<T>接口中的accept函数。

代码1:不安全的归档文件提取

public void extractToDisk(@Nonnull Path target, @Nonnull Predicate<String> filter) throws IOException {
    ⋮
        this.read((entrySource) -> {
            Path entryPath = entrySource.getPath(); // line 4
            String filename = entryPath.getFileName().toString();
        ⋮
            entrySource.extractToDisk(target.resolve(entryPath)); // line 7

        }, filter); // line 9
    }

read函数定义如下代码片段所示。该函数会遍历所有压缩项目,然后在TarEntrySource类对象上(该对象包含用户输入数据)调用accept函数。从中我们可以看到,来自org.apache.commons.compress.archivers.tar.TarArchiveEntry.getName()的未经过滤的用户输入数据(代码2第7行)最终会被java.nio.Paths.get()所处理(代码2第9行),因此存在路径遍历漏洞(Path Traversal)。

由于accept函数采用如上定义的lambda表达式来实现,我们可以一路跟踪用户输入,找到TarEntrySource.extractToDisk()函数(代码1第7行)。

代码2:读取TAR归档文件

public void read(@Nonnull IoConsumer<EntrySource> reader,
                 @Nonnull Predicate<String> filter) throws IOException {
    ⋮
    TarArchiveEntry entry;
    while ((entry = (TarArchiveEntry) inputStream.getNextEntry()) != null) {
        InputStream entryInputStream = new CloseShieldInputStream(inputStream);
        String name = entry.getName();  // line 7
        if (filter.test(name)) {
            reader.accept(new TarEntrySource(entryInputStream, Paths.get(name), entry)); // line 9
        }
    }
 }

TarEntrySource类中extractToDisk函数代码片段如下所示,该函数接受未经过滤的文件路径作为参数。从中我们可知,代码会创建所有子目录(代码3第5行),然后将目标文件拷贝到该目录中(代码3第8行)。

代码3:释放文件

private static class TarEntrySource extends DefaultEntrySource {
      ⋮
    public void extractToDisk(@Nonnull Path target) throws IOException {
      ⋮
     Files.createDirectories(target.getParent()); // line 5
     OutputStream out = new FileOutputStream(target.toFile());
      ⋮
     IoUtils.copy(this.inputStream, out, 32768); // line 8
      ⋮
     PosixFileAttributeView fileAttributeView = (PosixFileAttributeView)Files.getFileAttributeView(target, PosixFileAttributeView.class);
     fileAttributeView.setPermissions(FilePermissionUtils.toPosixFilePermissions(this.tarArchiveEntry.getMode())); // line 11

    }
  }

攻击者可以利用路径遍历漏洞将Git hook脚本释放到攻击者可控的Bitbucket仓库中。然而,如果没有正确设置这个shell脚本权限(比如没有设置执行权限),那么Git hook也不会被执行。这里值得一提的是,TAR归档文件包含文件的元(meta)信息,其中包括修改时间、用户名、组名以及文件模式(文件权限)。在代码3第11行处,程序会根据归档文件中对应的权限信息来设置文件权限。

 

0x04 时间线

  • 2019/02/27 向Atlassian报告Path Traversal漏洞
  • 2019/03/11 Atlassian确认该漏洞并分配了BSERV-11706问题编号
  • 2019/04/01 Atlassian在Bitbucket 6.1.2中解决该问题
  • 2019/05/22 Atlassian发布Bitbucket安全公告

 

0x05 总结

在本文中我们分析了Bitbucket服务端的不安全TAR归档文件解压操作,最终实现RCE漏洞效果。全世界有数百万开发者在使用Bitbucket,这显然是一个非常吸引攻击者的利用点。该漏洞可以有不同的利用场景,Bitbucket User可以诱骗Admin角色(非Sysadmin Admin)的用户来导入恶意TAR文件,以便获得远程Bitbucket服务端的控制权限;恶意Admin自己也可以利用该漏洞。我们建议用户尽快更新Bitbucket Data Center。此外,Atlassian团队在解决该问题过程中非常专业,在此表示感谢。

 

0x06 相关资料

(完)