brave / brave-browser

Brave browser for Android, iOS, Linux, macOS, Windows.
https://brave.com
Mozilla Public License 2.0
17.54k stars 2.27k forks source link

Issues with content filtering in local frames on iOS #40649

Open pes10k opened 3 weeks ago

pes10k commented 3 weeks ago

Description

Brave iOS mishandles some aspects of content filtering in local frames (e.g., frames with the url about:blank, or otherwise inherit the security origin of the parent frame, w/o having that origin in the frame's URL)

  1. the iOS app does not apply cosmetic filtering rules in local frames
  2. for the subset of request blocking and request replacing that is done through JS injection in the iOS app (fetch, XMLHTTPRequest, etc), the context of the request is determined incorrectly, which will confuse cases where rules have first-party or third-party filters
  3. scriptlets are not executed in local frames in Brave iOS

The root reason is that checks for the frame's origin are done using window.location.href, which will be something like about:blank, even though the frame's code is running with access to cookies/etc for the parent frame.

This is also a concern for scriptlets, which also aren't executed in iOS in local frames. This is a problem bc the parent frame can access the global object for the child local frame to re-obtain access to unmodified JS structures and prototypes (and so circumvent the scriptlets)

Steps to reproduce

The below is a general example of the vulnerability. The different issues discussed above can all be exploited through slightly different means.

This filter list rule should block all first-party image requests on site.example: site.example$first-party,img

However, if you run the following JS in a page hosted from site.example, the image request will not be blocked

const frameElm = document.createElement('iframe')
frameElm.src = 'about:blank'
document.body.appendChild(frameElm)

const imgElm = document.createElement('img')
frameElm.contentDocument.body.appendChild(imgElm)
imgElm.src = '/image.png'

The casue

Actual result

the image request to //site.example/image.png will not be blocked.

Expected result

the image request to //site.example/image.png shoud be blocked.

Reproduces how often

Easily reproduced

Brave version

all versions

Device/iOS version

all versions

Affected browser versions

Reproducibility

Miscellaneous information

Feel free to contact @pes10k with any questions about this. These vulnerabilities were the result of an ongoing research project between Brave an the University of California, San Diego

pes10k commented 3 weeks ago

as an example of why this matters for a scriptlet, consider the following:

a filter list has the rule site.example##+js(nowebrtc), indicating that a scriptlet should be run on site.example to prevent site.example from accessing WebRTC (e.g., window.RTCPeerConnection).

However, site.example can then re-gain access to WebRTC APIs w/ a local frame (since scriptlets aren't run in local frames).

 // this will be blocked / NOOP bc of this scriptlet
 // https://github.com/gorhill/uBlock/blob/26b2ab8bb5fc572a64e75d58f8d4d6388d9909c5/assets/resources/scriptlets.js#L3040
const connection1 = new RTCPeerConnection()
// this will noop bc of the scriplet. No connection is created
connection1.createOffer() 

const frameElm = document.createElement('iframe')
frameElm.src = 'about:blank'
const originalRTCPeerConnection = frameElm.contentWindow.RTCPeerConnection
const connection2 = new originalRTCPeerConnection()
// this will NOT noop, the site has regained access to the WebRTC API
connection2.createOffer()
ShivanKaul commented 3 weeks ago

We will need to do this check for local schemes i.e. about, blob or data (not data: since it gets a new empty origin) (https://fetch.spec.whatwg.org/#local-scheme)

pes10k commented 1 week ago

@ShivanKaul you should still check data, and make sure that parent frames can't access window on the child frame (since they could circumvent scriptlets that way, even if they can't regain access to storage)