GHSL-2023-271: Arbitrary command execution in changed-files

Coordinated Disclosure Timeline
协调披露时间表

  • 2023-12-18: Opened GHSA-mcph-m25j-8j63 through Private Vulnerability Reporting
    2023-12-18:通过私有漏洞报告开放GHSA-mcph-m25j-8j63
  • 2023-12-22: Fixed in 0102c07
    2023-12-22:已在 0102c07 中修复

Summary 总结

The tj-actions/changed-files workflow allows for command injection in changed filenames, allowing an attacker to execute arbitrary code and potentially leak secrets.
该 tj-actions/changed-files 工作流允许在更改的文件名中注入命令,从而允许攻击者执行任意代码并可能泄露机密。

Project 项目

changed-files 更改的文件

Tested Version 测试版本

v40.2.2 40.2.2 版

Details 

Potential Actions command injection in output filenames (GHSL-2023-271)
输出文件名中的潜在操作命令注入 ( GHSL-2023-271 )

The changed-files workflow returns the list of files changed in a commit or pull request.
changed-files 工作流返回提交或拉取请求中更改的文件列表。

const allChangedFiles = await getChangeTypeFiles({
    inputs,
    changedFiles: allFilteredDiffFiles,
    changeTypes: [
        ChangeTypeEnum.Added,
        ChangeTypeEnum.Copied,
        ChangeTypeEnum.Modified,
        ChangeTypeEnum.Renamed
    ]
})
core.debug(`All changed files: ${JSON.stringify(allChangedFiles)}`)
await setOutput({
    key: getOutputKey('all_changed_files', outputPrefix),
    value: allChangedFiles.paths,
    writeOutputFiles: inputs.writeOutputFiles,
    outputDir: inputs.outputDir,
    json: inputs.json,
    shouldEscape: inputs.escapeJson
})

Even though there is a shouldEscape option enabled by default, it only escapes " for JSON values. The setOutput function is defined as follows:
即使默认情况下启用了一个 shouldEscape 选项,它也只会 " 对 JSON 值进行转义。该 setOutput 函数定义如下:

export const setOutput = async ({
  key,
  value,
  writeOutputFiles,
  outputDir,
  json = false,
  shouldEscape = false
}: {
  key: string
  value: string | string[] | boolean
  writeOutputFiles: boolean
  outputDir: string
  json?: boolean
  shouldEscape?: boolean
}): Promise<void> => {
  let cleanedValue
  if (json) {
    cleanedValue = jsonOutput({value, shouldEscape})
  } else {
    cleanedValue = value.toString().trim()
  }

  core.setOutput(key, cleanedValue)

  ...
}

The jsonOutput function is defined as follows:
该 jsonOutput 函数定义如下:

export const jsonOutput = ({
  value,
  shouldEscape
}: {
  value: string | string[] | boolean
  shouldEscape: boolean
}): string => {
  const result = JSON.stringify(value)

  return shouldEscape ? result.replace(/"/g, '\\"') : result
}

This allows filenames to contain special characters such as ; and ` (backtick) which can be used by an attacker to take over the GitHub Runner if the output value is used in a raw fashion (thus being directly replaced before execution) inside a run block. By running custom commands an attacker may be able to steal secrets such as GITHUB_TOKEN if triggered on other events than pull_request. For example on push.
这允许文件名包含特殊字符,例如 ; ‘(反引号),如果输出值在 run 块内以原始方式使用(因此在执行前直接替换),攻击者可以使用这些字符来接管 GitHub Runner。通过运行自定义命令,攻击者可能能够窃取机密,例如 GITHUB_TOKEN 在除 pull_request .例如,在 push 上。

Proof of Concept 概念验证

In the case of a repository containing the following workflow, as detailed in changed-files README:
对于包含以下工作流的存储库,如 README 中 changed-files 所述:

name: CI

on:
  pull_request:
    branches:
      - main

jobs:
  changed_files:
    runs-on: ubuntu-latest
    name: Test changed-files
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # Example 1
      - name: Get changed files
        id: changed-files
        uses: tj-actions/changed-files@v40

      - name: List all changed files
        run: |
          for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
            echo "$file was changed"
          done

  1. Submit a pull request to the repository with a new file injecting a command. For example $(whoami).txt would be a valid filename.
    使用注入命令的新文件向存储库提交拉取请求。例如 $(whoami).txt ,将是一个有效的文件名。
  2. Upon approval of the workflow (triggered by the pull request), the action will get executed and the malicious pull request filename will flow into the List all changed files step.
    在批准工作流(由拉取请求触发)后,将执行该操作,恶意拉取请求文件名将流入该 List all changed files 步骤。
##[group]Run for file in $(whoami).txt; do
    for file in $(whoami).txt; do
        echo "$file was changed"
    done
shell: /usr/bin/bash -e {0}
##[endgroup]
runner.txt was changed

Impact 冲击

This issue may lead to arbitrary command execution in the GitHub Runner.
此问题可能会导致在 GitHub Runner 中执行任意命令。

Credit 信用

This issue was discovered by @jsoref (Josh Soref) and reported by GHSL team member @jorgectf (Jorge Rosillo).
此问题由 @jsoref (Josh Soref) 发现,并由 GHSL 团队成员 @jorgectf (Jorge Rosillo) 报告。

Contact 联系

You can contact the GHSL team at [email protected], please include a reference to GHSL-2023-271 in any communication regarding this issue.
您可以通过以下方式 [email protected] 联系 GHSL 团队,请在有关此问题的任何通信中注明。 GHSL-2023-271

原文始发于GitHub Security Lab:GHSL-2023-271: Arbitrary command execution in changed-files

版权声明:admin 发表于 2024年1月22日 下午7:13。
转载请注明:GHSL-2023-271: Arbitrary command execution in changed-files | CTF导航

相关文章