Solution is ...
?boi=<iframe id=CONFIG src=/>&custom={"toString":0,"src":"javascript:alert(1337)"}
The first thing to notice is that
iframe
tag is in the whitelisted tags. Also there's HTML injection via the
boi
GET parameter.
let safeTags = [..., 'iframe', ...]
/* ... */
// Sanitized with DOMPurify
bois.innerHTML += clean;
There's also a feature to add some CSS styles to your HTML element via the custom
GET paramater. The custom
parameter takes input in JSON.
Now the idea is to DOM Clobber the CONFIG
variable and set it to a window
object. This is possible because iframe
is in the whitelist and when clobbered via the name
property, the value of the variable will be set to the window
object of that iframe. Once clobbered, we can set the src
property to any path on the website and then immediately we can change it to javascript:alert(0)
, which will run the Javascript in the current website's context, hence XSS.
First off, to clobber the CONFIG
variable, we need to first undefine it, since it already exists. To do this we can cause an error in the first script block. There are 2 ways to cause an error and undefine the CONFIG
variable, but only one can lead to XSS. Basically, we set the toString
of the customStyles
to a value which can't be called, because toString
is supposed to be a function that returns the string representation of that object.
Here, we have an indirect call to the toString()
function, this happens when the code tries to create a comment from the object. Since comments are pretty much TextNodes, the object first has to be converted to a string, which is done via the toString()
function.
let comment = document.createComment(customStyles)
bois.appendChild(comment)
Hence, we can set the toString
to a number.
custom={"toString":0}
When called would trigger an error and the code below it in the same script block won't run, hence WINDOW.CONFIG
will never be created. This is possible because the CONFIG
is created at the runtime.
Now that we've undefined the CONFIG
vairable, we can clobber it.
<iframe name=CONFIG src=/>
So now the
CONFIG
vairable, is a window object and we fully control it. How? check this code.
if (window.customStyles) {
styleSetter(customStyles, `CONFIG[style] = customStyles[style]`)
}
CONFIG[style]
is set to
customStyles[style]
and we control
customStyles
via the
GET parameter
custom
. So it's simply just a matter of setting the
src
property to
javascript:alert(1337)
.
?boi=<iframe id=CONFIG src=/>&custom={"toString":0,"src":"javascript:alert(1337)"}
The unintended solutions were similar, but instead of setting src
to javascript:alert(1337)
, the innerHTMl
was used to set the HTML of a div
element. So I added some regex in-place (cssSafe
).