You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In May 2021, I solved my first Intigriti XSS challenge. Since then, I play every XSS challenge afterward, and solved most of them. Sometimes it's painful when you try everything you know but still can't solve it, however, the moment you made it, the pain is gone, replaced with joy and happiness.
As a player, I want to be on the other end(as a challenge maker) at least once, if I have an idea of an interesting XSS challenge.
I talked to @PinkDraconian in Jan 2021 and share an XSS challenge I created, after a few discussions, it gets accepted. This write-up is about the story behind the challenge.
Where the story begins
One day, when I was studying the famous Tiny XSS Payloads website, I noticed a payload:
<svg/onload=eval(`'`+URL)>
My question is: "Why do we need a quote before the URL?"
If we can control the URL, we can make it something like this: https://example.com/#';alert(1). After adding a quote before the URL, it becomes 'https://example.com/#';alert(1), just a string and a function call.
I realized that the quote is to make the URL a valid JavaScript snippet.
When I pasted the URL on the code editor, I noticed another interesting thing:
The part after // is grey out, because // means comment in JavaScript. Moreover, https: is also a valid syntax in JavaScript because it's a "label", what a coincidence!
Unlike other languages like C, JavaScript has no goto statement. But, you can still use the label with break and continue, it's useful when you have nested for-loop:
// without label, you need to have a flag to break outer loopletisOver=falsefor(leti=0;i<5;i++){console.log(i)for(letj=0;j<5;j++){if(i*j===9){isOver=truebreak}}if(isOver)break}// with label, it's easier
outer:
for(leti=0;i<5;i++){console.log(i)for(letj=0;j<5;j++){if(i*j===9){break outer
}}}
So, https://example.com is a valid JavaScript code, it's composed of labels and comments, cool, isn't it? That is to say, https://example.com\nalert(1) is also valid and will pop up an alert!
After I found this, I was thinking that maybe I can make it an XSS challenge.
Then I do.
Let's talk about the challenge
The core of the challenge is the following code:
window.name='XSS(eXtreme Short Scripting) Game'functionshowModal(title,content){vartitleDOM=document.querySelector('#main-modal h3')varcontentDOM=document.querySelector('#main-modal p')titleDOM.innerHTML=titlecontentDOM.innerHTML=content// DOM-XSS herewindow['main-modal'].classList.remove('hide')}if(location.href.includes('q=')){varuri=decodeURIComponent(location.href)varqs=uri.split('&first=')[0].split('?q=')[1]if(qs.length>24){showModal('Error!',"Length exceeds 24, keep it short!")}else{showModal('Welcome back!',qs)}}
I hope it looks normal, like what a normal developer will do. It's just extracting the query string q and checking its length, then putting it into HTML.
The challenge here is the length limit, you can only insert HTML with no more than 24 characters.
The shortest payload on TinyXSS is <svg/onload=eval(name)> which is 23 in length, but it doesn't work because of this line: window.name = 'XSS(eXtreme Short Scripting) Game', it prevents the payload from window.name.
All other payloads exceed 24 characters, including what I have mentioned previously: <svg/onload=eval("'"+URL)>
If you remember what I wrote at the beginning, you may try this payload as well: <svg/onload=eval(URL)> with the URL: https://challenge-0222.intigriti.io/challenge/xss.html?q=%3Csvg/onload=eval(URL)%3E&first=1#%0aalert(1)
Unfortunately, this doesn't work, because the URL is encoded, it's %0a instead of a newline character.
It seems a dead-end, unless you look at the code again carefully.
Reuse the existing thing
If you check the scope in devtool or print all properties of window, you should find a variable called uri. Let's look at the code again:
if(location.href.includes('q=')){varuri=decodeURIComponent(location.href)varqs=uri.split('&first=')[0].split('?q=')[1]if(qs.length>24){showModal('Error!',"Length exceeds 24, keep it short!")}else{showModal('Welcome back!',qs)}}
The length is 24 characters, perfectly fits the limitation.
By the way, if %0a is blocked, try U+2028(%E2%80%A8) and U+2029(%E2%80%A9) instead, it's also line terminators. I learned this trick from 0621 XSS challenge.
Great writeup for your amazing challenge! The people loved it and I loved the discussions it sparked!
We were incredibly lucky to be allowed to host your challenge at Intigriti!
In May 2021, I solved my first Intigriti XSS challenge. Since then, I play every XSS challenge afterward, and solved most of them. Sometimes it's painful when you try everything you know but still can't solve it, however, the moment you made it, the pain is gone, replaced with joy and happiness.
As a player, I want to be on the other end(as a challenge maker) at least once, if I have an idea of an interesting XSS challenge.
I talked to @PinkDraconian in Jan 2021 and share an XSS challenge I created, after a few discussions, it gets accepted. This write-up is about the story behind the challenge.
Where the story begins
One day, when I was studying the famous Tiny XSS Payloads website, I noticed a payload:
My question is: "Why do we need a quote before the URL?"
If we can control the URL, we can make it something like this:
https://example.com/#';alert(1)
. After adding a quote before the URL, it becomes'https://example.com/#';alert(1)
, just a string and a function call.I realized that the quote is to make the URL a valid JavaScript snippet.
When I pasted the URL on the code editor, I noticed another interesting thing:
The part after
//
is grey out, because//
means comment in JavaScript. Moreover,https:
is also a valid syntax in JavaScript because it's a "label", what a coincidence!Unlike other languages like C, JavaScript has no
goto
statement. But, you can still use thelabel
withbreak
andcontinue
, it's useful when you have nested for-loop:So,
https://example.com
is a valid JavaScript code, it's composed of labels and comments, cool, isn't it? That is to say,https://example.com\nalert(1)
is also valid and will pop up an alert!After I found this, I was thinking that maybe I can make it an XSS challenge.
Then I do.
Let's talk about the challenge
The core of the challenge is the following code:
I hope it looks normal, like what a normal developer will do. It's just extracting the query string
q
and checking its length, then putting it into HTML.The challenge here is the length limit, you can only insert HTML with no more than 24 characters.
The shortest payload on TinyXSS is
<svg/onload=eval(name)>
which is 23 in length, but it doesn't work because of this line:window.name = 'XSS(eXtreme Short Scripting) Game'
, it prevents the payload fromwindow.name
.How about
<script/src=//NJ.₨></script>
? I saw so many people were trying this way, but it won't work even if there is no length limitation, because a<script>
tag inserted with innerHTML should not execute.All other payloads exceed 24 characters, including what I have mentioned previously:
<svg/onload=eval("'"+URL)>
If you remember what I wrote at the beginning, you may try this payload as well:
<svg/onload=eval(URL)>
with the URL:https://challenge-0222.intigriti.io/challenge/xss.html?q=%3Csvg/onload=eval(URL)%3E&first=1#%0aalert(1)
Unfortunately, this doesn't work, because the
URL
is encoded, it's%0a
instead of a newline character.It seems a dead-end, unless you look at the code again carefully.
Reuse the existing thing
If you check the scope in devtool or print all properties of
window
, you should find a variable calleduri
. Let's look at the code again:Although the variable
uri
is declared inside the if block, it's still a global variable because var is function-scoped or globally scoped, not block-scoped.Is this variable helpful? Absolutely.
uri
is a decoded URL, our%0a
turns into\n
, a new line character! So, just replace the payload fromeval(URL)
toeval(uri)
, the payload works now: https://challenge-0222.intigriti.io/challenge/xss.html?q=%3Csvg/onload=eval(uri)%3E&first=1#%0aalert(1)We have to fix one last thing: it doesn't work on Firefox.
it's not hard to find out that
<style>
can be used instead of<svg>
, here is the final payload: https://challenge-0222.intigriti.io/challenge/xss.html?q=%3Cstyle/onload=eval(uri)%3E&first=1#%0aalert(document.domain)The length is 24 characters, perfectly fits the limitation.
By the way, if
%0a
is blocked, tryU+2028
(%E2%80%A8
) andU+2029
(%E2%80%A9
) instead, it's also line terminators. I learned this trick from 0621 XSS challenge.Other invalid but interesting solutions
I have another solution but with user interaction: https://challenge-0222.intigriti.io/challenge/xss.html?q=%3Cq%20oncut=eval(%22%27%22+URL)%3E1&first=1#';alert(1)
One needs to focus on the
<q>
element and pressctrl+x
to trigger the XSS.If you have other solutions, feel free to DM me(@aszx87410).
Closing Thoughts
Thanks for playing the challenge I created, I hope all of you have fun and enjoy it.
There is another great article that has mentioned the same technique: Smuggling Script via URL: Short HTML-based XSS payload, I haven't seen this until a player who solved the challenge sent me this via DM.
I should have added a new line filter to make it harder, at least not so easy to find the answer lol
The text was updated successfully, but these errors were encountered: