This post assumes that you have a fair understanding of what an IDOR is. If you are not familiar with IDORs, I would recommend reading the below articles before moving ahead:
Whenever I start application testing, IDORs is the first category of bugs I look for. Two reasons behind it:
- Easy to test
- Straightforward to conclude whether vulnerable or not
Historically, system developers have been assigning auto-incrementing integer identifiers to database rows. This made it trivial to exploit IDORs. Just one vulnerability could allow exfiltration of all objects belonging to any user in the application. Same goes for delete or edit IDORs.
With the introduction of MongoDB, developers started depending on Mongo Object IDs as identifiers. Mongo Object IDs provided some degree of randomness to the identifiers. However, these object IDs are not truly random. Infact, they are barely random. It is still trivial to predict Mongo Object IDs based just one know valid Object ID from the application.
Structure of a Mongo Object ID:
Mongo Object IDs are 12-byte hexadecimal strings. Calling it a string is not technically correct, but for the sake of this post, it is OK to consider them as strings. From a character length perspective, they are made of 24 hexadecimal numbers. These 12 bytes or 24 characters are not entirely random. MongoDB Object IDs follow the following pattern:
For example, here’s how we can dissect an actual Object ID returned by an application: 5f2459ac9fa6dc2500314019
- 5f2459ac: 1596217772 in decimal = Friday, 31 July 2020 17:49:32
- 9fa6dc: Machine Identifier
- 2500: Process ID
- 314019: An incremental counter
Of the above elements, machine identifier will remain the same for as long as the database is running the same physical/virtual machine. Process ID will only change if the MongoDB process is restarted. Timestamp will be updated every second.
The only challenge in guessing Object IDs by simply incrementing the counter and timestamp values, is the fact that Mongo DB generates Object IDs and assigns Object IDs at a system level. Object IDs are not incremented at a collection (Mongo equivalent for relational database tables) level. Say, for example, if you are testing an extremely busy application, and two users register on the application within a difference of 1 second. If there were no other Object IDs being generated on the system apart from these users, the last part of the Object ID would just be incremented by 1 and the timestamp would be incremented by 1 second. But on a really busy system, MongoDB can generate thousand of Object IDs per second. So, although these users were created just 1 second apart, their counters can be thousand places away from each other.
Although not trivial, it is still fairly predictable. You do not need to predict Object IDs for testing an IDOR. For testing, you can just create two accounts like usual and try accessing other user’s information using the identifier. Once, you have established that an endpoint is vulnerable to IDOR, you can then go ahead to predict Object IDs to increase severity of the vulnerability.
I found this really useful tool written by andresriancho: https://github.com/andresriancho/mongo-objectid-predict
With the default setting, given a seed Object ID, it sends back about 1000 probable Object IDs that could have possibly been assigned to your victim’s resource. Here’s what the output looks like:
Once, you have the list of valid Object IDs, you can import these values in Burp intruder or run them on a custom script to test if they are valid.
If you have any feedback or would like to ask any questions related to hacking, hit me up on twitter @ameyanekar. My DMs are open.