WIZER CTF #5: Willy Wonka's Secret

If you're a returning visitor to our CTF Recaps, feel free to dive straight into the insights! For first-time explorers, let us quickly introduce you to the essence of these recaps. Wizer CTFs were introduced to challenge developers, encouraging them to adopt a hacker's mindset and thereby code more securely. This initiative is a pivotal part of our new security awareness training, specially crafted for development teams - Wizer's Secure Code Training for Developers!

After a challenge retires, our Wizer Wizard and CTO, Itzik Spitzen, crafts takeaways that offer valuable insights into the challenge, focusing on the defensive perspective for your script. Curious to test-drive a CTF before delving into the notes? Visit wizer-ctf.com – it's free, and there's something for all skill levels!

Goal

In this fun secure code challenge, we identify a NoSQL Injection and an IDOR (Insecure Direct Object Reference) vulnerabilities which result in gaining unauthorized access to data about a user from a different company.

Description of code

Our code is a pretty straightforward set of API endpoints running NoSQL queries against a MongoDB database. It's intended to provide access to the companies and users data models as well as get a secret-key upon having the user IDs.

The first endpoint (secret.js) fetches a list of secret codes, which are effectively UUIDs, from the database upon providing an array of known User-IDs. The second endpoint (users.js) gets a list of User records upon providing known Company-IDs, in the same way. The last endpoint (companies.js) gets a company record when provided with a known Company-ID, which is also a UUID and is designed to fetch only a single company at a time. The last query is sensitive to a NoSQL Injection.


What’s wrong with that approach?

The approach in the code above bears two main flaws:

1. The /companies endpoint is sensitive to a NoSQL injection. The user input isn't sanitized and hence an attacker is free to inject almost any alternative condition into the field value. The code will insert the company id provided into a query using { company_id:req.body.company_id }. The assumption here is that the user can only ever supply a string, but that is not the case. An attacker can set the req.body.company_id to an Object like { "$ne":null }. Now the full query is { company_id:{ "$ne":null } } . This query will return every document for which the company_id is not equal to null, which is every result. The attacker can use this to get a list of all companies.

2. Relying on UUIDs and assuming that they are non-guessible, is a common mistake among developers. While maybe tough to guess, IDs are used in the app, and once an attacker finds a way to get access to some other UUIDs of existing records, nothing protects the app from serving unauthorized data.

What would a successful IDOR attack look like in this case?

Given the known Company-ID in the instructions, an attacker can start exploring the structure of the data (and quite frankly, even this known company-ID isn't required to exploit the vuln). Quickly one can figure out what they need to do to get Willy Wonka's Secret. By exploiting the /companies endpoint NoSQL Injection vuln, for instance by specifying { "$ne": null } as the Company-ID, an attacker can get the list of companies, then using the /users endpoint, find Willy Wonka's user and ID within one of the companies in the database and use it to get the secret code via the /secret endpoint.

So what?

An IDOR, which in some cases is also referred to as Broken Access Control, is a serious vulnerability. By gaining access to unauthorized data, an attacker can exploit the data itself or take over an admin/superadmin account. One might think, oh, that's not too bad, since you'd need to have access to a valid user account first to take advantage of an IDOR, but in 99% of the cases, that isn't hard, since many apps allow free signups, trials etc. Also, there are other ways to gain access to a valid user context, such as using Phishing emails or other forms of social engineering and exploiting other vulns (i.e. XSS). The other vuln here is a NoSQL Injection, which is a way for an attacker to gain unauthorized access to data as well, hence needless to say, it is dangerous independently.

Main Takeaways:

  • Never count on UUIDs record ID as a form of securing your data:
    Using a sequential ID or a UUID should be considered almost the same security level, which is basically none. You should always assume that there's a way to get those IDs.
  • NoSQL injections are real and exist in the wild:
    Always make sure your code is well protected and the user-input is properly sanitized to prevent undesired access to data. Starting with a list of allowed values (and opening it up as needed) is always better than allowing all values and narrowing it down later.
  • Tighten the logic of the code and validate returned values:
    In this case, /companies endpoint is designed to return a single record, while NoSQL Injection is possible, it is always important to add a logical layer to prevent access to unwanted data, is to check that the returned record is single and has the correct ID.

 

In the context of secure code training, this examination underscores the critical importance of addressing these vulnerabilities promptly. A proactive approach to secure code training is not merely a necessity; it is a strategic imperative in fortifying your system against potential exploits, unauthorized access, and safeguarding the integrity of your data. Embrace secure coding practices, continuously educate your development teams, and remain vigilant in the ever-evolving landscape of cybersecurity.

Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.

Want to learn more about Wizer's Secure Code Training for Developers? Request a demo here.

CODE WIZER!

Past Challenges

CTF #1: Image Previewer