I recently came across a challenging frameset xss, and I wanted to sure how I was able to exploit it.
First, imagine the following application.
root@kali:~/frameset# cat frameset.php <html> <head> <title>Vulnerable Frameset Example</title> </head> <frameset cols=21%,* framespacing=1> <frame name=left src="left.html" frameborder=1 framespacing=0> <frame name=right src="<?php echo $_GET['right'] ?>" frameborder=0 framespacing=0> </frameset> <body> Can't see me </body> </html> root@kali:~/frameset# cat left.html I'm the left frame! root@kali:~/frameset# cat right.html And I'm the right frame?
If we visit the page, we get the following view.
To start, this is a simple, but to the point application. We can verify that PHP is using the GET parameter by inputting an invalid one.
As the application is writing directly into a tag, we could just escape the current tag, and placing a script tag.
To verify that we can write arbitrary HTML, we can send a test tag.
Unfortunately, if we look into the source of the page and Firefox's debugger, we see the first sign of trouble.
For those of you not aware, the frameset tag is a wonderful tag, and one of the few in HTML that actually cares what is around it. For starters, if you have a frameset tag, your HTML document cannot have a body. Additionally, the only tags allowed inside of a frameset are "frame" and "noframes", that is it.
To verify that our first idea wouldn't work, we can try exiting the frame tag and creating a script tag. As you can see, the debugger gives us an error and we do not receive an alert.
Option one to solve this problem, is to use our XSS polyglot. If we use this as a new frame src, then the page will load the HTML into the frame, and our script will fire off.
To verify this, I finished the current frame tag, and created a new one. As you can see, the frameset XSS fired off!
The source of the page verified that there we no longer any errors, and that we had a new frame with the source of our XSS domain.
Unfortunately, this attack option does come with a downside.
If we add the following code to our application, it will also have a "secret" cookie value.
<?php $cookie_name = "secret"; $cookie_value = "thisISmy$3cr3t"; setcookie($cookie_name, $cookie_value, time() + (86400 * 30), "/"); // 86400 = 1 day ?>
We can verify that the application sets the cookie by running alert(document.cookie) in the browser console.
Unfortunately, if we attempt to get access to the cookie from our XSS payload on r4y.pw, we are unable to get to it. The reason for this is that we are violating the Same-origin policy. As we are loading our payload externally into a frame, it is coming from r4y.pw directly instead of the local application. In this case, we would not be able to steal a user's cookies or session.
Unfortunately, since option one doesn't bypass the SOP, we'll have to move on to option two.
After we load the page, we get our alert to fire off.
For example, we can use the following payload.
Once we load the page, we can verify that our alert fired off!
Additionally, inside the source of the page, we can see our newly created frame.
The main benefit of this payload over option one, is that the local application is loading the script, thereby bypassing the SOP.
Next, we can use the following payload.
When we view the source of the page, we can see that our payload and HTML is still intact.
Finally, when we load the page, our frameset XSS fires off and we can steal the user's cookie!