Solution is...
?name=<img name=getAlert><form id=alerts name=DEFAULTS>&key=innerHTML#--><img src onerror=alert(1337)>
The idea is to inject --><img src onerror=alert(0)>
via the location.hash
and trigger the HTML mutation via making changes using innerHTML
. A perfect candidate is SECRETS[secretKey]
because we control the property secretKey
. If we can set the SECRETS
variable to the div that holds our comment and set the secretKey
to innerHTML
then it's game over because it triggers the mutation. A quick scenario might be something like the following.
x = document.createComment("--><img src onerror=alert(1)>")
div.appendChild(x)
div.innerHTML += "blah" // triggers mutation
Our comments land inside the div which has the ID alerts
and we need to set SECRETS
variable to this div. But to set the SECRETS
variable to the div we should first undefine the variable DEFAULTS
so that we can use DOM Clobbering to redefine it to a div Element. Why DEFAULTS
? because in the following script block, you'll see that SECRETS
becomes DEFAULTS
.
/* Use `DEFAULTS` to init `SECRETS` */
SECRETS = DEFAULTS
Now lets first undefine the DEFAULTS
variable. One way is to cause an error in the script block that creates this variable so that the subsequent code wouldn't run and hence DEFAULTS
variable wouldn't be created in the first place.
We'll cause an error in the following line of code.
/* Handle to `#alert` */
let alerts = document.getAlert();
The idea here is to create an element with the name getAlert
which will clobber the document
property, so document.getAlert
will be no longer a function but an HTML Element. When it's called, it'll thrown an error because document.getAlert
is no longer a function, which will stop executing javascript from running the subsequent lines of code which ultimately means that the DEFAULTS
variable will never be created.
To clobber document.getAlert
we'll use img
because it can clobber using the name
attribute.
<img name=getAlert>
Earlier I told you that we need to set the DEFAULTS
variable to the div element which has the ID set to alerts
, well I lied. We can simply create a new Element with the ID set to alerts
and it's name to DEFAULTS
so that it clobbers the DEFAULTS
variable. We can simply use form
because it can have the ID set to alerts
but also clobber via the name
attribute which will be set to DEFAULTS
.
<form id=alerts name=DEFAULTS>
Now that we control the SECRETS
variable and also secretKey
, all there's left is to set the secretKey
to innerHTML
which triggers the mutation.
SECRETS[secretKey] += 1;
// FORMElement['innerHTML'] += 1;
Steps
- Undefine
DEFAULTS
by clobbering document.getAlert
.
- Redefined
DEFAULTS
to a Form Element with the ID alerts
so that SECRETS
will be this Form Element.
- Set
location.hash
to --><img src onerror=alert(0)>
which will land inside the Form Element because it's ID is alerts
.
- Set
secretKey
to innerHTML
so that when SECRETS[secretKey] += 1
runs, this will trigger the mutation.
Ergo XSS.