Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vulnerability] Scripts in iframe srcdoc are not hooked #242

Closed
20 tasks done
t2ym opened this issue Apr 17, 2018 · 0 comments
Closed
20 tasks done

[vulnerability] Scripts in iframe srcdoc are not hooked #242

t2ym opened this issue Apr 17, 2018 · 0 comments

Comments

@t2ym
Copy link
Owner

t2ym commented Apr 17, 2018

[vulnerability] Scripts in iframe srcdoc are not hooked

Root Cause

  • iframe.setAttribute('srcdoc', '<HTML>') is not hooked
  • iframe.srcdoc property is not hooked
  • HTML from srcdoc has URI about:srcdoc and bypasses Service Worker

Implementation

  • Introduce these parameters
    • hook.parameters.baseURI - URL object for the entry page
    • hook.parameters.emptyDocumentUrl = new URL('./empty-document.html', baseURI);
    • hook.parameters.bootstrap = "<script>frameElement.dispatchEvent(new Event('srcdoc-load'))</script>";
    • hook.parameters.onloadWrapper = "event.target.addEventListener('srcdoc-load', () => { $onload$ })";
    • hook.min.js?service-worker-initiator={Entry page URL} - automatically set
  • Convert srcdoc to onload and set src with emptyDocumentUrl
    • In static HTML
    • iframe.setAttribute('srcdoc')
    • iframe.srcdoc property
  • Call onload event handler
  • ACL to protect contentWindow and contentDocument
    • Reject if hooking is not available in the iframe
  • Reject javascript: in src attribute and property
    • Attribute
    • Property
  • Documentation
    • README
    • Issues

Examples

  • Source HTML
 <iframe id="srcdocStaticBare"
  onload="console.log('srcdocStaticBare onload event', event);" 
  srcdoc="<script src=&quot;../../chai/chai.js&quot;></script><script>chai.assert.throws(()=> { return caches; }, /^Permission Denied:/);</script><p1>srcdocStaticBare</p1>"
  width="80%" height="100"></iframe>
  • Converted HTML
<iframe id="srcdocStaticBare"
  src="https://www.local162.org/components/thin-hook/demo/empty-document.html?url=https%3A%2F%2Fwww.local162.org%2Fcomponents%2Fthin-hook%2Fdemo%2Fmy-view3.html%2Ciframe%40srcdocStaticBare"
  onload="event.target.contentDocument.write(`<script src=&quot;../../chai/chai.js&quot;></script><script>chai.assert.throws(()=> { return caches; }, /^Permission Denied:/);</script><p1>srcdocStaticBare</p1><script>frameElement.dispatchEvent(new Event('srcdoc-load'))</script>`);event.target.addEventListener('srcdoc-load', () => { return __hook__((...args)=>(__hook__(()=>{__hook__('()',console,['log',['srcdocStaticBare onload event',event]],'/components/thin-hook/demo/my-view3.html,iframe#srcdocStaticBare,onload@3767');},null,args,'/components/thin-hook/demo/my-view3.html,iframe#srcdocStaticBare,onload@3767')),null,[],'/components/thin-hook/demo/my-view3.html,iframe#srcdocStaticBare,onload@3767',0); })"
  width="80%" height="100"></iframe>
  • Empty Document to load hooking infrastructure
<!--
@license https://github.com/t2ym/thin-hook/blob/master/LICENSE.md
Copyright (c) 2017, 2018, Tetsuya Mori <t2y3141592@gmail.com>. All rights reserved.
-->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../../thin-hook/hook.min.js?no-hook=true&hook-name=__hook__&context-generator-name=method&discard-hook-errors=false&fallback-page=index-fb.html&hook-property=true&hook-global=true&hook-prefix=_pp_&compact=true"></script>
  <script context-generator src="no-hook-authorization.js?no-hook=true"></script>
  <script context-generator src="context-generator.js?no-hook=true"></script>
  <script context-generator src="bootstrap.js?no-hook=true"></script> <!-- NEW -->
  <script src="hook-callback.js?no-hook=true"></script>
  <script src="hook-native-api.js?no-hook=true"></script>
</head>
<body>
</body>
</html>
  • bootstrap.js
{
  let baseURI;
  switch (self.constructor.name) {
  case 'Window':
    if (self === top) {
      baseURI = location.href;
    }
    else {
      baseURI = top.hook.parameters.baseURI;
    }
    hook.parameters.baseURI = baseURI;
    break;
  case 'ServiceWorkerGlobalScope':
    baseURI = new URL(location.origin + new URL(location.href).searchParams.get('service-worker-initiator')).href;
    hook.parameters.baseURI = baseURI;
    break;
  case 'DedicatedWorkerGlobalScope':
  case 'SharedWorkerGlobalScope':
    // For Hook Workers; Insignificant in hooked web workers
    baseURI = new URL(location.origin + 
        (new URL(location.href).searchParams.has('service-worker-initiator')
          ? new URL(location.href).searchParams.get('service-worker-initiator')
          : location.pathname
        )
      ).href;
    hook.parameters.baseURI = baseURI;
    break;
  default:
    baseURI = location.href;
    hook.parameters.baseURI = baseURI;
    break;
  }
  hook.parameters.emptyDocumentUrl = new URL('./empty-document.html', baseURI);
  hook.parameters.bootstrap = `<script>frameElement.dispatchEvent(new Event('srcdoc-load'))</script>`;
  hook.parameters.onloadWrapper = `event.target.addEventListener('srcdoc-load', () => { $onload$ })`;
  //console.log('bootstrap.js: location.href = ' + location.href + ' baseURI = ' + baseURI + ' bootstrap = ' + hook.parameters.bootstrap);
}

Notes

  • Even if hook.min.js and other hooking infrastructure script are inexistent in DOM, they are installed before their script tags are overwritten by srcdoc document.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant