Around the beginning of February 2020, GitHub Security updated their public security disclosures and hacker leaderboard, and this happened:

GitHub Security Leaderboards with Vlad Ionescu on the ninth position out of ten people

GitHub leaderboard as of June 2020

Previously, on October 7, 2019, something popped up on HackerOne:

A 10000 \$ bounty awarded to Vlad Ionescu on HackerOne for a GitHub security report

HackerOne bounty

This is the story of how it all came to be. Unlikely to ever have a sequel.

   


   

Vlad likes pretty

Something most people on the internet don’t know about me is that I like pretty stuff. Be it a whiskey pipette, a lovely painting, many dog thoughts, a ferocious lover — it does not matter. I like pretty!

Although I generally have excellent impulse control1, pretty is pretty and I want pretty. I want to enjoy pretty. Savor pretty. Surprisingly often, this puts me in weird situations — like accidentally hacking GitHub.

CircleCI is not pretty

CircleCI is a hosted build service. It can build or compile code to ensure there are no build errors, it can run tests to ensure the correct thing happens, it can do whatever you want it to do!

But CircleCI is not pretty. For some cruel reason, CircleCI forces people to log in to their UI to get to the test output. Oh, a test failed? Spend 3 minutes following redirects and logging into things. Only then you get to see why the test failed.

The typical workflow looks like this:

  • do some work
  • send work to GitHub
  • CircleCI is notified and starts building and testing the code
  • notification is received from GitHub and/or CircleCI
  • go to GitHub, see that the “Test X” step failed
  • click “Details Test X
  • click “See more in CircleCI
  • new tab opens
  • click “Log In
  • go through the login process
  • finally, see how the test failed

WHY?! I don’t understand why CircleCI even has a GitHub integration. Why not have the output right there on GitHub? Why does CircleCI insist on adding another step to the whole process?

It’s not even a limitation on the GitHub side: Brigade can, and does, precisely that!

I really do not like the whole user experience around the CircleCI integration with GitHub. It was, and it still is, a constant source of annoyance for me. I mean look at it:

