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, the developer identifies a tricky XSS vulnerability by bypassing a CSP (Code Security Policy) nonce protection and using DOM Clobbering. Check out this CTF for yourself here.
Description of code
The code of the challenge is a partial script which is very common in web-apps. It leaves a backdoor for developers to run some debug code. However, by including CSP at the top, with a nonce generated key, the system is built to protect the script from changes. It also validates that window.environment is set to something other than production, before allowing the debug code into the HTML.
What’s wrong with that approach?
The CSP nonce does indeed protect the code from script alterations, however, it does not block other DOM manipulations. Through user input, DOM manipulations are still possible, an attacker could change the control flow of the code and activate unintended functionality. Even though the intended debug script injection is seemingly protected, this code shows that if we're not careful, we could still leave cracks for attackers to exploit it and inject malicious code.
What would a successful XSS look like in this case?
With the goal of injecting `debug` script through the GET argument, we'd first need to somehow skip the script line `window.environment = 'production'` to force the global variable to be empty. It's fairly simple to break it by injecting a closing title tag and open a new <script> into the `name` GET variable. This would break the following script and cause it to be skipped. So now `window.envrionment` is left undefined, and we can now use a DOM Clobbering trick to fill the gap with something, so that `window.envronment` is anything but the string 'production'. Concatenating a DOM element (such as div or an anchor) with id=environment into the `name` will do the trick.
- While nonce can help prevent undesired script changes, it's not enough:
While CSP nonce makes sure that code cannot be altered, slightly diverting the flow of the app by using simple methods, could allow an attacker to use some intended functionality to inject code. Hence, always make sure that the logic is tight, and the elements which expose dangerous functionality are well protected.
- Be aware of DOM Clobbering:
- Enabling debug code on the client side is very dangerous:
While very convenient for developers, debug code is by definition not part of the intended behavior of the app, and as such, exposes functionality which could be exploited by potential attackers. In this case, it's not a very realistic scenario, since the developers left an open `check` for attackers to inject any code they want through an intended debug functionality. However, the important takeaway is that while the developers understandably seek for ways to add code to make debugging easier in various ways, this code's very existence is unadvised and should be ideally completely removed from the code on the released version.
Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.