Writeup for JanuaryXSS in intriger
Writeup for JanuaryXSS in intriger
Write on the wall
In order to prepare for the coming interview-examination, I decided to write some easy writeups for practicing my awful English. This article is a begaining.
Pretty interesting challenge, not so hard as usual. After about 2 hours when i tried my way to solve the chall, I found a way to alert
the flag
in page. Meanwhile, there are some tricks during the soving process, and this writeup was made to record them
The Challenge
The challenge address: http://challenge-0121.intigriti.io/
To simplify the code, i remake the challenge at localhost
<code id="flag">THIS_FLAG</code>
<div id="popover"><p>We just hit 30,000 Twitter followers! Thank you!</p></div>
<script>
window.href = new URL(window.location.href);
window.r = href.searchParams.get("r");
//Remove malicious values from href, redirect, referrer, name, ...
["document", "window"].forEach(function(interface){
Object.keys(window[interface]).forEach(function(globalVariable){
if((typeof window[interface][globalVariable] == "string") && (window[interface][globalVariable].indexOf("javascript") > -1)){
delete window[interface][globalVariable];
}
});
});
window.onload = function(){
var links = document.getElementsByTagName("a");
for(var i = 0; i < links.length; i++){
links[i].onclick = function(e){
e.preventDefault();
delete(window.origin);
safeRedirect(e.target.href);
}
}
}
if(r != undefined){
iter = "r";
safeRedirect(r);
}
function safeRedirect(url){
if(!url.match(/[<>"' ]/)){
console.log(url)
window.setTimeout(function(){
if(url.startsWith("https://")){
window.location = url;
}
else{ //local redirect
window.location = window.origin + "/" + url;
}
window.setTimeout(function(){
document.getElementById("error").style.display = "block";
}, 1000);
}, 5000);
document.getElementById("popover").innerHTML = `
<p>You're being redirected to ${url} in 5 seconds...</p>
<p id="error" style="display:none">
If you're not being redirected, click <a href=${url}>here</a>
</p>.`;
}
else{
alert("Invalid URL.");
}
}
</script>
Firstly, As we can see in the code ,we are able to control window.r
from passing a GET parameter like server.com?r
Then, the global properties document
and window
will be cleaned if document/window.$x
instance of string
and include the string: javascript
["document", "window"].forEach(function(interface){
Object.keys(window[interface]).forEach(function(globalVariable){
if((typeof window[interface][globalVariable] == "string") && (window[interface][globalVariable].indexOf("javascript") > -1)){
delete window[interface][globalVariable];
}
});
});
Finally the window.r
will be used as paramater named url
to splice the window.location
and the tag<a href=$window.r>
However, we fail to escape the label because the Waf matches <>"'
At the end, window.onload
could operate until the style.display
in window.setTimeout
executed competely.window.onload
need the Inline-blocks be totally loaded
window.onload = function(){
var links = document.getElementsByTagName("a");
for(var i = 0; i < links.length; i++){
links[i].onclick = function(e){
e.preventDefault();
delete(window.origin);
safeRedirect(e.target.href);
}
}
}
The conception
After glancing over the code, I made two conceptions via different sinks:
1、sink of <a>
: If i could control the attribute href
like javacript:alert(1)
, I will get a click xss.
2、sink of window.location
: If i could control the paramater window.origin
like javascript:alert(1)
, I will get a wonderful xss that wihout user interaction
The exploration
The value of href
is same as the window.origin
,so i gave up the first idea anyway. Now, let us find a way to the second one
First of all, We could not control the window.origin
via simple url request
Quickly, the Dom-clobbering
came to my mind, and we have to find a way to delete the window.origin
. Coincidently, the waf helped us do that and the server resolves the subdomains to root domain broadly which means javascript.xxx.com
equal to xxx.com
The delete condition is achieved, we are supposed to create window.orgin
. Do not forget the tag <a>
is partly containable. Although the waf filter out "|'|space
, we still create new attributes through %09|%0a|%0d|
It is worth mentioning that not all the tags could be converted to string via toString()
and few tags would call tostring()
to return the attribute value in it. Here, tag <a>
meets our demand both.
Another one more thing, if attribute href
contains a complete url,toString()
function will return the entire url. If href
startwith normalstring
, the function will return host
+normalstring
as result because normalstring
is regarded as a relative path.
However, if href
startwith protocol, it returns the full protocol value, nearly caters to our intention!
As a final step, we need to write javascript:alert(1)
to the href
attribute and bypass the waf url.indexof('javascript')>-1
,
Got a complete utilize! The eventual payload here:
https://javascript.challenge-0121.intigriti.io/?r=Javascript:alert(flag.innerText)/%0aid=origin