Web-of-trust services (WOT) such as Keybase, Onename, and Blockstack promise to verify individuals' identities on the web. Since many applications on the web are not consistent this often leads to unintended behaviour and therefore security vulnerabilities in web-of-trust services. This write-up analyses three attack vectors that I stumbled across while conducting research on the security of WOT services.
WOT services allow users to create tokens and place them on their personal pages (e.g. GitHub profile). The service will then look for the token using a scraper and if the token is valid, display that the user does in fact own that external page. The idea behind this method is so that users can display what pages on the web belong to them and then tie their WOT account to all of those external services. On top of that, since the verification tokens need to be publicly accessible, other users can verify that the proof is legitimate by visiting the page containing the token.
On the web a lot of profile-based applications allow redistribution of content by sharing someone else's entry on your personal timeline — on Twitter users can retweet content and on GitHub people can fork projects. Since some WOT services use a public entry or post to verify the user's identity, I asked myself whether it would be possible to claim ownership of someone else's account by having them share a token that was posted onto an attacker's timeline. One particular service stood out for me, GitHub, where WOT applications such as Keybase require users to place the verification token into a GitHub gist file. The interesting behaviour that I noticed with GitHub is that when a user forks a gist, the gist is not only displayed on the user's timeline, it replaces the original author's username with the sharer's username.
One vulnerable service was Keybase, where if an attacker could convince a victim to fork their GitHub gist, the attacker would be able to claim ownership of the victim's GitHub username. On top of that, Keybase allowed modification of the verification snippet, allowing an attacker to hide the token in an HTML comment.
An example attack using this technique against Keybase could have unfolded as follows:
- The attacker requests a verification token from Keybase for the victim's GitHub username;
- Keybase prompts the attacker to place the verification token in a keybase.md gist;
- The attacker creates a keybase.md gist hiding the verification token in an HTML comment;
- The victim forks the attacker's GitHub gist;
- The attacker instructs Keybase to verify /victim/keybase.md.
As a result, the attacker's Keybase account states that they own the victim's account. To make matters worse, Keybase has a browser extension that allows users to browse to certain applications (e.g. GitHub, Twitter, Hacker News, etc.) and message the user on Keybase via the profile page — the extension adds a little messaging window on the user's profile. Messages are sent to the account on Keybase that has verified ownership of the account. This means the extension will trick the user into thinking they are messaging the intended recipient, but all messages land in the attacker's inbox since they control the victim's username.
Some WoT services require placing the verification token in a specific filename. Keybase, for example, as mentioned in the previous section, require GitHub verification tokens to be placed in GitHub gists called keybase.md. On web assets, Keybase require the file to be named keybase.txt and either placed under the top-level directory or the
.well-known path. The reason behind the
.well-known proposal in RFC5785, is to prevent filename collisions and clogging up the root directory. The former is particularly interesting when it comes to WOT services since if an attacker can control the filename on a website, they could potentially claim ownership of the domain. One such case happened with liberapay.com and Keybase. Liberapay, an open-source donation platform, did not restrict username's containing dots in them; therefore one could create usernames containing file extensions. This became apparent to me when I set up a profile page for the security.txt project (https://liberapay.com/security.txt). I created a user called keybase.txt and embedded the Keybase verification snippet in the profile's description. This allowed me to claim ownership of liberapay.com. Keybase did not verify the content type of the keybase.txt file and did not even ensure that the token is not embedded into a page.
After claiming ownership of liberapay*.com* I noticed that liberapay*.org* redirects to liberapay*.com. This next attack consisted of using a verification token generated for liberapay.org* embedded on liberapay*.com* to claim ownership of liberapay*.org. Keybase's scraper would blindly follow the redirect and not validate the final endpoint to make sure it matches the target host. Keybase would request liberapay.org/keybase.txt which redirects to liberapay.com*/keybase.txt where a valid keybase.txt file is located.
One particular plausible attack scenario that I could come up with was claiming branded URL-shorteners using this technique.
All services affected by these attack vectors were notified and promptly resolved most of the reported issues. Keybase remain vulnerable to attack vectors 2 and 3 — as far as I can tell they do not plan on resolving those issues. I was thoroughly impressed by the response times of all the affected parties and I look forward to working with them again in the future. As a result of this research, I have become addicted to finding logic flaws.