From 1c33d78657c709554a7d03c68834d3027f077e81 Mon Sep 17 00:00:00 2001 From: Alesandro Ortiz Date: Tue, 20 Aug 2024 19:21:51 -0400 Subject: [PATCH 1/6] Add baseURI leak from sandboxed iframes --- content/docs/attacks/baseuri.md | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 content/docs/attacks/baseuri.md diff --git a/content/docs/attacks/baseuri.md b/content/docs/attacks/baseuri.md new file mode 100644 index 000000000..1369da234 --- /dev/null +++ b/content/docs/attacks/baseuri.md @@ -0,0 +1,66 @@ ++++ +title = "baseURI" +description = "" +date = "2024-08-20" +category = [ + "Attack", +] +abuse = [ + "iframes", +] +defenses = [ + "Browser Fix", + "Application Fix", +] +menu = "main" +weight = 3 ++++ + +`document.baseURI` can be used in an opaque-origin sandboxed iframe to leak the full URL of an ancestor page. + +If a URL has sensitive information, the origin, query, or fragment (hash) are most likely to contain this sensitive information. + +## Sandboxed opaque-origin about:srcdoc iframe + +An iframe loaded with `about:srcdoc` and sandboxed without `allow-same-origin` (i.e. has opaque origin) can read `document.baseURI` to leak the closest http(s):// origin document's URL. + +This also works in nested frames, with the baseURI value set to the closest document's URL that has an http(s):// origin. For example, nesting multiple `about:srcdoc` within `https://example.com/path?query#hash` will still leak the full `example.com` URL. + +## Code Snippet + +Adapted from crbug 40867031[^crbug-40867031]: +1. Navigate to a URL, where the URL contains secrets. e.g. https://example.com/path?query#hash +2. Run the following JavaScript in DevTools: +```javascript +f = document.createElement("iframe"); +f.sandbox = "allow-scripts"; +f.srcdoc = ""; +document.body.appendChild(f); +``` + +{{< hint info >}} +Technically this also works with `about:blank` (verified via DevTools), but only an extension might be able to script this, so it's not that useful. +{{< /hint >}} + +## Verified Browser Versions +As of August 20th, 2024: +* Chrome 127.0.6533.120 Stable + 129.0.6668.9 Canary +* Edge 127.0.2651.105 Stable +* Firefox 128.0 Stable + +## Defense + + +* HTML specification fix [^html-spec-9025] +* Browser fixes: [^crbug-40867031] [^crbug-330744612] +* Application mitigation: Applications should avoid having sensitive information in URL if the page may include sandboxed `about:srcdoc` iframes with untrusted data. + +____ + +## References + +[^crbug-330744612]: Chromium bug: +`Consider not inheriting base url in sandboxed srcdoc iframes`, [link](https://issues.chromium.org/issues/330744612) +[^crbug-40867031]: Chromium bug: +`Consider limiting how much of URL is inherited for base URL`, [link](https://issues.chromium.org/issues/40867031) +[^html-spec-9025]: WHATWG HTML specification issue: Sandboxed iframes with opaque origin should not inherit fallback base URL, [link](https://github.com/whatwg/html/issues/9025) From eeefa86ca0281548bff2298d018408ca5c581b42 Mon Sep 17 00:00:00 2001 From: Alesandro Ortiz Date: Fri, 23 Aug 2024 17:56:11 -0400 Subject: [PATCH 2/6] Update contributors list --- content/docs/contributions/_index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/docs/contributions/_index.md b/content/docs/contributions/_index.md index 18ae4c35a..6b2ce9bc7 100644 --- a/content/docs/contributions/_index.md +++ b/content/docs/contributions/_index.md @@ -89,7 +89,8 @@ We would like to thank the following users who [contributed](https://github.com/ [Brasco](https://github.com/Brasco/), [rick.titor](https://github.com/riccardomerlano), [Chris Fredrickson](https://github.com/cfredric/), [jub0bs](https://github.com/jub0bs), [Zeyu (Zayne) Zhang](https://github.com/zeyu2001), [Medi](https://twitter.com/medi_0ne), -[Aaron Shim](https://github.com/aaronshim), [Jorian Woltjer](https://jorianwoltjer.com) +[Aaron Shim](https://github.com/aaronshim), [Jorian Woltjer](https://jorianwoltjer.com), +[Alesandro Ortiz](https://AlesandroOrtiz.com) In addition, we would also like to acknowledge the users who [contributed](https://github.com/xsleaks/xsleaks/wiki/Browser-Side-Channels/_history) to the predecessor of the current XS-Leaks wiki: From 2a6b9a45c9c6dfeb4c1d7b8426ee5f943ffad0bd Mon Sep 17 00:00:00 2001 From: Alesandro Ortiz Date: Fri, 23 Aug 2024 18:21:02 -0400 Subject: [PATCH 3/6] Apply suggestions from code review Co-authored-by: terjanq --- content/docs/attacks/baseuri.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/content/docs/attacks/baseuri.md b/content/docs/attacks/baseuri.md index 1369da234..60e413ce3 100644 --- a/content/docs/attacks/baseuri.md +++ b/content/docs/attacks/baseuri.md @@ -1,5 +1,5 @@ +++ -title = "baseURI" +title = "Sandboxed iframes" description = "" date = "2024-08-20" category = [ @@ -8,29 +8,20 @@ category = [ abuse = [ "iframes", ] -defenses = [ - "Browser Fix", - "Application Fix", -] menu = "main" weight = 3 +++ -`document.baseURI` can be used in an opaque-origin sandboxed iframe to leak the full URL of an ancestor page. - -If a URL has sensitive information, the origin, query, or fragment (hash) are most likely to contain this sensitive information. -## Sandboxed opaque-origin about:srcdoc iframe +## Base URL An iframe loaded with `about:srcdoc` and sandboxed without `allow-same-origin` (i.e. has opaque origin) can read `document.baseURI` to leak the closest http(s):// origin document's URL. This also works in nested frames, with the baseURI value set to the closest document's URL that has an http(s):// origin. For example, nesting multiple `about:srcdoc` within `https://example.com/path?query#hash` will still leak the full `example.com` URL. -## Code Snippet +### Code Snippet -Adapted from crbug 40867031[^crbug-40867031]: -1. Navigate to a URL, where the URL contains secrets. e.g. https://example.com/path?query#hash -2. Run the following JavaScript in DevTools: +The below snippet demonstrates how a sandboxed iframe can leak its parent's `document.URI`. ```javascript f = document.createElement("iframe"); f.sandbox = "allow-scripts"; @@ -42,7 +33,6 @@ document.body.appendChild(f); Technically this also works with `about:blank` (verified via DevTools), but only an extension might be able to script this, so it's not that useful. {{< /hint >}} -## Verified Browser Versions As of August 20th, 2024: * Chrome 127.0.6533.120 Stable + 129.0.6668.9 Canary * Edge 127.0.2651.105 Stable From 36b7035ba22e42324e61b1e2ee8e6d67d4d7bdba Mon Sep 17 00:00:00 2001 From: Alesandro Ortiz Date: Fri, 23 Aug 2024 18:42:50 -0400 Subject: [PATCH 4/6] Rename `baseuri.md` to `sandboxed-iframes.md` --- content/docs/attacks/{baseuri.md => sandboxed-iframes.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/docs/attacks/{baseuri.md => sandboxed-iframes.md} (100%) diff --git a/content/docs/attacks/baseuri.md b/content/docs/attacks/sandboxed-iframes.md similarity index 100% rename from content/docs/attacks/baseuri.md rename to content/docs/attacks/sandboxed-iframes.md From cc86bee07cd1a70512d529771b6f881b62712d2b Mon Sep 17 00:00:00 2001 From: Alesandro Ortiz Date: Fri, 23 Aug 2024 19:47:03 -0400 Subject: [PATCH 5/6] Expand on nuanced behavior, implement feedback Some of the nuanced behavior requires re-verification, and general readability improvements are needed, hence WIP with TODOs. --- content/docs/attacks/sandboxed-iframes.md | 37 ++++++++++++++--------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/content/docs/attacks/sandboxed-iframes.md b/content/docs/attacks/sandboxed-iframes.md index 60e413ce3..7ef18a357 100644 --- a/content/docs/attacks/sandboxed-iframes.md +++ b/content/docs/attacks/sandboxed-iframes.md @@ -12,16 +12,30 @@ menu = "main" weight = 3 +++ - ## Base URL -An iframe loaded with `about:srcdoc` and sandboxed without `allow-same-origin` (i.e. has opaque origin) can read `document.baseURI` to leak the closest http(s):// origin document's URL. +`about:srcdoc` iframes inherit the parent's base URL if the initiator is same-origin with the parent. It can be read via `document.baseURI` property which contains the full URL of the parent. +(TODO(AO): Think about when to include and how to phrase "closest parent" in most sentences, since this seems relevant in most scenarios, but can make sentences hard to read.) + +Currently, this behavior is preserved even for sandboxed iframes without `allow-same-origin` (i.e. opaque origin). This is problematic if the rendered content is untrusted, which is a common use case of opaque-origin `about:srcdoc` frames, and the parent's URL contains sensitive information. + +In cases where there are nested frames, and some frames do not have an `http(s)://` URL, then the closest parent with an `http(s)://` URL will be used. +For example, `A(example.com) -> B(about:srcdoc, opaque origin) -> C(about:srcdoc)`, page C will have `document.baseURI` set to page A's full URL despite page B being the initiator. +(TODO(AO): Verify where Chromium checks for `http(s)://` (in origin or URL?). This is relevant in scenarios where non-opaque `about:blank` page creates `about:srcdoc` frames, or non-opaque `about:srcdoc` frame creates opaque `about:srcdoc` frames.) + +This also works in nested frames, with the baseURI value set to the closest document's URL that has an `http(s)://` origin. For example, nesting multiple `about:srcdoc` within `https://example.com/path?query#hash` will still leak the full `example.com` URL. + +{{< hint warning >}} +For frames with non-opaque origins, the origin is set to the initiator's origin. Untrusted code should not be run in non-opaque `about:srcdoc` frames since it has access to the same origin. +{{< /hint >}} -This also works in nested frames, with the baseURI value set to the closest document's URL that has an http(s):// origin. For example, nesting multiple `about:srcdoc` within `https://example.com/path?query#hash` will still leak the full `example.com` URL. +{{< hint info >}} +`about:blank` iframes inherit their initiator's (in most cases, a direct ancestor) base URL. This is less useful for attackers since this does not leak cross-origin URLs, but can still be useful to leak origins via `location.ancestorOrigins`. +{{< /hint >}} ### Code Snippet -The below snippet demonstrates how a sandboxed iframe can leak its parent's `document.URI`. +The below snippet demonstrates how a sandboxed `about:srcdoc` iframe can leak its closest `http(s)://` parent's `document.baseURI`. ```javascript f = document.createElement("iframe"); f.sandbox = "allow-scripts"; @@ -30,20 +44,15 @@ document.body.appendChild(f); ``` {{< hint info >}} -Technically this also works with `about:blank` (verified via DevTools), but only an extension might be able to script this, so it's not that useful. +As of August 2024, base URL inheritance works as described in this page in Chrome, Edge, and Firefox. +(TODO(AO): Re-verify all scenarios in Firefox and re-verify HTML spec alignment.) {{< /hint >}} -As of August 20th, 2024: -* Chrome 127.0.6533.120 Stable + 129.0.6668.9 Canary -* Edge 127.0.2651.105 Stable -* Firefox 128.0 Stable - ## Defense - -* HTML specification fix [^html-spec-9025] -* Browser fixes: [^crbug-40867031] [^crbug-330744612] -* Application mitigation: Applications should avoid having sensitive information in URL if the page may include sandboxed `about:srcdoc` iframes with untrusted data. +To prevent leaking full URLs, applications should avoid using opaque iframes with about:srcdoc to render untrusted code. +To prevent leaking URL origins, applications should avoid using opaque iframes to render untrusted code. +Browsers are researching ways of mitigating the issues with sandboxed iframes [^html-spec-9025] [^crbug-40867031] [^crbug-330744612]. ____ From 7bea684049b3181217a478ae326d9c28b38bb784 Mon Sep 17 00:00:00 2001 From: Alesandro Ortiz Date: Fri, 23 Aug 2024 19:55:48 -0400 Subject: [PATCH 6/6] Add example hint --- content/docs/attacks/sandboxed-iframes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/docs/attacks/sandboxed-iframes.md b/content/docs/attacks/sandboxed-iframes.md index 7ef18a357..97fac1436 100644 --- a/content/docs/attacks/sandboxed-iframes.md +++ b/content/docs/attacks/sandboxed-iframes.md @@ -20,7 +20,9 @@ weight = 3 Currently, this behavior is preserved even for sandboxed iframes without `allow-same-origin` (i.e. opaque origin). This is problematic if the rendered content is untrusted, which is a common use case of opaque-origin `about:srcdoc` frames, and the parent's URL contains sensitive information. In cases where there are nested frames, and some frames do not have an `http(s)://` URL, then the closest parent with an `http(s)://` URL will be used. -For example, `A(example.com) -> B(about:srcdoc, opaque origin) -> C(about:srcdoc)`, page C will have `document.baseURI` set to page A's full URL despite page B being the initiator. +{{< hint example >}} +`A(example.com) -> B(about:srcdoc, opaque origin) -> C(about:srcdoc)`, page C will have `document.baseURI` set to page A's full URL despite page B being the initiator. +{{< /hint >}} (TODO(AO): Verify where Chromium checks for `http(s)://` (in origin or URL?). This is relevant in scenarios where non-opaque `about:blank` page creates `about:srcdoc` frames, or non-opaque `about:srcdoc` frame creates opaque `about:srcdoc` frames.) This also works in nested frames, with the baseURI value set to the closest document's URL that has an `http(s)://` origin. For example, nesting multiple `about:srcdoc` within `https://example.com/path?query#hash` will still leak the full `example.com` URL.