Compass Security Blog

Offensive Defense

XSS worm – A creative use of web application vulnerability

In my free time, I like to do some bug bounty hunting. For some reasons, I’ve been doing this almost exclusively for Swisscom. One of the reason is that the scope is very broad and I like to have this vast landscape full of possibilities. While preparing the talk we had at BlackAlps ( with Florian, he mentioned that an ex-colleague, Alexandre, had done a cool PoC for an XSS exploitation in the webmail at By chance, I had been starting to work on this system as well (and found an authorization bypass regarding email attachments… but this is another story ;-) ). Curious as I am, I contacted Alexandre and he shared his PoC with me, all credits for the idea and the first version goes to him!

The whole idea is to use an XSS that can be forwarded via email to turn it into a worm that can replicate itself and spread on the mailbox of numerous users. This was possible in his version of the PoC thanks to an XSS that would get activated when the recipient of an email would reply and email or forward it. I thought it was very cool, but could use some improvement and I looked for another XSS to remove most of the user interaction. I was lucky enough to find one.

The XSS is triggered in the attachments, where one can use special characters like < and > that won’t be properly escaped in the filename of the attachments. This happens when reading an email, the HTTP request is as follows:

POST /cp/applink/mail/ReadMessage HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-CP-Application: RichUI
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 143 Cookie: [CUT BY COMPASS]
Connection: close


As can be seen in the response below, the angle brackets are not escaped but the double-quotes are. The filename is later included into the DOM without more escaping, allowing for XSS.

"attachments":       [
      { "size": "24820" }, 
      { "fileName": "grumpycat<img src=x onerror=alert(1)>.jpg" }, 
      { "downloadUrl": "/applink/mail/Downloader?dhid=attachmentDownloader&messageId=8&" }, 
      { "contentType": "IMAGE/JPEG; name=\"grumpycat<img src=x onerror=alert(1)>.jpg\"" }, 
      { "mimeType": "IMAGE/JPEG" }

However, there were some limitations. After some tries,I turned to twitter and got the help I needed:

Armed with this, I could send the first version of the worm using the webmail. This would simply load a further script on my server and I could then easily trigger and modify the worm:

From here, it was easy to update the PoC and the code would now do the following upon opening the email:

  • Open a first popup “Here we go!”
  • Open a second popup to thank the user and confirm that he wants to run arbitrary JavaScript in his session
  • If the user accepted, open a third popup asking if the user agrees to mine some crypto coin while executing the PoC
  • If the user accepted, open the bitcoin miner
  • Get a list of all contacts and count the ones that end with, display 4 of them to the user and ask if we can send the worm to one of them
  • Ask for a confirmation that the user really wants to send the payload to this email
  • Confirming that the email was sent correctly and quitting

All of this involves a lot of user interaction, but it would have been trivial (and even easier) to do it without asking the permission and let the worm propagate automatically. Here is a small video of the code in action:

XSS worm in action


  • End of October 2018: First XSS Worm PoC
  • 01.11.2018: Found second XSS and reported to Swisscom
  • 02.11.2018: PoC code updated and forwarded to Swisscom
  • 27.11.2018: Fix applied and verified
  • 21.12.2018: Publication of this blog post

As a last note, it was a pleasure to collaborate with Alexandre and the Swisscom team again, not forgetting all the ones on Twitter that took some time to help as well. Thanks to everyone that read that far, have a safe and merry Christmas!


  1. Marc Ruef

    Great analysis, congrats!

  2. Yunus Ahmed

    Where can i get my payload encoded in Base64?

    • Nicolas Heiniger


      The easiest way to do it if you have a linux/unix OS is to use the base64 command. I usually do it as follows:
      $ base64 <<< 'HereComesThePayload' SGVyZUNvbWVzVGhlUGF5bG9hZAo=

      And the reverse operation:
      $ base64 -d <<< 'SGVyZUNvbWVzVGhlUGF5bG9hZAo=' HereComesThePayload

      Otherwise you can find many site that will do it online, just google for "online base64 encoder".


Leave a Reply

Your email address will not be published. Required fields are marked *