Unlike probably all web developers I love HTTP 500 errors - they often indicate that something went terribly wrong and there might be a vulnerability. In most cases, you are left with something like this

Cute but doesn't say much about the error :(
Cute but doesn't say much about the error :(

But in some, the errors are more verbose and sometimes disclose a little more information than they should about what’s actually happening behind the scenes and what went wrong. And this was the case with this bug.

Intro

I had burp running with Autorize extension - it automatically sends every request that went through proxy again with cookies of another user and without any cookies whatsoever.

The app I was testing can be compared to something like GitHub - You can create public/private projects, collaborate, etc.

A bit of information gathering

There was this feature to duplicate a project. It was a bit weird because it required a JWT token although the rest of the app used cookie-based auth. I tried to mess with it a bit but didn’t find anything useful. Next, I played with a bunch of other endpoints and found another two that were using JWT auth - “download project as zip” and “write files to project”. (Didn’t find any vulnerabilities in them, just noted that they were using JWT)

Some bugs are hidden where you least expect them

Then I looked into Autorize, scrolled a bit, inspected responses, and then noticed that the unauthenticated request to “duplicate project” endpoint returned error 500 and much longer response than any other request. I don’t have a screenshot of that but I think it was something about 8Kb

So I did what any other person would do. Clicked on the response aaand there was a LOT of stuff

There were JWT tokens everywhere! After a while of base64-decoding and comparing stuff, I found out that this was basically a God token. If someone requested /api/{project_id}/duplicate without any authorization it would then create a new user and issue a token with write permission for the supplied project.

This was the base64decoded token:

{
  "userId":"9e46fac3-e0c6-4532-af6c-3fe78444cef2",
  "projectId":"5e822a7b-9563-4ae4-9820-c39bad8b715d",
  "accessType":"edit",
  "iat":158196154
}

Then the app would for some reason error out and throw this token at me, I don’t really know what went wrong because the token was working!

Remember those two JWT endpoints I was talking about earlier?

So now with this token I was able to read or write every project and needed just its uuid to do it - that limits the impact a bit but I still had r/w access to practically every project on the app

Lessons learned

  • Note weird things, patterns of behavior of the app - it can be useful later
  • Learn to use burp extensions
  • Using UUIDs instead of numeric IDs can be a pretty good defense in depth against IDORS (Not perfect though)