Coordinated Disclosure Timeline

Summary

The pr-website-deploy-comment workflow is vulnerable to Poisoned Pipeline Execution (PPE).

Project

Microsoft FluentUI

Tested Version

Latest commit at the time of reporting.

Details

Code Injection in pr-website-deploy-comment.yml workflow (GHSL-2024-327)

The pr-website-deploy-comment.yml workflow gets triggered when the workflow ‘PR Website Deploy’ finishes successfully:

name: PR Website Deploy | Comment on PR
on:
  workflow_run:
    workflows: ["PR Website Deploy"]
    types:
      - completed
...
jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
...

The workflow then downloads two artifacts generated by the triggering workflow and extracts the contents of pr.txt into a step output variable called id:

- name: Download WebSite artifacts
  uses: actions/download-artifact@v4
  with:
    name: pr-website-artifacts
    path: ./website
    run-id: ${{ github.event.workflow_run.id }}
    github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
  with:
    name: pr-number
    path: ./results
    run-id: ${{ github.event.workflow_run.id }}
    github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Load PR number
  id: pr_number
  run: echo "id=$(cat pr.txt)" >> $GITHUB_OUTPUT
  working-directory: ./results

This step output variable is then interpolated into a bash script that gets executed:

- name: Load WEBSITE URL
  id: website_url
  run: echo "id=${{env.DEPLOY_HOST_URL}}pull/${{steps.pr_number.outputs.id}}/" >> $GITHUB_OUTPUT

A malicious actor could send a Pull Request and modify the triggering workflow to upload any arbitrary content to the pr.txt file. Since the contents of this file are not validated and they get interpolated into a Bash script, the attacker will be able to get arbitrary code execution in the context of the deploy job.

Similar injections affect other steps:

      - name: Upload PR WebSite
        uses: azure/cli@v2
        env:
          AZCOPY_AUTO_LOGIN_TYPE: 'AZCLI'
        with:
          azcliversion: latest
          inlineScript: |
            az storage blob upload-batch \
              --destination '$web' \
              --source ./website \
              --account-name ${{ env.AZURE_STORAGE }} \
              --destination-path pull/${{steps.pr_number.outputs.id}} \
              --auth-mode login \
              --overwrite

and

      - name: Update PR deploy site GitHub status
        uses: actions/github-script@v7
        with:
          script: |
            const run = require('./.github/scripts/deploy-pr-site-status');
            const config = { websiteUrl: '${{ needs.deploy.outputs.website_url }}', prId: '${{ needs.deploy.outputs.pr_number }}'};
            await run({github,context,core,config});

Impact

The deploy job has access to the AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_SUBSCRIPTION_ID secrets and, in addition, has id-token: write permissions so an attacker would be able to fetch a JWT to log in into Azure CLI. Its not clear what permissions are granted to this token but it seems like, at least, it will have access to release a new version of the FluentUI web site (https://react.fluentui.dev/?path=/docs/concepts-introduction–docs) since this is what the workflow does. An attacker could use this to inject XSS or links pointing to malware in such website.

Credit

This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2024-327 in any communication regarding this issue.