Introduction
This week’s I have an example a website where business owners can create an account and advertise their own businesses. This forms something of a social network where users can follow each other, “love” businesses, etc.

The online business directory/social media website.
Just like this toy retailer covered previously, this website has a cross-site scripting (XSS) vulnerability in the “first name” field of the user profile. However, unlike the toy retailer, there is no protection here against a user adding malicious code that can be executed on another user’s browser.

The vulnerable edit details page.
The Edit Details Page
This website is backed by CloudFlare which provides some decent protections against users trying to execute an XSS attack against a website.
An example of seeing these protections in action is when trying to change my first name to John<script>alert('hello')</script>
and then hitting the “Update” button at the bottom of the page; I get this screen:

CloudFlare does a decent job of blocking potentially malicious content.
However, when I click the “Change avatar” button at the top of the page and upload a profile picture, CloudFlare doesn’t kick in and reject the request. The profile is updated and the image is saved. We’re in! 😎

The malicious HTML is escaped in several places (blue regions) but not escaped in one (red region).
Aside: Thoughts on CloudFlare's WAF
This is curious because both are POST requests that are made to the same endpoint (/account
) and contain the same information except that when I upload my avatar all the image data also gets sent in the request.
I suspect that CloudFlare only inspects the first N bytes of the request for anything naughty where N is some number that is apparently smaller than the size of the image I’ve chosen.
I have confirmed that by rearranging the order of the request parameters so that the name is sent before the profile picture that the request is indeed blocked. In theory, one should be able to bypass CloudFlare’s XSS protections by stuffing at least N bytes of data in a junk request parameter before the malicious payload.
It seems that N is equal to around 200kB. I think I’ll use this in the future!
Having Some Fun With It
Okay, now it’s time for a theoretical exercise. We now have a way to execute code under the profile of another user. All they have to do is view my profile and my code gets executed under their account. Here are some of the things we could do:
- Sign the user out.
- Make the user “follow” my account (aka, making me a celebrity).
- Change the user’s name to contain the copy of the same code. This would effectively make the code a worm.
- Make the user “love” my business. I feel like this would probably be the most effective use of the vulnerability.
- Adding fake pages/forms to the site to trick the user into giving up information.
- A combination of the above or anything else you can think of! The only limit is your imagination!
Some of these are more useful than others but it showcases the power that a small security hole can have if exploited by a bad actor.
Aside: Samy Worm
A similar vulnerability existed on the social media website MySpace in 2005. A user named Samy figured out that he could execute JavaScript via custom CSS styles on his profile page. So whenever a user viewed his profile his script would:
- Update the bio of the user to include “Samy is my hero”,
- Send a friend request to Samy, and
- Copy the script itself into the user’s custom CSS styles for their profile page too.
Because this code would copy itself, it propagated to more than one million accounts within a day of its release. MySpace took the site down for a couple of hours to fix the issue. This saw the developer arrested and charged.
Disclosure
- April 1st: I reported this issue via the email address on their website.
- April 9th: I reached out the their parent company using the form on their website.
- April 19th: I called the parent company at their physical office and left a message with reception.
- April 28th: I reached out to the CEO of the parent company via a mutual contact on LinkedIn. This seems to have worked to get their attention.
- May 1st: The issue has been fixed.
The solution to this problem was to HTML-escape the two locations where the first name was displayed. This has the interesting effect of the header (that was previously okay) being doubly escaped! Overall, the company was difficult to get ahold of but was pretty quick to implement a fix 😌
