10
Insecure Direct Object References

Like XSS and open redirects, insecure direct object references (IDORs) are a type of bug present in almost every web application. They happen when the application grants direct access to a resource based on the user’s request, without validation.

In this chapter, we’ll explore how these work. Then we’ll dive into how applications prevent IDORs, and how you can bypass those common protection mechanisms.

Mechanisms

Despite its long and intimidating name, IDOR is easy to understand; it’s essentially a missing access control. IDORs happen when users can access resources that do not belong to them by directly referencing the object ID, object number, or filename.

For example, let’s say that example.com is a social media site that allows you to chat with others. When you sign up, you notice that your user ID on the site is 1234. This website allows you to view all your messages with your friends by clicking the View Your Messages button located on the home page. When you click that button, you get redirected to this location, which displays all your direct messages: https://example.com/messages?user_id=1234.

Now, what if you change the URL in the URL bar to https://example.com/messages?user_id=1233?

You notice that you can now see all the private messages between another user, user 1233, and their friends. At this point, you’ve found an IDOR vulnerability. The application does not restrict access to messages based on the user’s identity. Instead, it allows users to request any messages that they wish. The application naively trusts user input, and it directly loads resources based on the user-provided user_id value, like this piece of example code:

messages = load_messages(request.user_id)
display_messages(messages)

IDORs are not just limited to reading other users’ information, either. You can also use them to edit data on another user’s behalf. For example, let’s say that users can submit a POST request to change their password. The POST request must contain that user’s ID and new password, and they must direct the request to the /change_password endpoint:

POST /change_password

(POST request body)
user_id=1234&new_password=12345

In this case, if the application doesn’t validate that the submitted user ID corresponds to the currently logged-in user, an attacker might be able to change someone else’s password by sending a user ID that doesn’t belong to them, like this:

POST /change_password

(POST request body)
user_id=1233&new_password=12345

Finally, IDORs can affect resources other than database objects. Another type of IDOR happens when applications reference a system file directly. For example, this request allows users to access a file they’ve uploaded: https://example.com/uploads?file=user1234-01.jpeg.

Since the value of the file parameter is user1234–01.jpeg, we can easily deduce that user-uploaded files follow the naming convention of USER_ID-FILE_NUMBER.FILE_EXTENSION. Therefore, another user’s uploaded files might be named user1233–01.jpeg. If the application doesn’t restrict users’ access to files that belong to others, an attacker could access anyone’s uploaded files by guessing the filenames, like this: https://example.com/uploads?file=user1233-01.jpeg.

A malicious user might even be able to read sensitive system files through this endpoint! For instance, /etc/shadow is a file on Unix systems used to keep track of user passwords. Because it is sensitive, it should not be exposed to regular users. If you can read the file this way, through a URL like https://example.com/uploads?file=/PATH/TO/etc/shadow, then you’ve found a vulnerability! Attackers being able to read files outside the web root folder is also known as a path traversal attack, or directory traversal attack. We will talk more about directory traversal attacks in Chapter 17.

Prevention

IDORs happen when an application fails at two things. First, it fails to implement access control based on user identity. Second, it fails to randomize object IDs and instead keeps references to data objects, like a file or a database entry, predictable.

In this chapter’s first example, you were able to see messages belonging to user 1233 because the server didn’t check the logged-in user’s identity before sending private info. The server wasn’t verifying that you were, in fact, user 1233. It simply returned the information you asked for.

In this case, since user IDs are simply numbers, it’s easy to infer that you can also retrieve the messages for user 1232 and user 1231, like so:

  1. https://example.com/messages?user_id=1232
  2. https://example.com/messages?user_id=1231

This is why the vulnerability is called an insecure direct object reference. The user’s ID is used to directly reference the user’s private messages on this site. If not secured by proper access control, these predictable direct object references expose the data hidden behind them, allowing anyone to grab the information associated with the reference.

Applications can prevent IDORs in two ways. First, the application can check the user’s identity and permissions before granting access to a resource. For example, the application can check if the user’s session cookies correspond to the user_id whose messages the user is requesting.

Second, the website can use a unique, unpredictable key or a hashed identifier to reference each user’s resources. Hashing refers to the one-way process that transforms a value into another string. Hashing IDs with a secure algorithm and a secret key makes it difficult for attackers to guess the hashed ID strings. If example.com structured its requests as follows, attackers would no longer be able to access other users’ messages, since there would be no way for an attacker to guess such a long, random user_key value:

https://example.com/messages?user_key=6MT9EalV9F7r9pns0mK1eDAEW

