Learn Web Pentesting: Invariants and Feedback Loops

Published: 28 Jan 2025

We recently released a lab on MongoDB IDOR and how to guess ObjectIds. Basically, you need to find the ObjectId of an account that was created 7 days ago. In those 7 days, a few accounts were created and deleted to make things a bit more fun.

We have had plenty of people struggling with this lab. For good reasons, it’s not an obvious lab. You cannot just ./hack_objectid to solve it, or at least not to solve it efficiently. This forces people to write their own script. Your script should take an ObjectId, retrieve the current timestamp and counter from that ObjectId, and then play with both values to try to find the admin account.

This is where you can see the power of two things when trying to exploit vulnerabilities: Invariants and Short Feedback Loops.

Without Invariants and Short Feedback Loops

Basically, one of our subscribers will write a script that attacks the online lab to try to find the ObjectId to get the key to solve the challenge. It won't work the first time; they will tweak the code, run it again; it won't work, they will tweak the code again and re-run the code. The problem is that every single change costs around 10 minutes of testing. To make matters worse, you have nothing to do during those 10 minutes, so people tend to get distracted—a few YouTube Shorts, some TikTok, and maybe a bit of social media... This quickly becomes a nightmare to stay focused on solving the lab.

Leveraging Invariants

There is a good chance that decoding and re-encoding the ObjectId may go wrong, especially if you haven’t done it before. To make sure your code does the right thing, you need to find invariants that you can leverage—basically, ways to validate your code.

A mathematical example would be to try to compute 3x4. You should get 12. Then you can do 12/3 and 12/4 to make sure you get 4 and 3. This allows you to check that the value 12 is correct.

We want to find something similar for our ObjectId. Once we have written our decoding (let’s call it DEC) and encoding (ENC) functions, we take the ObjectId and check that:

timestamp, uniq_value, counter = DEC(objectId) 
if ObjectId == ENC(timestamp, uniq_value, counter) 
  // WIN 
else 
  // There is something wrong with our decoding and encoding 

A very simple check, but it will prevent a lot of problems and save a lot of testing time. It doesn’t prevent issues in both functions that may cancel each other out, though. To account for this, we can leverage a third-party website or check that the timestamp we just created and decoded matches the current time (that we can retrieve using date +%s on Unix for example).

Short feedback loops

Now that we know our encoding/decoding seems to work, it’s time to leverage Short Feedback Loops. We know that the ObjectId was created around 7 days ago. Let’s mimic that code and see if we can find it.

timestamp, uniq_value, counter = DEC(objectId) 
timestamp -= 7*24*60*60 // 7 days 
counter -= 10 
newobjectid = ENC(timestamp, uniq_value, counter) 

We get our new ObjectId named newobjectid, and we can now try to find it locally by writing code that will just search for this value:

timestamp, uniq_value, counter = DEC(objectId) 
for timestamp_delta in range(-10, 10): 
  for counter_delta in range(-12, 0): 
    test = ENC(timestamp + timestamp_delta, uniq_value, counter + counter_delta)
    if test == newobjectid 
      // WIN !! 
    ... 

This won’t guarantee that our exploit works, but it will increase the likelihood that it does and will speed up development significantly.

Conclusion

As a security tester, you have to leverage Invariants and Short Feedback Loops. This is the best way to limit the risk of mistakes and ensure that mistakes are quickly detected. This will save you a ton of time, work, and frustration. Next time you are testing or exploiting a vulnerability, ask yourself: "Can I find invariants to check that my code does what I think it does?" and "How can I create a short feedback loop?" to speed up the process and limit frustration.

Photo of Louis Nyffenegger
Written by Louis Nyffenegger
Founder and CEO @PentesterLab