WIZER CTF #4: Movies Database

Hey! Not your first time visiting our CTF Recaps? Go ahead and jump on down to the insights! If it is your first time, allow us to give a quick rundown of what these are. Wizer CTFs were launched to challenge developers to learn to think like a hacker in order to learn to code more securely.  It's part of our new security awareness training we're designing focused on the dev team

Once a challenge is retired the Wizer Wizard behind these creations - our very own CTO Itzik Spitzen - creates takeaways that provide clues into the challenge from the perspective of defending your script. Want to testdrive a CTF before reading the notes? Go ahead at wizer-ctf.com - it's free and there's something for all levels.


In this challenge, we identifiy an Insecure Deserialization vulnerability which results in an RCE (Remote Code Execution) capability and take advantage of it to read a secret file from the project. Check out this CTF for yourself here.

Description of code

Our code uses the node-serialize package. This package allows for the serialization and deserialization of javascript objects. These objects can contain functions. In order to properly serialize and deserialize these, eval must be used.

This makes the library inherently vulnerable to a remote code execution IF an attacker is able to control the string to be deserialized. The maintainer of this project is well-aware of this and has included this section in the README, warning developers.

However, clearly this developer did not take that warning to heart and that has left this code vulnerable to an RCE:

What’s wrong with that approach?

A combination of an unsolvable vulnerability in the `node-serialize` library with the ability to return any message using the custom `No items found` message, allows the user to run pretty much any RCE and get the response directly.

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

With the goal of reading the secret.js file, the first step is to identify the RCE within the `message` value of the sent JSON, and use the format: `_$$ND_FUNC$$_function (){}()` to run any code. Adding `()` at the end of the function code will ensure that it's executed upon deserialization. Once that's done, some code to read the file should be incorporated in the RCE, however, leaving one last hump to cross, which is to find where the `secret.js` file is actually located. Looking at the URL, an attacker can identify that the challenge is deployed on Vercel, which is a Serverless Function deployment. This means that the files might be reorganized by the service. Running `process.cwd()` and a few shell commands (such as `ls` etc) to identify the file and parent folder, is required to finally discover the full path for `secret.js` and execute the attack.

So what?

RCE is one of the worst types of attacks, since it enables an attacker to potentially take over the server, read any data and perform almost any action freely. Needless to say that having access to various keys and a server, could even result in taking over an entire system.

Main Takeaways:

  • Be on the lookout for Insecure Deserialization vulnerabilities in any 3rd party libraries you're using:
    When adding a library to your code, always read about its known vulns, in case you are using package managers (such as `npm`), always review the vulns report produced by the tool upon installing a library.
  • Question the need for freely returning messages which are fully controlled by user input:
    An element that makes this attack possible, is the ability of the user to fully control the `message` field value. It allows the attacker to incorporate an RCE and return any string value. Make sure there's no better way to satisfy a requirement than to add a fully externally controlled field available. When considering a filter for such value, it's also impotant to note that an allowlist of allowed values is always better than a denylist.
  • Always minimize the app service permissions to the bare minimum:
    While not directly controlled by developers, limiting the service's user credentials on the server can minimize the damage that a potential attacker can cause. It is the developer's duty to identify the minimal requirements to run the code and let the deployment/dev-ops team know about it. In this case, Vercel is doing a great job limiting everything, since it's a Serverless deployment, but it's easy to imagine having admin rights on the server while executing RCE, the damage could have been way worse.


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


Past Challenges

CTF #1: Image Previewer