But this method isn’t a complete protection against IDORs. Attackers can still leak user information if they can find a way to steal these URLs or user_keys. The best way to protect against IDORs is fine-grained access control, or a combination of access control and randomization or hashing of IDs.

Hunting for IDORs

Let’s hunt for some IDORs! The best way to discover IDORs is through a source code review that checks if all direct object references are protected by access control. We’ll talk about how to conduct source code reviews in Chapter 22. But if you cannot access the application’s source code, here’s a simple and effective way to test for IDORs.

Step 1: Create Two Accounts

First, create two different accounts on the target website. If users can have different permissions on the site, create two accounts for each permission level. For example, create two admin accounts, two regular user accounts, two group member accounts, and two non-group-member accounts. This will help you test for access control issues among similar user accounts, as well as across users with different privileges.

Continuing the previous example, you could create two accounts on example.com: user 1235 and user 1236. One of the accounts would serve as your attacker account, used to carry out the IDOR attacks. The other would be the victim account used to observe the effects of the attack. The message pages for the two users would have the following URLS:

  1. https://example.com/messages?user_id=1235 (Attacker)
  2. https://example.com/messages?user_id=1236 (Victim)

If the application doesn’t allow you to create so many accounts, you could reach out to the company and ask for more accounts. Companies will often grant you extra accounts if you explain that you’re participating in their bug bounty program. Also, if the application has paid memberships, ask the company for a premium account or pay for one yourself. Quite often, paying for these memberships is worth it, because you gain access to new features to test.

In addition to testing with two accounts, you should also repeat the testing procedure without signing in. See if you can use an unauthenticated session to access the information or functionalities made available to legitimate users.

Step 2: Discover Features

Next, try to discover as many application features as possible. Use the highest-privileged account you own and go through the application, looking for application features to test.

Pay special attention to functionalities that return user information or modify user data. Note them for future reference. Here are some features that might have IDORs on example.com:

  1. This endpoint lets you read user messages:
https://example.com/messages?user_id=1236
  1. This one lets you read user files:
https://example.com/uploads?file=user1236-01.jpeg
  1. This endpoint deletes user messages:
POST /delete_message

(POST request body)
message_id=user1236-0111
  1. This one is for accessing group files:
https://example.com/group_files?group=group3
  1. This one deletes a group:
POST /delete_group

(POST request body)
group=group3

Step 3: Capture Requests

Browse through each application feature you mapped in the preceding step and capture all the requests going from your web client to the server. Inspect each request carefully and find the parameters that contain numbers, usernames, or IDs. Remember that you can trigger IDORs from different locations within a request, like URL parameters, form fields, filepaths, headers, and cookies.

To make testing more efficient, use two browsers, and log into a different account in each. Then manipulate the requests coming from one browser to see if the change is immediately reflected on the other account. For example, let’s say you create two accounts, 1235 and 1236. Log into 1235 in Firefox and 1236 in Chrome.

Use Burp to modify the traffic coming from Firefox. Turn on Intercept in the Proxy tab and edit requests in the proxy text window (Figure 10-1). Check if your attack has succeeded by observing the changes reflected on the victim account in Chrome.

Also, note that APIs like Representational State Transfer (REST) and GraphQL are often found to be vulnerable to IDOR too. We will talk more about hacking APIs in Chapter 24. Be on the lookout for these endpoints. You can use the recon techniques from Chapter 5 to discover additional endpoints. Then follow this testing methodology to switch out IDs found in those endpoints as well.

f10001

Figure 10-1: Modify the request in Burp’s proxy window to switch out the IDs.

Step 4: Change the IDs

Finally, switch the IDs in the sensitive requests and check if the information returned also changes. See if you can access the victim account’s information by using the attacker account. And check if you can modify the second user’s account from the first.

For example, in this setup, you can try to access the functionalities that user 1236 has access to via your Firefox browser:

  1. This endpoint lets you read user messages:
https://example.com/messages?user_id=1236
  1. This one lets you read user files:
https://example.com/uploads?file=user1236-01.jpeg
  1. This endpoint deletes user messages:
POST /delete_message

(POST request body)
message_id=user1236-0111
  1. This one is for accessing group files:
https://example.com/group_files?group=group3
  1. This endpoint deletes a group:
POST /delete_group

(POST request body)
group=group3

If any of these requests succeed in accessing or modifying user 1236’s information, you’ve found an IDOR vulnerability.

Bypassing IDOR Protection

