WIZER CTF #19: CALCULATOR

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!

Link to challenge #19

Goal

In this challenge, we're identifying a tricky Code Injection vulnerability.

Description of code

The code below showcases a simple calculator endpoint. The developer figured this would be safer than just using eval because they read about scopes in JavaScript and noticed that Function() runs in a different scope. Today we prove that it's not safer at all! A requirement of the feature was to allow the consumers of the endpoint to take advantage of the `mathjs` library to enable calculations such as `100 + Math.sqrt(1000)`. To achieve that, the developer provided `require` as an external argument to the scope so that the isolated scope is able to run the following calculation: `100 + argument[0]('mathjs').sqrt(1000)`.


chal19

What’s wrong with that approach?

The `require` argument can be misused to import libraries which will provide unauthorized access to data/files and assets.

What would a successful Command Injection attack look like in this case?

An attacker could use the `require` argument to import the `fs` library and read the contents of any file including `/etc/passwd`. Dynamically requiring the `fs` library, makes functions like `readFileSync('[path]')` accessible.

So what?

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.

Main Takeaways:

  • Never use `eval` nor `String(Function())` involving user input:
    Avoid the use of `eval` and Function constructors with user input. If you must use them, make sure that the input is sanitized and validated. In this case for instance, assuming we had to provide access to `mathjs`, it could have been sent as an argument instead of the wildcard `require` argument, which was likely provided as part of potential future functionality. Yet, even if the `require` argument was completely absent, the `String(Function())` approach is still vulnerable to code injection and there are ways to bypass the need to use `require` and still import general functionality into the isolated scope.
  • Never skip user-input sanitization:
    Always validate and sanitize user input. In this case, the developer could have used a whitelist of allowed functions and arguments to prevent the injection of malicious code.
  • Apply a least privilege approach upon deployment of the code:
    Make sure that the code is running with the least amount of privileges possible. In this case, the code could have been running in a container with limited access to the host machine. If you aren't the guy who's responsible for the deployment, make sure to communicate the potential risks to the person who is.

 

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

CODE WIZER!

Past Challenges

CTFs For Developers