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 identifying yet another tricky case of a code injection vulnerability.
The code below is a version 2 of Challenge #19, which is a simple calculator endpoint. The developer figured that the user of Function c'tor would be safer than just using eval because they read about scopes in JavaScript and noticed that Function() runs in a different scope. Then, the solvers of challenge #19 showed that the code is dangerously vulnerable. Going back to the drawing board, the developer removed the `require` argument and also came up with the following validation, to ensure that the input is valid:
Today we prove that even though a validation was implemented, it's still not safe at all! Take a look at the full app code below.
There are other applicable ways to bind a library in order to use it in NodeJS. Furthermore, there are multiple bypass options for a blacklist of keywords, especially in flexible mighty languages like Javascript.
There are multiple ways to bypass the validation, one such way is to use a combination of `eval(atob())` which hides keywords that are being looked for. However, that's not enough since we can't use the `require` command anymore since it's not present on this version, but we could still use `process.binding` to import `fs` and then use the `.internalModuleReadJSON('/etc/passwd')`. Another way around the validation is to break unallowed words into parts and then concatenate them in the code. For instance, we could use `pro` and `cess.binding` to bypass the validation and run eval on the textual command in order to import `fs`.
Code Injection is an extremely dangerous vulnerability, once an attacker gains access to execute any code and basically any library by controlling user-input, the range of opportunities is broad starting from reading secret keys, through server and infrastructure take over.
Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.