Portswigger DOM-XSS Labs
Last updated
Last updated
To solve the lab, call print()
for the victim. I am given an exploit server. Since this is DOM XSS, the lab requires us to identify a sink and pass some JS input into it.
I noticed this bit in the /
directory:
The addEventListener
function waits for a message
on the window
object. Upon its existence, it sets the innerHTML
to the message e.data
.
This is vulnerable because it takes some user controlled input and passes it to innerHTML
. The innerHTML
sink does not accept <script>
elements on the modern browser, so I have to use iframe
or img
.
So firstly, I have to find a way to send a message
to this thing. Reading the Mozilla documentation shows that I can use the postMessage()
function to enable cross-origin communication between window
objects.
So I can use an iframe
to make the user visit the shop. Afterwards, I can refer to the current iframe
via contentWindow
.
Then, I can use onload
to load the postMessage
function, which passes the payload to the website and executes it within the user's browser. The targetOrigin
, which is the second argument for postMessage
is set to *
.
This specifies that there is no preference for the origin of the window to be for the event to be executed, meaning the event will always execute regardless.
Thus, the final payload is:
This demonstrates a DOM-based redirection vulnerability caused by web messaging. To solve the lab, construct a HTML page that calls print()
and deliver it to the victim.
Here's the code in the page:
Similar to the one above, just that this time it checks for the existence of http:
or https:
before carrying on. This indexOf
simply checks whether it is > -1
. This means that as long as I include http:
anywhere in the URL, I can bypass this check and get to the redirect vulnerability.
The payload used is similar to Lab 1, but this time I have to pass a url
to the page. The lab says to use Javascript URLs, and I can use //
to comment the http:
portion:
JSON.Parse
Same as above, just that this time I have to pass a JSON object. The Javascript embedded in the page is significantly longer:
The most notable d.type
would be d.url
, since it directly passes d.url
to .src.
. This allows for me to pass Javascript URLs.
To exploit this, I would need to first generate a JSON object, and then specify the type
as load-channel
, and then url
as javascript:print()
.
Here's the final payload, and there are lot of control characters required.
This lab contains an open-redirection vulnerability. To solve the lab, redirect the vi9ctim to the exploit server.
When viewing a product, I noticed this at the bottom of the page source:
What this does is take the url
parameter from the URL and then redirects the user to it. It checks for this via Regex (hence explaining the syntax highlighting). It checks the URL because it uses .exec(location)
. The location
object refers to the URL it is linked to.
There's a ternary operator, which basically checks for the returnUrl
parameter existence. If it does not exist, it returns the user to /
.
There is an obvious open redirect since the url
parameter is controlled by the user, and it is passed to the location.href
sink.
To solve the lab, just visit https://0a380022033605cb8014628100a40070.web-security-academy.net/postId=3&url=https://exploit-0ae900e3037c054a808161c001d1006a.exploit-server.net/exploit
.
This lab has a cookie vulnerable to XSS. To solve the lab, call print()
for the victim. When visiting a product page, this is the cookie sent:
And here's the Javascript code within the page:
The cookie changes this part of the page:
The above can be escaped to execute XSS. Using ?productId=2'><script>alert(1)</script>
results in an invalid product ID. Including an &
character to indicate another parameter works:
To call this, I have to visit the page twice with the same URL in order to set the lastViewedProduct
cookie.
So somehow, I have to make the user visit the same page twice, once with the payload and once without.
The easiest way to do this is:
Make the user visit the page with the payload.
Wait a bit.
Redirect the user back to the default page.
The wait can be done using setTimeout()
, and the redirect can be done with document.location
.