Insecure Direct Object References (IDOR)
The main takeaway is that an IDOR vulnerability mainly exists due to the lack of an access control on the back-end
. If a user had direct references to objects in a web application that lacks access control, it would be possible for attackers to view or modify other users' data.
Identifying IDORs
The very first step of exploiting IDOR vulnerabilities is identifying Direct Object References. Whenever we receive a specific file or resource, we should study the HTTP requests to look for URL parameters or APIs with an object reference (e.g. ?uid=1
or ?filename=file_1.pdf
). These are mostly found in URL parameters or APIs but may also be found in other HTTP headers, like cookies.
In the most basic cases, we can try incrementing the values of the object references to retrieve other data, like (?uid=2
) or (?filename=file_2.pdf
). We can also use a fuzzing application to try thousands of variations and see if they return any data. Any successful hits to files that are not our own would indicate an IDOR vulnerability.
AJAX Calls
We may also be able to identify unused parameters or APIs in the front-end code in the form of JavaScript AJAX calls. Some web applications developed in JavaScript frameworks may insecurely place all function calls on the front-end and use the appropriate ones based on the user role.
This is not unique to admin functions, of course, but can also be any functions or calls that may not be found through monitoring HTTP requests. The following example shows a basic example of an AJAX call:
Understand Hashing/Encoding
Some web applications may not use simple sequential numbers as object references but may encode the reference or hash it instead. If we find such parameters using encoded or hashed values, we may still be able to exploit them if there is no access control system on the back-end.
Compare User Roles
If we want to perform more advanced IDOR attacks, we may need to register multiple users and compare their HTTP requests and object references. This may allow us to understand how the URL parameters and unique identifiers are being calculated and then calculate them for other users to gather their data.
Mass IDOR Enumeration
We are assuming our logged in user has uid = 1
Insecure Parameters
Checking the file links, we see that they have individual names:
We see that the files have a predictable naming pattern, as the file names appear to be using the user uid
and the month/year as part of the file name, which may allow us to fuzz files for other users. This is the most basic type of IDOR vulnerability and is called static file IDOR
We see that the page is setting our uid
with a GET
parameter in the URL as (documents.php?uid=1
). If the web application uses this uid
GET parameter as a direct reference to the employee records it should show, we may be able to view other employees' documents by simply changing this value.
When we try changing the uid
to ?uid=2
, we don't notice any difference in the page output, as we are still getting the same list of documents, and may assume that it still returns our own documents:
However, we must be attentive to the page details during any web pentest
and always keep an eye on the source code and page size. If we look at the linked files, or if we click on them to view them, we will notice that these are indeed different files, which appear to be the documents belonging to the employee with uid=2
Mass Enumeration
We can try manually accessing other employee documents with uid=3
, uid=4
, and so on. However, manually accessing files is not efficient in a real work environment with hundreds or thousands of employees. So, we can either use a tool like Burp Intruder
or ZAP Fuzzer
to retrieve all files or write a small bash script to download all files, which is what we will do.
We can pick any unique word to be able to grep
the link of the file. In our case, we see that each link starts with <li class='pure-tree_link'>
, so we may curl
the page and grep
for this line.
As we can see, we were able to capture the document links successfully. We may now use specific bash commands to trim the extra parts and only get the document links in the output. However, it is a better practice to use a Regex
pattern that matches strings between /document
and .pdf
, which we can use with grep
to only get the document links, as follows:
Now, we can use a simple for
loop to loop over the uid
parameter and return the document of all employees, and then use wget
to download each document link:
When we run the script, it will download all documents from all employees with uids
between 1-10, thus successfully exploiting the IDOR vulnerability to mass enumerate the documents of all employees.
Bypassing Encoded References
Example:
We see that it is sending a POST
request to download.php
with the following data:
Using a download.php
script to download files is a common practice to avoid directly linking to files, as that may be exploitable with multiple web attacks. In this case, the web application is not sending the direct reference in cleartext but appears to be hashing it in an md5
format. Hashes are one-way functions, so we cannot decode them to see their original values.
We can attempt this with various other fields, but none of them matches our hash. In advanced cases, we may also utilize Burp Comparer
and fuzz various values and then compare each to our hash to see if we find any matches.
Function Disclosure
As most modern web applications are developed using JavaScript frameworks, like Angular
, React
, or Vue.js
, many web developers may make the mistake of performing sensitive functions on the front-end, which would expose them to attackers.
In the example: If we take a look at the link in the source code, we see that it is calling a JavaScript function with javascript:downloadContract('1')
. Looking at the downloadContract()
function in the source code, we see the following:
So base64 of uid -> MD5
We can test this by base64
encoding our uid=1
, and then hashing it with md5
, as follows:
We are using the -n
flag with echo
, and the -w 0
flag with base64
, to avoid adding newlines, in order to be able to calculate the md5
hash of the same value, without hashing newlines, as that would change the final md5
hash.
With that, we can begin enumerating other employees' contracts using the same hashing method we used above. Before continuing, try to write a script similar to what we used in the previous section to enumerate all contracts
.
Mass Enumeration
Once again, let us write a simple bash script to retrieve all employee contracts. More often than not, this is the easiest and most efficient method of enumerating data and files through IDOR vulnerabilities. In more advanced cases, we may utilize tools like Burp Intruder
or ZAP Fuzzer
, but a simple bash script should be the best course for our exercise.
We can start by calculating the hash for each of the first ten employees using the same previous command while using tr -d
to remove the trailing -
characters, as follows:
Next, we can make a POST
request on download.php
with each of the above hashes as the contract
value, which should give us our final script:
IDOR in Insecure APIs
So far, we have only been using IDOR vulnerabilities to access files and resources that are out of our user's access. However, IDOR vulnerabilities may also exist in function calls and APIs, and exploiting them would allow us to perform various actions as other users.
While IDOR Information Disclosure Vulnerabilities
allow us to read various types of resources, IDOR Insecure Function Calls
enable us to call APIs or execute functions as another user.
We see that the page is sending a PUT
request to the /profile/api.php/profile/1
API endpoint. PUT
requests are usually used in APIs to update item details, while POST
is used to create new items, DELETE
to delete items, and GET
to retrieve item details.
So, a PUT
request for the Update profile
function is expected. The interesting bit is the JSON parameters it is sending:
We see that the PUT
request includes a few hidden parameters, like uid
, uuid
, and most interestingly role
, which is set to employee
. The web application also appears to be setting the user access privileges (e.g. role
) on the client-side, in the form of our Cookie: role=employee
cookie, which appears to reflect the role
specified for our user.
So, unless the web application has a solid access control system on the back-end, we should be able to set an arbitrary role for our user, which may grant us more privileges
. However, how would we know what other roles exist?
Exploiting Insecure APIs
There are a few things we could try in this case:
Change our
uid
to another user'suid
, such that we can take over their accountsChange another user's details, which may allow us to perform several web attacks
Create new users with arbitrary details, or delete existing users
Change our role to a more privileged role (e.g.
admin
) to be able to perform more actions
Trying to change the uid, first in the JSON body then in the URL too, doesn't work in the example.
Let's see if we can create a new user with a POST
request to the API endpoint. We can change the request method to POST
, change the uid
to a new uid
, and send the request to the API endpoint of the new uid
but no luck. The web application might be checking our authorization through the role=employee
cookie because this appears to be the only form of authorization in the HTTP request.
Finally, let's try to change our role
to admin
/administrator
to gain higher privileges. Unfortunately, without knowing a valid role
name, we get Invalid role
in the HTTP response, and our role
does not update.
Chaining IDOR Vulnerabilities
Using a GET
request in the previous example gives us information about other users, including their roles and the uuid.
Modifying Other Users' Details
Now, with the user's uuid
at hand, we can change this user's details by sending a PUT
request to /profile/api.php/profile/2
with the above details along with any modifications we made. We don't get any access control error messages this time, and when we try to GET
the user details again, we see that we did indeed update their details.
One type of attack is modifying a user's email address
and then requesting a password reset link, which will be sent to the email address we specified, thus allowing us to take control over their account. Another potential attack is placing an XSS payload in the 'about' field
, which would get executed once the user visits their Edit profile
page, enabling us to attack the user in different ways.
Chaining Two IDOR Vulnerabilities
Since we have identified an IDOR Information Disclosure vulnerability, we may also enumerate all users and look for other roles
, ideally an admin role.
IDOR Prevention
We learned various ways to identify and exploit IDOR vulnerabilities in web pages, web functions, and API calls. By now, we should have understood that IDOR vulnerabilities are mainly caused by improper access control on the back-end servers. To prevent such vulnerabilities, we first have to build an object-level access control system and then use secure references for our objects when storing and calling them.
Object-Level Access Control
An Access Control system should be at the core of any web application since it can affect its entire design and structure. To properly control each area of the web application, its design has to support the segmentation of roles and permissions in a centralized manner. However, Access Control is a vast topic, so we will only focus on its role in IDOR vulnerabilities, represented in Object-Level
access control mechanisms.
User roles and permissions are a vital part of any access control system, which is fully realized in a Role-Based Access Control (RBAC) system. To avoid exploiting IDOR vulnerabilities, we must map the RBAC to all objects and resources. The back-end server can allow or deny every request, depending on whether the requester's role has enough privileges to access the object or the resource.
Once an RBAC has been implemented, each user would be assigned a role that has certain privileges. Upon every request the user makes, their roles and privileges would be tested to see if they have access to the object they are requesting. They would only be allowed to access it if they have the right to do so.
There are many ways to implement an RBAC system and map it to the web application's objects and resources, and designing it in the core of the web application's structure is an art to perfect. The following is a sample code of how a web application may compare user roles to objects to allow or deny access control:
Code: javascript
The above example uses the user
token, which can be mapped from the HTTP request made to the RBAC
to retrieve the user's various roles and privileges. Then, it only allows read/write access if the user's uid
in the RBAC system matches the uid
in the API endpoint they are requesting. Furthermore, if a user has admin
as their role in the back-end RBAC, they are allowed read/write access.
In our previous attacks, we saw examples of the user role being stored in the user's details or in their cookie, both of which are under the user's control and can be manipulated to escalate their access privileges. The above example demonstrates a safer approach to mapping user roles, as the user privileges were not be passed through the HTTP request
, but mapped directly from the RBAC on the back-end using the user's logged-in session token as an authentication mechanism.
There's a lot more to access control systems and RBACs, as they can be some of the most challenging systems to design. This, however, should give us an idea of how we should control user access over web applications' objects and resources.
Object Referencing
While the core issue with IDOR lies in broken access control (Insecure
), having access to direct references to objects (Direct Object Referencing
) makes it possible to enumerate and exploit these access control vulnerabilities. We may still use direct references, but only if we have a solid access control system implemented.
Even after building a solid access control system, we should never use object references in clear text or simple patterns (e.g. uid=1
). We should always use strong and unique references, like salted hashes or UUID
's. For example, we can use UUID V4
to generate a strongly randomized id for any element, which looks something like (89c9b29b-d19f-4515-b2dd-abb6e693eb20
). Then, we can map this UUID
to the object it is referencing in the back-end database, and whenever this UUID
is called, the back-end database would know which object to return. The following example PHP code shows us how this may work:
Code: php
Furthermore, as we have seen previously in the module, we should never calculate hashes on the front-end. We should generate them when an object is created and store them in the back-end database. Then, we should create database maps to enable quick cross-referencing of objects and references.
Finally, we must note that using UUID
s may let IDOR vulnerabilities go undetected since it makes it more challenging to test for IDOR vulnerabilities. This is why strong object referencing is always the second step after implementing a strong access control system. Furthermore, some of the techniques we learned in this module would work even with unique references if the access control system is broken, like repeating one user's request with another user's session, as we have previously seen.
If we implement both of these security mechanisms, we should be relatively safe against IDOR vulnerabilities.
Last updated