How I got access to millions of [redacted] accounts


I'd like to start by saying that the company involved has my full respect for handling security in a mature manner and actively seeking researchers to take a deep look at their products. It's important to note that during testing I did not access personal data or files belonging to users other than my own.

Onto the chase.

It was a cool, dark night. The world was still, the bounties were calling. One in particular caught my eye. It had been running for nearly two weeks so I wasn't expecting to find many new bugs, but sometimes you get lucky. It was a time-limited affair, and with 24 hours left on the clock I decided to try my luck. At first I didn't find much - the odd information leak, header injection, an XSS, nothing that hadn't been reported half a dozen times. But then...

An interesting discovery

Things soon got interesting when I came across a script which got passed a URL parameter. This kind of behaviour should set any bounty hunter's vulnerability sense tingling, and sure enough with a bit of experimentation I found that it was possible to make the application place requests to an external webserver as long as a particular path was in the URL. I set up a log script and captured the requests for later analysis.

Before I get ahead, a note about the authentication mechanism used by the site. Every POST request requires an Authorization header be sent containing credentials in JSON Web Token format. For attackers this is a bit of a pain in the arse as it means CSRF attacks are pretty much impossible, but it's also pretty useful when gathering information because access tokens can be decoded to reveal the username, permissions and other information about the owner of the token.

The JWT token

2 + 2 = 4

I spent some time experimenting with the vulnerable script, injecting various URLs and playing with the responses. Could data returned by the attacker-controlled server be used to trigger an XSS? Sadly not, everything ended up properly encoded in a clean JSON string. Could the script be used to perform reconnaissance on the internal network? Yes, yes it could, curious. But then I noticed something even more interesting.

The POST request I received from the remote server didn't just contain the usual headers, but also a custom header named X-User-Token, which held a full copy of the user's authentication token. Suddenly with a single call to the vulnerable script I would be handed the security token - and therefore the account - of the current user. Cool.

The user's authentication token

This is where things got really interesting. I found that the request to my server also contained an Authorization header with another, distinct security token, apparently used for internal authentication between services. I pasted the token into the JWT debugger and out popped the name of the user whose token I was holding - webproxy. I was initially excited when I found that the user had some permissions I hadn't seen before, but after some testing the account's extra permissions sadly turned out to be fairly limited in scope.

By this point I had a few other scripts which took a URL parameter and suffered from similar out-of-band request issues, each slightly different in the way it needed to be called to perform the desired action. With some patience I tried each of the URLs in turn and sure enough out popped the webproxy user each time.

Until one of the requests contained something different.


It would be fun to disclose the name of the superuser account because some thought had gone into it, but I don't want to give away too much. Suffice it to say that when I checked the permissions on this new token they were very impressive. Superhero-like, even. I set the token as my own and tried out a few API calls.

I could do everything. Access personal information of another account? Check. Change their password? Check. List and download their personal files? Check, check check. On the very last day of the bounty I'd hit the jackpot.

The response was swift. The bugs were escalated, validated, and 21 hours later patches were pushed.


It's always nice to see a vendor of this size taking positive steps and engaging with the security community. Hopefully running invite-only bounties like this will give vendors the confidence to run public bounties and lead others in the industry to run similar programmes for the benefit of users everywhere.