what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

GitHub Widespread Injection

GitHub Widespread Injection
Posted Nov 3, 2020
Authored by Google Security Research, Felix Wilhelm

Github Actions supports a feature called workflow commands that is susceptible to widespread code injection vulnerabilities.

tags | advisory, vulnerability
advisories | CVE-2020-15228
SHA-256 | fad674c47b105cfc1035cbe0b4661f311b3d8159fc76033622fa185b205e5785

GitHub Widespread Injection

Change Mirror Download
Github: Widespread injection vulnerabilities in Actions

Github Actions supports a feature called workflow commands
(https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions) as a
communication channel between the Action runner and the executed action. Workflow commands are
implemented in runner/src/Runner.Worker/ActionCommandManager.cs
and work by parsing STDOUT of all executed actions looking for one of two
command markers.

V2 commands have to start at the beginning of a line and look like this \u"::workflow-command
parameter1={data},parameter2={data}::{command value}\u". V1 commands can also start in the middle of
a line and have the following syntax: \u"##[command parameter1=data;]command-value\u". The current
version of the Github action runner supports a small number of different commands but the most
interesting one from a security perspective is \u"set-env\u". As the name suggests, \u"set-env\u" can be
used to define arbitrary environment variables as part of a workflow step. A simple example (in V1
syntax) would be ##[set-env name=VERSION;]alpha, which puts VERSION=alpha in the environment of all
succeeding steps in a workflow.

The big problem with this feature is that it is highly vulnerable to injection attacks. As the
runner process parses every line printed to STDOUT looking for workflow commands, every Github
action that prints untrusted content as part of its execution is vulnerable. In most cases, the
ability to set arbitrary environment variables results in remote code execution as soon as another
workflow is executed.
I've spent some time looking at popular Github repositories and almost any project with somewhat
complex Github actions is vulnerable to this bug class. A couple of examples to show how this bug
can be exploited in practice:

VSCode and CopyCat:

VSCode has a workflow for newly opened issues which runs
https://github.com/microsoft/vscode-github-triage-actions/blob/master/copycat/CopyCat.ts to copy
new issues into other repositories. As CopyCat prints the untrusted issue.title to stdout, it is
vulnerable to a workflow command injection.
Exploiting this instance is as easy as opening a new issue with the title \u"##[set-env

This will set the environment variable NODE_OPTIONS to the string \u"--experimental-modules
ring('hex'));//\u" which will get parsed by the Node interpreter during later execution steps. My
payload simply dumps the process environment in hex-encoded form to bypass secret redaction, but of
course more complex payloads are possible.


Even Githubs own actions are vulnerable to this issue. actions/stale dumps untrusted issue titles
to STDOUT using core.info https://github.com/actions/stale/blob/ade4f65ff5df7d690fad2b171eeb852f4809dc0b/src/IssueProcessor.ts#L116, which boils down to a direct write to process.stdout
Fortunately, stale is often used as part of a single step workflow and I wasn't able
to exploit this bug class without executing a step after the workflow command injection. However,
workflows that use actions/stale and have multiple steps can be exploited in the same way as the
CopyCat issue from above (one example would be

Non-forked pull requests:

Actions that operate on issues are the most obvious attack vector, but non-forked pull requests are
also interesting. Actions triggered by forked pull requests don't have access to write tokens but
an external contributor can just create a pull request between two existing branches in the target
repo to trigger a privileged workflow run.
One interesting example for a vulnerable action is https://github.com/cirrus-actions/rebase which
triggers on /rebase comments on a pull request and which is used by a number of popular Github
repos such as https://github.com/ReactiveX/rxjs.

If the action is executed on a pull request that can't be rebased, the full Github API
representation of the PR is printed to STDOUT:

This also includes the attacker controlled PR body and title and can be exploited by creating a
non-forked and non-rebasable PR with the following body: ##[set-env
(This works even though the body is printed as part of a JSON document as V1 workflow commands can
start in the middle of a line.). Code execution is triggered by just commenting \u"/rebase\u" under the
As rebase relies on actions/checkout, this even works if rebase is the last step in a workflow.
Current versions of action/checkout define a post-execution step to cleanup git credentials and
which will trigger our exploit.

Suggested Fix:

I'm really not sure about the best way to address this issue. I think the way workflow commands are
implemented is fundamentally insecure. Deprecating the v1 command syntax and hardening set-env with
an allowlist would probably work against the direct RCE vectors. However, even the ability to
overwrite \u"normal\u" environment variables used by later steps is probably enough to exploit most
complex actions. I also did not look into the security impact of other workspace commands.
A good long-term fix would be to move workflow commands into some out-of-bound channel (e.g a new
file descriptor) to avoid parsing STDOUT, but this will break a lot of existing action code. (I do
not think that this should be addressed by simply patching vulnerable actions. Depending on the
programming language used it is pretty much impossible to guarantee that no malicious data will end
up on STDOUT. )


My private repo github.com/felixwilhelm/actions includes vulnerable actions and triggers. Please
let me know if I should give someone on your side access to it.

Felix Wilhelm of Google Project Zero

This bug is subject to a 90 day disclosure deadline. After 90 days elapse, the bug report will
become visible to the public. The scheduled disclosure date is 2020-10-19. Disclosure at an earlier
date is also possible if agreed upon by all parties.

Related CVE Numbers: CVE-2020-15228,CVE-2020-15228.

Found by: Felix Wilhelm
Login or Register to add favorites

File Archive:

July 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jul 1st
    27 Files
  • 2
    Jul 2nd
    10 Files
  • 3
    Jul 3rd
    35 Files
  • 4
    Jul 4th
    27 Files
  • 5
    Jul 5th
    18 Files
  • 6
    Jul 6th
    0 Files
  • 7
    Jul 7th
    0 Files
  • 8
    Jul 8th
    28 Files
  • 9
    Jul 9th
    44 Files
  • 10
    Jul 10th
    24 Files
  • 11
    Jul 11th
    25 Files
  • 12
    Jul 12th
    11 Files
  • 13
    Jul 13th
    0 Files
  • 14
    Jul 14th
    0 Files
  • 15
    Jul 15th
    28 Files
  • 16
    Jul 16th
    6 Files
  • 17
    Jul 17th
    34 Files
  • 18
    Jul 18th
    6 Files
  • 19
    Jul 19th
    34 Files
  • 20
    Jul 20th
    0 Files
  • 21
    Jul 21st
    0 Files
  • 22
    Jul 22nd
    0 Files
  • 23
    Jul 23rd
    0 Files
  • 24
    Jul 24th
    0 Files
  • 25
    Jul 25th
    0 Files
  • 26
    Jul 26th
    0 Files
  • 27
    Jul 27th
    0 Files
  • 28
    Jul 28th
    0 Files
  • 29
    Jul 29th
    0 Files
  • 30
    Jul 30th
    0 Files
  • 31
    Jul 31st
    0 Files

Top Authors In Last 30 Days

File Tags


packet storm

© 2022 Packet Storm. All rights reserved.

Security Services
Hosting By