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 a Stored DOM-based XSS vulnerability caused by an incomplete error handling structure.
The code below showcases a message creating and viewing app. The front-end code below represents the two main user interfaces: message creation and message retrieval / viewing pages. When we want to show a message on the page, we fetch the messages from the API and then place the messageId, firstname, lastname and the message itself in the browser's DOM. For the first three, innerText is used, however for the message itself, innerHTML is used. The developer(s) of Challenge #10 has revised the app and added the field `attn` (attention), furthermore, in order to mitigate the original vulnerability, they added a server side sanitization using the well known library DOMPurify.
If compared to Challenge #10, the sanitization structure which was implemented is definitely a good step forward, it is indeed an improved version since it's done upon save function on the server side and not only on the frontend client code side. However, the flaw of the change is within the error handling structure below, which can be exploited to change the flow of the code and affect the results. The other effective way to solve it would have been to encode the output, and prevent the XSS from being triggered upon /showMessage.
Another effective way to solve it would have been to encode the output, and prevent the XSS from being triggered upon /showMessage.
By not providing a 4th argument (causing `req.body[3]` to be null), an error will be caused and the error handling structure in place, which is currently merely printing the error message but not exiting the function, will allow the attacker to continue the code flow, skipping the DOMPurify sanitization and injecting a script tag into the message. Then once that message is saved in the system, the attacker would use some method such as a phishing email or any other social engineering trick, to make someone hit a link which includes that ID, as demonstrated below:
https://https://chal26-j89j45fg8.vercel.app/views/showMessage?messageId=[malicious id]
This vulnerability is a specific subtype of XSS named Stored DOM-based XSS.
So what?
While the code injection required to capture the flag is absolutely harmless, once an XSS attack is possible, by using a phishing strategies or other social engineering techniques, they could cause someone to click the link with the payload and execute an attack to hijack session cookies and/or perform actions on their behalf. Attackers can make requests authenticated as the user (transfer money in a banking application for example) or send data back to them (make API calls and send all the resulting data back). It could escalate and be immensely harmful as well since once attackers are able to run Javascript within the context of a logged-in user it could serve as an entry point, they can then identify and exploit other vulnerabilities such as broken access control (a.k.a. IDOR), weak encryption / hashing and others to execute wider attacks such as taking over an admin account.
Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.