IDORs aren’t always as simple as switching out a numeric ID. As applications become more functionally complex, the way they reference resources also often becomes more complex. Modern web applications have also begun implementing more protection against IDORs, and many now use more complex ID formats. This means that simple, numeric IDORs are becoming rarer. How do we bypass these obstacles and find IDORs anyway?

IDORs can manifest in applications in different ways. Here are a few places to pay attention to, beyond your plain old numeric IDs.

Encoded IDs and Hashed IDs

First, don’t ignore encoded and hashed IDs. When faced with a seemingly random string, always suspect that it is encoded and try to decode it. You should also learn to recognize the most common encoding schemes, like base64, URL encoding, and base64url. For example, take a look at the IDs of this endpoint:

  1. https://example.com/messages?user_id=MTIzNQ
  2. https://example.com/messages?user_id=MTIzNg

These user_ids are just the base64url-encoded version of a user’s ID. MTIzNQ is the base64url-encoded string of 1235, and MTIzNg is the encoded version of 1236. Some applications use encoding schemes that you can easily reverse. In this case, you can simply encode your false IDs by using an online base64url encoder and executing the IDOR.

You might not be able to tell which encoding scheme the site is using at first. In this case, use the Smart Decode tool (Figure 10-2) in Burp’s decoder, or simply try to decode the string with different schemes (URL encoding, HTML encoding, hex encoding, octal encoding, base64, base64url, and so on) to figure out the encoding scheme in use. Once you gain more experience reading encoded data, you’ll develop an intuition for knowing the encoding scheme.

f10002

Figure 10-2: You can try to use different methods to decode a string in Burp’s decoder. Or you can use the Smart Decode tool and see if Burp can detect the encoding scheme.

If the application is using a hashed or randomized ID, see if the ID is predictable. Sometimes applications use algorithms that produce insufficient entropy. Entropy is the degree of randomness of the ID. The higher the entropy of a string, the harder it is to guess. Some IDs don’t have sufficient entropy and can be predicted after careful analysis. In this case, try creating a few accounts to analyze how these IDs are created. You might be able to find a pattern that will allow you to predict IDs belonging to other users.

Leaked IDs

It might also be possible that the application leaks IDs via another API endpoint or other public pages of the application, like the profile page of a user. I once found an API endpoint that allowed users to retrieve detailed direct messages through a hashed conversation_id value. The request looks like this:

GET /messages?conversation_id=O1SUR7GJ43HS93VAR8xxxx

This seems safe at first glance, since the conversation_id is a long, random, alphanumeric sequence. But I later found that anyone could request a list of conversation_ids for each user, just by using their public user ID! The following request would return a list of conversation_ids belonging to that user:

GET /messages?user_id=1236

Since the user_id is publicly available on each user’s profile page, I could read any user’s messages by first obtaining their user_id on their profile page, retrieving a list of conversation_ids belonging to that user, and finally loading the messages via their conversation_ids.

Offer the Application an ID, Even If It Doesn’t Ask for One

In modern web applications, you’ll commonly encounter scenarios in which the application uses cookies instead of IDs to identify the resources a user can access.

For example, when you send the following GET request to an endpoint, the application will deduce your identity based on your session cookie, and then send you the messages associated with that user:

GET /api_v1/messages
Host: example.com
Cookies: session=YOUR_SESSION_COOKIE

Since you don’t know another user’s session cookies, you cannot use those session cookies to read their messages. This might make it seem like the application is safe from IDORs. But some applications will implement an alternative way of retrieving resources, using object IDs. They sometimes do this for the convenience of the developers, for backward compatibility, or just because developers forgot to remove a test feature.

If no IDs exist in the application-generated request, try adding one to the request. Append id, user_id, message_id, or other object references to the URL query, or the POST body parameters, and see if it makes a difference to the application’s behavior. For example, say this request displays your messages:

GET /api_v1/messages

Then maybe this request would display another user’s messages instead:

GET /api_v1/messages?user_id=ANOTHER_USERS_ID

Keep an Eye Out for Blind IDORs

Still, sometimes endpoints susceptible to IDOR don’t respond with the leaked information directly. They might lead the application to leak information elsewhere, instead: in export files, email, and maybe even in text alerts. For example, imagine that this endpoint on example.com allows users to email themselves a copy of a receipt:

POST /get_receipt

(POST request body)
receipt_id=3001

This request will send a copy of receipt 3001 to the registered email of the current user. Now, what if you were to request a receipt that belongs to another user, receipt 2983?

POST /get_receipt

(POST request body)
receipt_id=2983