An image comparing CircleCI in GitHub( three builds, 1 failed, and a link below with 'See more details on CircleCI' versus a Brigade test run with the output right there

CircleCI vs Brigade

GitHub Actions are pretty

In August 2019, it happened: I finally got access to the GitHub Actions Beta! A day I was very much looking forward to.

I am working on a continuous challenge to be less cranky, and CircleCI was making me cranky. This was a chance for me to be happier!

GitHub Actions are similar to CircleCI: they can build code, they can test code, they can do whatever you want them to do!

Even more, GitHub Actions can answer to more than just new code: they can run when a comment is posted, or when a change is approved! This flexibility empowers people to build even more amazing workflows.

I immediately started playing around with GitHub Actions. I loved everything about them — they were so flexible. More importantly, the output is right there. On the same page! Look how gorgeous they are:

An image showing very clear and pretty logs from GitHub Actions

Everything was nice in the world. For a couple days.

GitHub Actions user experience is not pretty

GitHub Actions were unacceptably slow due to a lack of caching, and don’t even get me started on the whole slew of unexpected and random limitations.

You think GitHub Actions can answer to say a comment being posted on a Pull Request, but then surprise! They can indeed run on a comment post, but you don’t actually have access to the code in that Pull Request.

I started working with GitHub Actions more and more, and I started even creating some actions. It was not pretty, but it was prettier than the CircleCI alternative.

I was doing a lot of Terraform at that time, and I really wanted warnings from tflint on Pull Requests. Output on the separate Checks tab was not enough anymore — I wanted it in the Conversations tab. That is where I spend my time, so that is where everything relevant should be. I don’t want to even have to press one button.

GitHub Actions are pretty with reviewdog

I ended up discovering the lovely reviewdog: it takes the output from any tool and sends it to GitHub!

Want the output as a Check annotation? It can do that. Want the output as a Review comment? It can do that.

Look how pretty it is:

Reviewdog posting a comment on a Pull Request with a golint error

Comments are right there!

It was love at first sight! I started working more and more with reviewdog, and I even ended up writing a reviewdog tflint GitHub Action!

Life was good again!

GitHub Actions are not pretty if it has unexpected limitations

Now I get notified that things are wrong, and in the right place. But wouldn’t auto-fixes be even prettier? Not fully automatic, but something like “click a button and it’s fixed”.

Since I want pretty, I started working on it! I imagined something along these lines:

  • GitHub Actions sees that code formatting is wrong
  • reviewdog posts a comment letting me know
  • when the time is right, I comment pls fix
  • automated fixes just appear

GitHub Actions at that time were very… particular about how they downloaded the code and what version of the code was downloaded. For the life of me, I could not get an auto-fix workflow to do what I wanted.

GitHub Actions are pretty with auto-fixes

After about a week of perusing the incomplete very beta documentation, I figured it out!

If I post a comment on the review screen and have the auto-fixer GitHub Action respond to the pull_request_review event, it all works! WOOOO! The right code is downloaded, the fixes are pushed back, everything works!

Life was so pretty! It was an exemplary workflow, and it made life so much easier.

GitHub Actions are not pretty if it leaks secrets

I was so proud of the thing I built. I was even telling people about it and insisting they use it because it is a better user experience.

In one of the chats, it was pointed out to me that I was kind of breaking the security of GitHub Actions.

In my mind it was just “beware, you need to set this thing for this to work”, not “please create a secret so I can steal it”.

I have access to a surprising amount of GitHub Organizations and GitHub Teams, so I went forth and tested if I was actually breaking the Secret protection.

In 10 minutes I had confirmed that I was indeed able to steal any secret for any public GitHub repository, without any user involvement.

Oops.

Submitting the report

Allow me to set the scene.

It is late in the evening on September 29. I am on my couch with my laptop and a glass of Lupi, a lovely blend of red wines. Rather inebriated, but happy and content. Doing stuff on GitHub.

I panicked a bit. I allegedly found a security issue. I am also drunk, so I might be wrong. But I could be right. But I am a dum-dum and GitHub people are smarter than me, so there is no way. But what if?

Fuck. I panicked some more.

I had to report it — even if there was a small chance of it being real, there was a risk. I never had any issues or reservations about looking dumb: that’s how you learn!

I imagined I would send an email to something like security@github.com. Looking into it, I saw that GitHub has an open process on how to report security issues: they have a HackerOne account. HackerOne handles the process and GitHub responds. Nice!

I quickly created an account with HackerOne, hoping that a 2-minute-old-account will be allowed to send a report. Surprisingly, they allow that. Nice!

Drunkenly, I wrote a report and submitted it. For your pleasure, here it is in all its glory:

Description:

Unsure how much of a security issue this is but better safe than sorry. This is my first report ever so I have no idea what I am doing. Apologies if I am wrong.

GitHub Actions for the pull_request_review event run in the base repo not in the fork. But a fork can change the code for the action to leak secrets. No input is needed.

This was discussed a bit on https://github.com/reviewdog/action-tflint/issues/2 where I was urged to report it.

Steps To Reproduce:

See https://github.com/org-name-redacted/repo-for-fork-testing/pull/9 with the https://github.com/org-name-redacted/repo-for-fork-testing/commit/2dc3d01c3f342d722ca0a0b0543901a15de7fe16/checks check, the Test step. I base64 encoded the secrets to get around the secret hiding thing.

  1. Have a public repository with GitHub Actions access and some Secrets set for the repo
  2. Fork the repo to an account
  3. Add an action that runs for pull_request_review that tries to get the value of a secret
  4. Create PR to the public repository
  5. Leave a review comment on the created PR
  6. Go to the Actions tab where the action ran and secrets values can be found

This does require knowing the secrets name, but I guess a brute force attack searching for secrets names is not improbable.

Again, apologies if Actions is out of scope/ this issue is known.

Impact

Leaking of secrets( whose names are known) for any public repository with GitHub Actions enabled.

I woke up the next day. A ridiculously sunny Monday. I thought about it some more, and I updated my initial report.

I remember it vividly: I was in an Uber on my way to a client onsite, and I was sitting in the back trying to write a “serious” and “adult” report:

Hi,

I don’t see a way to edit this but I’d like to raise this to Critical. I am still new and confused, but being able to leak any secret from GitHub Actions seems huge to me.

While Actions are still in preview, multiple projects use them already. This exploit has a huge impact for GitHub Actions and for any org using Actions:

Pretty much every secret on GitHub can be retrieved in less than 5 minutes, with no action required from the person being attacked. A very easy way to find names is to search for secrets in .github/worrkflows across all GiHub. Once a name is found the repo is forked, a PR is created back with the following code, a comment is left on the PR, and in less than 1 minute the secrets are out:

name: Leak

on: [pull_request_review]

jobs:
  leak:
    name: Leak GitHub Seecrets
    runs-on: ubuntu-latest
    steps:
      - name: Leak the secre
        run: |
          echo $VALUE | base64
        env:
          VALUE: ${{ secrets.SECRET_NAME_WHICH_IS_EASY_TO_GET }}

Please treat this with the highest priority.

Thank you,

Vlad Ionescu

It had a typo and all that, but it was a better report.

GitHub Bounties are pretty

GitHub, to their credit, responded super-fast to the report. Keeping in mind that times are local to Bucharest and the US is 10-ish hours behind, the timeline looks like this:

  • Dead-of-the-night Sunday, September 29: initial report
  • Early Monday, September 30: updated report
  • Late Monday, September 30: report triaged and GitHub started working on it
  • October 7: 10.000 $ bounty awarded for the bug report2 3
  • October 18: lifetime GitHub Pro subscription awarded, formal invitation into @GitHubBounty organization

GitHub Bounties spam is weird

As soon as GitHub awarded the bounty, their HackerOne page was updated and showed that “Vlad Ionescu reported something secret and got 10 grand from GitHub”. That brought more attention to me than I would’ve liked to. It was a bit overwhelming.

I started getting a bunch of messages asking me for details about the vulnerability. I started getting a lot of messages asking me if I want to pair on researching bugs.

On the one hand, that is very spammy. On the other hand, awww, people hack together and collaborate and that seems nice? I have doubts about how nice it actually is.

Conclusion

The fact that a random human with zero experience can just create an account on HackerOne and report a security issue to a major company is fantastic. And they take these reports seriously! And give out rewards!

Major props to HackerOne and the GitHub Security Team for all this! They managed to create an outstanding process, and they stick to it.

Also, I can now put Security Researcher on my resume! Ahem, it’s a resume; I have to sell myself. “Established myself as part of the top 0.3 percent of hackers”.


  1. Shhhhhhhh. I really do! ↩︎

  2. I, of course, tried negotiating for a higher bounty. Well, “asking” cannot really be called negotiating. ↩︎

  3. The reward was split with the person that suggested reporting the issue to GitHub. ↩︎