By: makelaris
30 minutes
This particular challenge was a simple one, but it was a good introduction to the concept of client-side attacks, and as my first CTF within this project, I figured it was a good starting point just to warm up.
JSCalc application
So, from the little description that was given, it was clear from the get-go that this website was using an eval()
function to actually run the calculator. Given that I was already very familiar with JavaScript, I knew that this was a very potential vector for remote code execution.
I then downloaded the source code and snooped around in then to look for more clues, and to confirm whether or not this attack vector was actually feasible.
Poking around, I see a file called calculatorHelper.js
. Hmm, looks fishy. Let's take a look inside.
calculate(formula) {try { return eval(`(function() { return ${formula} ;}())`);
Wow, would you look at that. Un-filtered user input going directly into the eval()
function. Interestingly, it took a little playing around for me to figure out what we could do. Given that the eval is calling an anonymous function that then returns the formula that was given, I figured that we actually needed to use a second eval
to be able to do whatever I wanted.
From here, I jumped on a local Node instance just to ensure I was getting the syntax right, before I proceeded to try it on the website again. I got to this point:
Testing with listing files
Great, our payload is indeed working, but there's no flag file! Re-checking the overall structure of the source code, I noticed that the file.txt
was likely located in the parent folder. No big deal, we can change our payload very easily.
Found the flag file!
The rest is history from here - we just change readdirSync
to readFileSync
instead.
Pwned :)
Reflection and Learnings
The use of eval()
can pose a lot of security risk (as evident) in this challenge, exacerbated by the fact that the user input was unfiltered.
Our primary takeaway here is that eval()
should really just not be used whenever possible due to the possibility of RCE and DOM Based XSS vulnerabilities. While we didn't utilise an XSS based attack in this challenge (since it wasn't needed), there was no reason why we couldn't do some other nefarious acts.
Instead, we should potentially look into using other, safer alternatives, such as JSON.parse()
for parsing JSON, or even just utilising robust, popular libraries that handle expression evaluation securely.
Whenever a website opens its doors for users to input their own data - this could be a potential risk. It is paramount that any user input is properly validated and sanitised thoroughly, to ensure that we are only parsing and processing data that we know is safe (as much as possible).
Even if there was a need to use eval()
(which there really shouldn't be), the input should at least be filtered.
This first challenge has made me realise that there are so many things to think about when working with software in a security context. Before taking this course, I had some trivial knowledge of what not to do when developing software (e.g. storing passwords in plain text, using env
variables when possible etc.), but this first challenge alongside all the CTFs have made me realise the reality.
This is why it is so important that during code development in teams, we have comprehensive code reviews and testing. Sometimes, just having an extra pair of eyes could be the difference between a safe website and a vulnerable website (like this one), as others can offer another perspective over your code that you may not have. Beyond this, security and penetration testing could be another way to help mitigate vulnerabilities overall.
Next Writeup
Baby Nginxatsu