While the HTTP response does not change, you may get a copy of receipt 2983 in your email inbox! Often a malicious request can cause an info leak sometime in the future. I once found an IDOR that led to an info leak one month later, in a monthly report.

Change the Request Method

If one HTTP request method doesn’t work, you can try plenty of others instead: GET, POST, PUT, DELETE, PATCH, and so on. Applications often enable multiple request methods on the same endpoint but fail to implement the same access control for each method. For example, if this GET request is not vulnerable to IDOR and doesn’t return another user’s resources

GET example.com/uploads/user1236-01.jpeg

you can try to use the DELETE method to delete the resource instead. The DELETE method removes the resource from the target URL:

DELETE example.com/uploads/user1236-01.jpeg

If POST requests don’t work, you can also try to update another user’s resource by using the PUT method. The PUT method updates or creates the resource at the target URL:

PUT example.com/uploads/user1236-01.jpeg

(PUT request body)
NEW_FILE

Another trick that often works is switching between POST and GET requests. If there is a POST request like this one

POST /get_receipt

(POST request body)
receipt_id=2983

you can try rewriting it as a GET request, like this:

GET /get_receipt?receipt_id=2983

Change the Requested File Type

Switching the file type of the requested file sometimes leads the server to process the authorization differently. Applications might be flexible about how the user can identify information: they could allow users to either use IDs to reference a file or use the filename directly. But applications often fail to implement the same access controls for each method of reference.

For example, applications commonly store information in the JSON file type. Try adding the .json extension to the end of the request URL and see what happens. If this request is blocked by the server

GET /get_receipt?receipt_id=2983

then try this one instead:

GET /get_receipt?receipt_id=2983.json

Escalating the Attack

The impact of an IDOR depends on the affected function, so to maximize the severity of your bugs, you should always look for IDORs in critical functionalities first. Both read-based IDORs (which leak information but do not alter the database) and write-based IDORs (which can alter the database in an unauthorized way) can be of high impact.

In terms of the state-changing, write-based IDORs, look for IDORs in password reset, password change, and account recovery features, as these often have the highest business impact. Target these over, say, a feature that changes email subscription settings.

As for the non-state-changing (read-based) IDORs, look for functionalities that handle the sensitive information in the application. For example, look for functionalities that handle direct messages, personal information, and private content. Consider which application functionalities make use of this information and look for IDORs accordingly.

You can also combine IDORs with other vulnerabilities to increase their impact. For example, a write-based IDOR can be combined with self-XSS to form a stored XSS. An IDOR on a password reset endpoint combined with username enumeration can lead to a mass account takeover. Or a write IDOR on an admin account may even lead to RCE! We’ll talk about RCEs in Chapter 18.

Automating the Attack

After you get the hang of hunting for IDORs, you can try to automate IDOR hunting by using Burp or your own scripts. For example, you can use the Burp intruder to iterate through IDs to find valid ones. The Burp extension Autorize (https://github.com/Quitten/Autorize/) scans for authorization issues by accessing higher-privileged accounts with lower-privileged accounts, whereas the Burp extensions Auto Repeater (https://github.com/nccgroup/AutoRepeater/) and AuthMatrix (https://github.com/SecurityInnovation/AuthMatrix/) allow you to automate the process of switching out cookies, headers, and parameters. For more information on how to use these tools, go to the Extender tab of your Burp window, then to the BAppStore tab to find the extension you want to use.

Finding Your First IDOR!

Now that you know what IDORs are, how to bypass IDOR protection, and how to escalate IDORs, you’re ready to look for your first one! Hop on a bug bounty program and follow the steps discussed in this chapter:

  1. Create two accounts for each application role and designate one as the attacker account and the other as the victim account.
  2. Discover features in the application that might lead to IDORs. Pay attention to features that return sensitive information or modify user data.
  3. Revisit the features you discovered in step 2. With a proxy, intercept your browser traffic while you browse through the sensitive functionalities.
  4. With a proxy, intercept each sensitive request and switch out the IDs that you see in the requests. If switching out IDs grants you access to other users’ information or lets you change their data, you might have found an IDOR.
  5. Don’t despair if the application seems to be immune to IDORs. Use this opportunity to try a protection-bypass technique! If the application uses an encoded, hashed, or randomized ID, you can try decoding or predicting the IDs. You can also try supplying the application with an ID when it does not ask for one. Finally, sometimes changing the request method type or file type makes all the difference.
  6. Monitor for information leaks in export files, email, and text alerts. An IDOR now might lead to an info leak in the future.
  7. Draft your first IDOR report!
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset