How I hacked Gitcoin’s tipping mechanism
Gitcoin offers a really neat way to send ethereum to any user on GitHub with just their username. I broke this mechanism and dumped the private keys that hold the tips before they’re claimed. This exploit has since been fixed, but here’s how I did it.
As the tech lead at Deco.Network, I am constantly researching new ways for coders to get paid. Therefore, Gitcoin’s tipping mechanism piqued my interest. I was curious as to how exactly it worked, so on July 26th 2018 I started to dig into the code.
The simplest implementation of this kind of tipping mechanism would be to have Gitcoin hold the tips until they’re claimed. So, the sender sends the money to Gitcoin, and then Gitcoin lets a GitHub user claim it by using “Login with GitHub” on the Gitcoin site. This is not how Gitcoin does it, though.
Gitcoin understandably wants to avoid being the custodian of those tips until they’re claimed. So instead, they used a somewhat convoluted process involving Shamir’s Secret Sharing Scheme and IPFS. If the mixture of those two things already sounds a bit fishy to you, then congratulations, you probably figured out how I performed this exploit.
Sending a tip used to work like this:
- The sender picks a GitHub username and amount of ETH to send, and hits “send”
- Javascript running in the browser generates a brand new Ethereum private key.
- That private key is split into 3 parts using Shamir’s secret sharing scheme with a threshold of 2 parts. That means you can combine any of the 2 parts to get the original private key back.
- 2 of the parts are uploaded to IPFS
- The IPFS hashes of those 2 parts that were uploaded to IPFS are uploaded to the Gitcoin DB.
- The 3rd part is also uploaded to the Gitcoin DB.
- The tip funds are sent to the address that corresponds with the newly created private key.
To claim a tip, a GitHub user does a “Login with GitHub” on the Gitcoin website. This causes Gitcoin to release the share of the private key it’s storing, and also the IPFS hash for another share. The user’s browser retrieves the share from IPFS, and combines it with the share that was stored in the Gitcoin DB. At this point, the user’s browser has the private key containing the tip money, and then presumably the user can sweep this into their own wallet (I didn’t actually check the code for this part, so this is just a guess)
So how did I break this mechanism and dump all the private keys?
I connected to the Gitcoin IPFS node, and asked it to list the hashes of all it’s stored items. I then retrieved all these items from IPFS and stored them in an array. I used a nested loop to try combining every item with every other item via Shamir’s Secret Sharing Scheme, and stored the result in another array. Then, I filtered the array of combined items, only keeping the ones that are the same length as an Ethereum private key
The result was an array with dozens of private keys, as you can kind of see in the screenshot below. The keys are censored, just in case.
Once I had an array of private keys, I looped over it to derive the corresponding Ethereum addresses. Then I checked all of them for a nonzero balance, which some keys did indeed have.
At this point, I contacted Kevin Owocki from Gitcoin, who promptly fixed the issue and rewarded me for finding this bug. I have to commend Kevin for the quick fix and generous reward.
Gitcoin is awesome!
Gitcoin is an amazing tool that is making serious progress in pushing open source forward. You should check it out if you haven’t already. I my opinion, it’s a model application for what you can do with Ethereum smart contracts.
You can view Gitcoin’s post on this issue here where they explain how they fixed it. Check it out!