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!
In this challenge, we're uncovering an application logic flaw that has some big security impact.
The code below is of a custom Request Processor, which is a NodeJS application that processes custom formatted requests from the user. The expected structure of the request is as stated in the comment: "data:<data>,requestType:<requestType>,key:<key>,". Upon receiving the request, the code splits the request into its parts and validates the key against the secret stored. If the key matches the secret stored, then the action is performed (within an external code, not included in this challenge), otherwise, an "invalid key" error is returned. Furthermore, in the following part of the code, a validation was added to ensure the existence of all the required fields in the request, which returns an error "Invalid request format: data:<data>,requestType:<requestType>,key:<key>," if one of the fields is missing:
See the full code below:
An oversight of a minor logic error, makes it possible to trick the system and get in the crack created between the valid and the invalid structures. There are two validations for key existence: (1) const keyFound = keyIndex > 0; which is used to protect the key validation process and (2) A check of `keyIndex < 0` within the part that checks if all the expected elements exist. But what if keyIndex is exactly 0? The logic isn't actually validating the particular expected order of the fields.
By providing the key as the first data field "key:xyz," in the request, the keyIndex will be 0, which means that we can pass the validation of existence and yet skip the verification of the key against the stored secret, since `keyIndex > 0` isn't true. This way, an attacker could perform any action without the key.
The showcased vulnerability could cause a variety of risks depending on the functionality of the code which can be overridden. In our little example, the attacker could bypass the key authentication, and practically perform any action without having to have any knowledge of the secret key, however, logic which isn't tight could practically be anywhere and cause almost any type of risks.
Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.