Tuesday 3 November 2020

From a 500 error to Django admin takeover

This bug is about a private target I was hunting. I passed all the subdomains to FFUF, a great tool written in GoLang to brute force directories. 

Since there were no interesting 200 responses against my wordlist. I started checking other responses like 302, 403, etc. 

I noticed one of the subdomains (let that be sub.vulnerable.com) gave a 500 error for the endpoint 

/api-docs/


This was interesting because when tried an endpoint /anything, it returned 404. So I was quite sure that /api-docs existed but needs more privileges or something like that...

I tried lots of methods, but nothing worked. So I just went back to test the main application. I signed up, and I had an intuition to refresh the page in the next tab, which was /api-docs/

PFFF, to my surprise, I could see the API docs now, which was a swagger UI. 



I after seeing the admin-api, I thought I had hit a goldmine. But that was not the case. None of the admin-API's were working. So I started looking at the normal API endpoints. 


Two of the endpoints were interesting:

1. /api/panel/v1/users/{id}/

Where the {id} value is an integral value. I automated the request for 1 to 100 numerical values, and I was able to fetch other user's email addresses, DOB, name, etc. by filtering all 200 responses.



 2. A similar endpoint and similar leak with search functionality

/api/panel/v1/users/?page=1&page_size=1&search=cyberboy


I reported the bug, and the team asked me if I could raise the severity. 

Challenge accepted!


I started digging the endpoint again  /api/panel/v1/users/{id}/

and notice a JSON value in response "is_staff:false"

So it is pretty easy to guess that there are different roles for the website. I wanted to know more about the roles, so I thought of looking at the admin account's privileges, and they would-be some developers of the company. It was easy to find the developers' names in public places and then pass the name to the search API and fetch their data if I get lucky.

So when I did this, one of the names worked, and I got an admin account information.

/api/panel/v1/users/?page=1&page_size=1&search=NAME_OF_A_DEV_I_KNEW


Now I have a few more interesting roles. 

"is_superuser":true

"is_staff":true

"lms_role":"super_admin"


But again, how do I access an admin account. I tried and failed. So I thought, what if I am modified to these privileges. The first thing I tried was making a POST request with the above JSON value to my own ID


POST /api/panel/v1/users/{id}/ HTTP/1.1

{
"is_superuser":true
"is_staff":true
"lms_role":"super_admin"
}

Unfortunately, a 500 error.

The next thing would be to try the PATCH method. 

PATCH /api/panel/v1/users/{id}/ HTTP/1.1

{
"is_superuser":true
"is_staff":true
"lms_role":"super_admin"
}

I got 200, okay. 

I logged into my account, and I was a staff and a superuser who could edit and modify contents. 

My curiosity did not stop so, I searched for "lms_role" turns out; it's a learning management system and had something to do with Django. So I just opened /admin. And I was greeted with welcome "Cyberboy."

Here we go.. I am the admin now :)



Mission accomplished!
I informed the team that I would like to stop here, and they agreed. 

Bounty reward 3000$