SOP Bypass via browser-cache

Introduction

Whilst hunting for security issues on Keybase.io’s public HackerOne program, I noticed that several API endpoints had CORS enabled. For those who are not familiar with CORS, it allows for a site to relax the SOP so that other domains may interact with (most often) a web API. In this article I’ll discuss how I was able to manipulate the browser cache into returning private user data through a misconfiguration of Keybase’s CORS policy.

Overview

Keybase provides an API that allows users to perform a lookup of other Keybase users when encrypting a message, similar to a contact book lookup. It provided publicly accessible information on other users that is required when encrypting a message such as their public PGP key. So far so safe right?

The CORS policy implemented on this endpoint looked like the following:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With
Access-Control-Allow-Credentials: false

There is some important aspects to understand here in order to be able to make sense of the issue:

  1. The wildcard (*) value for Access-Control-Allow-Origin allows for any external domain to interact with the API and perform queries cross-origin
  2. Using the wildcard value implements ‘Access-Control-Allow-Credentials: false’ meaning authenticated requests cross-origin are disallowed (For security reasons). The Authorization header exposed here for an authenticated request is not overly relevant for the exploit, as it is not required to query the public API.

The issue

No CSRF token is required in order for these cross-origin requests to work, naturally, since the API is considered public. Users utilizing Keybase.io are authenticated and their session is stored in cookies, while more sensitive API endpoints require an Authorization token in a header.

What I noticed was that if I performed a lookup on myself while authenticated (had a keybase.io session set in my cookies), or even entered a letter contained in my name, my own account would be returned in the search results along with private information. This included:

  • Email Address
  • How many invitation codes I had used / left to use
  • Billing plan information
  • Timestamps relating to last logged in, and time / date of email verification
  • Private PGP key (Encrypted with TripleSec)

I had no private PGP key stored on Keybase, and I later learned that private PGP key hosting on Keybase.io is in fact a legacy feature from 2015-2016 that is no longer implemented.

Once the cookies were removed from my request to the endpoint, my private information mentioned above was no longer returned. However one thing I did notice in the response returned by the endpoint was an ‘Etag’ header. This header is an indicator of browser caching, instructing a browser to fetch from its cache if the content in the response had not changed.

Payload and Result

I recalled a trick I learned from @Bitk_, in which it’s possible to use javascript’s fetch API to force a request cross-origin to retrieve from the browser cache directly. Keybase did not implement any cache-control headers in the response, so I created a payload locally (The null origin won’t matter) like so:

<html>
<script>  
var url = "https://keybase.io/_/api/1.0/user/lookup.json?username={YOUR_USERNAME}";  
fetch(url, {    
    method: 'GET',    
    cache: 'force-cache'
    });
</script>
</html>

I knew that if this request succeeded, it would likely return the cached response given by Keybase after I had made an authenticated request, which would likely contain my private information.

And voila!

In order to confirm that the payload was successful, we can see below that fetch had pulled the response directly from the browser cache.

Timeline

19/12/2019 – Issue reported to Keybase

19/12/2019 (2 hours later) – Issue remediated with implementation of the ‘Cache-Control: no-store’ header in the response as advised.

24/12/2019 – Public disclosure of HackerOne report

3 thoughts on “SOP Bypass via browser-cache

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s