Finding More IDORs – Tips And Tricks ($100/Day)

1 year ago 96
BOOK THIS SPACE FOR AD
ARTICLE AD
Object scope: Is it private or public?

Start by looking at what objects should be publicly readable versus the ones that should not be. For instance, in an online store, product details and reviews associated with each product may be readable by the general public using the following endpoints:

GET /api/products/<product_id>GET /api/reviews/<product_id>

However, the buyer’s private profile information should not be:

GET /api/users/<user_id>/creditcard/details
GET /api/users/<user_id>/mailingaddress/details

If the API structure looks something like the above, then you can assume that the user-related information should only be visible to that user. That is one of the best places to start testing for high-impact IDORs.

Pro tip: Don’t forget to try create/update/delete operations on objects that are publicly readable but shouldn’t be writable. Can you PUT to /api/products and change a price?

If you see an endpoint that exposes a resource in typical fashion such as:

GET /api/albums/<album_id>/photos/<photo_id>

Think about what other directories and endpoints there are likely to be in the API. Tools such as Burp Suite Intruder or FFUF are great here when combined with API-specific wordlists. If regular API wordlists are not finding anything, then consider using a tool like CeWL which will generate custom wordlists for individual applications. Sometimes there will be endpoints that the web app itself rarely hits, but you can send your own requests to them if you find one. These can be gold mines! In the above example, you could try looking for:

GET /api/posts/<post_id>/comment/…GET /api/users/<user_id>/details/…

We can also provide alternative values for each section and test to see if they exist. For example, version 1 of the API may have the appropriate access controls in place, but perhaps version 2 is not fully rolled out yet. You may find that version 2 of the API is still accessible if you make calls directly to it and that it lacks the access controls as it is not finished.

/service/v1/users/<user_id>

Where:

service: application contextv1: versionusers: resource<user_id>: parameter

At times, simply providing an API with information that it does not require or expect may produce results. Try adding object identifiers to requests that didn’t originally have them. The application might mistakenly honor the unexpected ID parameter without subjecting it to access control. Something like the following example might get you access to another user’s photo album:

GET /api/MyPictureList → /api/MyPictureList?user_id=<other_user_id>

Some applications will accept client-supplied ID values when creating an object. This ID value can be anything. If you do not see parameters like id, user_id, account_id, pid or picture_id while creating an object, you should try adding one yourself and test it. You may find that you are able to assign arbitrary IDs and then reference the objects.

Pro tip: You can find parameter names to try by deleting or editing other objects and seeing the parameter names used.

Once you have spent a decent amount of time testing an application, you may start to remember parameters that have been used throughout it. You can try these parameters in other areas and see if they will be accepted. For example, if you see something like:

GET /api/albums?album_id=<album id>

You could replace the album_id parameter with something completely different and potentially get other data.

GET /api/albums?account_id=<account id>

This could be used to obtain a list of a user’s album_ids, which you could then use in the original query. There is a Burp extension called Paramalyzer which will help with this by remembering all the parameters you have passed to a host.

Similarly, sometimes an API will require an ID and you can try supplying multiple IDs to provoke odd behavior and potentially bypass access control mechanisms. This is known as HTTP parameter pollution. Something like this might get you access to the admin’s account:

GET /api/account?id=<your account id> →
 /api/account?id=<your account id>&id=<admin’s account id>

These are some of the most satisfying bugs to find! Sometimes, applications will only enforce access control on the typical HTTP request method that the client will be making but neglect to implement the same access controls across all HTTP request methods.

Try switching POST and PUT and see if you can upload something to another user’s profile. For RESTful services, try changing GET to POST/PUT/DELETE to discover create/update/delete actions.

Access controls may be inconsistently implemented across different content types. For example, if your app sends XML to the server with a Content-Type header of application/xml, try rewriting the request to use JSON with a Content-Type header of application/json. Don’t forget to try alternative and less common values like text/xml, text/x-json, and similar.

POST /api/chat/join/123
[…]
Content-type: application/xml → Content-type: application/json

<user>test</user>

{“user”: “test”}

When making a second purchase, the client may just be sending a credit card ID number instead of the actual card details themselves. Try looking for and changing this credit card ID number.

Alarm bells! Pay close attention to these types of actions as they are often where you can find IDORs. Can add yourself to another chat simply by changing values?

Similarly, if there is a copy, move or delete function on the web app then this is a good place to try looking for IDORs! Try testing all CRUD functions (Create, Read, Update, Delete) on every endpoint.

This one is simple; it is not very common but sometimes requesting a different file type or extension may be enough to bypass the access control. Experiment by appending different file extensions (e.g. .json, .xml, .config) to the end of requests that reference a document.

There may be multiple ways of referencing objects in the database and the application only has access controls on one. Try numeric IDs anywhere non-numeric IDs are accepted:

username=user1 → username=1234
account_id=7541A92F-0101-4D1E-BBB0-EB5032FE1686 → account_id=5678
album_id=MyPictures → album_id=12

If a regular ID replacement isn’t working, try wrapping the ID in an array and see if that does the trick. For example:

{“id”:19} → {“id”:[19]}

These can be very exciting bugs to find in the wild and are so simple. Try replacing an ID with a wildcard. You might get lucky!

GET /api/users/<user_id>/ → GET /api/users/*

Do not despair. Sometimes applications will throw an error message regardless of if the action you attempted was successful or not. Even if you get an error message, check manually to see if the action worked.

Make sure if you are replacing one ID with another that you replace it in every part of the request, not just in one instance. This is an easy mistake to make and could cause you to overlook potential vulnerabilities.

These can be tricky and daunting at first. The best option is to try to decode it by looking at a few different IDs. This may reveal that only a small part of the ID is changing in each instance. Knowing this may help you narrow down the link between the decrypted input and the encrypted output.

If you receive an “access denied” response from the server such as a 401 Unauthorized, it’s worth trying other object identifiers to make sure the access controls are uniformly applied. You can use something like Burp Suite Intruder to quickly enumerate identifiers.

Read Entire Article