During my monthly downtime I like to go bug hunting in WordPress plugins. Not because I am a malicious neer-do-well, but because WordPress now makes up over 30% of websites! Often plugins for WordPress contain security flaws that go unnoticed and and proceed to be installed on a multitude of sites which can be a serious issue.
WordPress is used by small businesses, enterprises and this blog. Therefore it seems appropriate that we could contribute to identifying risks.
The Elementor plugin is widely used and is pretty awesome for rapidly prototyping varied pages with a nice drag and drop design method. You can have a look at the plugin here:
With the huge usage of the Elementor plugin (over 3 million installs) I put some time into investigating this in more depth.
The plugin seems to be very well implemented but there is always a sneaky XSS somewhere. So in comes Burp Suite’s intruder dropping a whole heap of payloads into every dynamic part of the application….and…..bingo!
Ok, so the “alert(1)” payload worked, it popped up my alert box as expected, however I wanted to try to get something that was more like an actual attack vector, thus I began to try the variations of document.cookie.
So trying the standard:
The next attempt is to try using an accessor instead:
…and again..still nothing.
Ok, so lets try and host the script on another system and see if I can use the XSS to call my script from elsewhere (actual hostnames and IPs redacted).
Unfortunately not, it seems spaces are also changed to underscores.
Ok, well there is a good trick to use the “form feed” character URL encoded (%0c) which should bypass that.
Ok, so that bypassed the issue with spaces, but we are getting some nasty URL encoded values rendered that are not going to pop this XSS.
However, I did notice that because the first “script src=” was being rendered, it was actually trying to call the remote script. However, this was not a valid URL by any means due to the inability to close off the script tags.
So I need to find a way to call my remote host correctly. Well a little while ago I had read a great post on using different variations on an IP address (octal, decimal and hex etc) that mean we can call the remote location without entering anything that looks like a URL.
Article: XSS Without Dots
So a payload was built using this format that looked like the following (the redacted IP address is just 127.0.0.1 in hexadecimal for this post):
This would point to a remote server that simply had “alert(document.cookie)” in the index.html file.
Hmm, still getting issues with the URL encoding. Well lets try and sort that out by URL encoding this payload.
Final Thoughts / Remediation
Whilst this XSS was not a particularly difficult one to find, it had some nuances that made it awkward to find the right working payload. Persistence and a couple of hours free time were enough to get this working nicely.
The developers of Elementor were contacted prior to this post to ensure they had adequate time to remediate the issue. This has now been fixed with some extra sanitization.
If you are using Elementor 2.8.4 or below, please update now to the latest version!