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 an OS Command Injection vulnerability in the code below. These issues are common and can pose a significant risk! Let's dive into it.
Description of code
The code below showcases a couple of endpoints taken from a certain app. The app stores a folder on the server with some company "assets" typically: images, files etc, likely uploaded by the company into the app. The first endpoint showcased is `/companyAssets` which receives a company ID and retrieves the OS Command output of `ls -l [folder name]` where the folder name is kept in the database per company. The second endpoint is `/addAssetsFolder` which receives a company ID and a folder name. The company can then use this folder name to store their assets. Both endpoints perform sanitization on the user inputs. The `/companyAssets` endpoint will not accept a company ID which isn't a valid UUID, and the `/addAssetsFolder` endpoint ensures that the company ID is a UUID, sanitizes the folder from special characters and ensures the folder name ends with the text `/assets`.
What’s wrong with that approach?
The sanitization of the folder name isn't sufficient and still allows a savvy hacker to bypass it and execute OS commands on the server side. It seems like the developer did think about an OS Command Injection vulnerability, however, created a custom sanitization and apparently missed at least a single crack in the sanitization.
What would a successful OS Command Injection attack look like in this case?
An attacker who is looking at this app, even potentially without having access to the actual code via something like BurpSuite, could attempt multiple bypass techniques. It's not too difficult to find that an OS Command could still be executed within the following format `$( <some command> )`, while the sanitization removes multiple special characters, it has missed this option. With that, the malicious command can be `cat /etc/passwd` for instance. Once that's possible, there's only one last obstacle to overcome; the system automatically adds the postfix `/assets` to the end of the assets folder name specified, if `/assets` isn't there already, hence it needs to be escaped. To overcome that, an attacker could end the folder name with `#` (hashtag), this way, any text coming after the hashtag, as per the OS parser, is ignored and considered a comment.
OS Command Injection, which is a way to achieve 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.
- Replace all OS Commands with equivalent in-language libraries:
Most common OS Commands could be easily replaced with a direct in-language command or a common library. In this case, we wanted to list the files within a specific folder. Using the `ls` OS Command might sound straightforward, but is definitely opening up a can of worms when it comes to risks. In our case, the robust library `fs` which is a native NodeJS library, would give us a set of capabilities to do pretty much anything we need in the file-system, including `fs.readdir()` or `fs.readdirSync()` which provide the entire set of tools to read the list of files in any location. The arguments of in-language functions are by-definition more tight and specific, hence the risks of getting access to undesired functionality is much lower and more easily controllable.
- Custom sanitization isn't a good practice:
While in this case the recommended best practice isn't really better sanitization, especially when it comes to OS Commands, the shell is so full of tricks and bypasses that it's very hard to think about all the options and block all OS Commands without breaking the application's functionality.
Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.